# Lộ trình học CKAD

## Tuần 1 (Ngày 1–5): Application Design and Build

**Ngày 1 - Container image**

*   Học: OCI image spec, multi-stage build để giảm kích thước image, vì sao chạy non-root trong Dockerfile là best practice chứ không chỉ là yêu cầu thi.
    
*   Làm: viết Dockerfile multi-stage cho app Node.js, build image chạy bằng user non-root, push lên registry; so sánh kích thước image trước/sau tối ưu.
    

**Ngày 2 - kubectl + chọn đúng loại workload**

*   Học: khi nào dùng Deployment vs StatefulSet vs DaemonSet vs Job - đây là năng lực domain "Design and Build" thật sự đo, không phải chỉ biết cú pháp.
    
*   Làm: tạo Pod/Deployment/Service **chỉ bằng lệnh imperative** (`kubectl run`, `kubectl create deployment`, `kubectl expose`) kết hợp `--dry-run=client -o yaml` để sinh YAML nhanh - đây là kỹ năng sống còn vì thi tính giờ 6-8 phút/task.
    

**Ngày 3 - Multi-container Pod**

*   Học: 4 pattern hay nhầm lẫn - **sidecar** (container phụ chạy song song, chia sẻ vòng đời, VD: log shipper), **init container** (chạy xong mới tới container chính, VD: chờ DB sẵn sàng), **ambassador** (proxy hoá kết nối ra ngoài), **adapter** (chuẩn hoá output/log của container chính).
    
*   Làm: 1 Pod dùng sidecar để ghi log ra file riêng; 1 Pod dùng init container chờ database sẵn sàng mới cho container chính start; 1 Pod dùng ambassador pattern để proxy kết nối tới service khác nhau theo môi trường.
    
*   Tại sao gộp lại: bản gốc tách 4 pattern này ra 3 ngày khác nhau nhưng không phân biệt rõ - thực ra chúng dễ nhầm nhất khi học rời rạc, phải học cùng lúc để thấy khác nhau ở đâu.
    

**Ngày 4 - Volume: ephemeral vs persistent**

*   Học: `emptyDir` (dữ liệu tạm, mất khi Pod bị xoá) khác `PersistentVolume`/`PersistentVolumeClaim` (dữ liệu tồn tại độc lập vòng đời Pod) ở điểm nào.
    
*   Làm: Pod dùng `emptyDir` chia sẻ dữ liệu giữa 2 container; Pod dùng `hostPath` đọc file trên node; PV (hostPath) + PVC + Pod mount PVC, restart Pod xác nhận dữ liệu còn.
    

**Ngày 5 - Job, CronJob & CRD**

*   Học: Job đảm bảo chạy xong mới coi là hoàn thành (khác Pod thường); CRD là gì, tự viết 1 CRD.
    
*   Làm: Job chạy 1 tác vụ đảm bảo hoàn thành; CronJob backup mỗi giờ; cài 1 Operator có sẵn (VD: Prometheus Operator) và tạo 1 CR để dùng CRD đó.
    

## Tuần 2 (Ngày 6–10): Application Deployment

**Ngày 6 - Rolling update: hiểu cơ chế, không chỉ chạy lệnh**

*   Học: `maxSurge`/`maxUnavailable` điều khiển tốc độ và độ an toàn của rolling update thế nào.
    
*   Làm: Deployment 3 replica, đổi image, quan sát `kubectl rollout status`; chỉnh `maxSurge=0` để bắt buộc rolling update tuần tự thay vì song song, quan sát khác biệt.
    

**Ngày 7 - Rollback & lịch sử triển khai**

*   Làm: cố tình deploy 1 image lỗi (sai tên), quan sát Pod CrashLoopBackOff, `kubectl rollout undo` về version trước; dùng `kubectl rollout history` xem lại các revision.
    

**Ngày 8 - Blue/Green và Canary**

*   Học: Kubernetes không có tính năng canary/blue-green sẵn - cả hai đều dựng từ Service selector (blue/green) hoặc tỷ lệ replica/label (canary), khác nhau ở tốc độ chuyển traffic.
    
*   Làm: Blue/Green - deploy version mới song song version cũ (label khác nhau), đổi `selector` của Service để chuyển hết traffic ngay lập tức. Canary - deploy version mới với ít replica hơn cùng label chọn bởi Service, tăng dần tỷ lệ traffic bằng cách tăng dần số replica.
    

**Ngày 9 - Helm**

*   Làm: cài 1 ứng dụng bằng Helm chart có sẵn; `helm upgrade`/`helm rollback`; tự viết 1 Helm chart đóng gói app của Ngày 1; parameterize chart để chạy khác nhau ở dev/staging/prod bằng file `values-<env>.yaml` riêng.
    
*   Tại sao gộp: bản gốc rải Helm ra 4-5 ngày khác nhau (cài, dùng, upgrade, tạo chart, quản lý môi trường) - đây là một kỹ năng liên tục, học ngắt quãng làm mất mạch.
    

**Ngày 10 - Scaling & PodDisruptionBudget**

*   Làm: HPA scale theo CPU > 80%; scale tay 3→5→2 quan sát ReplicaSet; PodDisruptionBudget đảm bảo tối thiểu 50% Pod khả dụng khi node bảo trì.
    

## Tuần 3 (Ngày 11–15): Application Environment, Configuration and Security

**Ngày 11 - ConfigMap**

*   Làm: tạo ConfigMap từ literal, từ file, từ thư mục; inject vào Pod bằng biến môi trường đơn lẻ (`env.valueFrom`), bằng `envFrom` (toàn bộ), và bằng volume mount (file); test đổi ConfigMap sau khi Pod chạy - xác nhận volume mount tự cập nhật nhưng biến môi trường thì không (phải hiểu sự khác biệt này, hay bị hỏi trong đề thi).
    

**Ngày 12 - Secret**

*   Làm: tạo Secret loại `generic`, `tls`, `docker-registry`; inject theo 2 cách như ConfigMap; giải thích được base64 chỉ là encoding, không phải encryption - Secret an toàn hơn ConfigMap chỉ vì Kubernetes xử lý nó khác (không log ra, không hiện trong `kubectl describe` mặc định), không phải vì nó được mã hoá sẵn.
    

**Ngày 13 - SecurityContext & Pod Security Standards**

*   Làm: cấu hình Pod chạy non-root (`runAsNonRoot`, `runAsUser`), `readOnlyRootFilesystem: true`, drop toàn bộ Linux capabilities rồi chỉ thêm lại cái cần thiết; gắn label `pod-security.kubernetes.io/enforce` cho namespace ở mức `restricted`, thử deploy Pod chạy root và xác nhận bị chặn.
    

**Ngày 14 - ServiceAccount & RBAC**

*   Học: RBAC đủ cho 1 app truy cập đúng resource nó cần - least privilege ở tầng ứng dụng.
    
*   Làm: tạo ServiceAccount riêng cho app (không dùng `default`), Role + RoleBinding chỉ cho phép app đọc ConfigMap trong namespace của nó, gắn ServiceAccount vào Deployment, test app gọi Kubernetes API bằng token của ServiceAccount đó.
    

**Ngày 15 - ResourceQuota, LimitRange, resource requests/limits**

*   Làm: đặt request/limit cho 1 container, cố tình vượt limit memory quan sát bị OOMKilled và vượt limit CPU quan sát bị throttle (2 hành vi khác nhau); ResourceQuota giới hạn namespace; LimitRange đặt mặc định khi Pod không khai request/limit.
    

## Tuần 4 (Ngày 16–19): Services and Networking

**Ngày 16 - Service**

*   Làm: cùng 1 app, expose lần lượt bằng `ClusterIP`, `NodePort`, `LoadBalancer`, quan sát khác nhau ở khả năng truy cập từ đâu; kiểm tra `kubectl get endpoints` để thấy Service chỉ là một tầng trỏ tới Pod theo label selector.
    

**Ngày 17 - Ingress & Gateway API**

*   Học: Gateway API đã được đưa vào đề thi CKA (đầu 2026) và CKAD (giữa 2026, khoảng ~5% điểm), là chuẩn kế nhiệm Ingress (tách rõ vai trò `GatewayClass`/`Gateway`/`HTTPRoute` cho từng persona, hết phụ thuộc annotation riêng của từng controller). **Nhưng Ingress chưa bị gỡ khỏi đề thi** - Linux Foundation liệt kê cả hai trong syllabus chính thức, và ghi nhận thực tế nhiều đề thi 2026 vẫn ra Ingress nhiều hơn Gateway API. Production hiện tại phần lớn vẫn chạy Ingress, nên cả hai đều cần biết.
    
*   Làm: cài Ingress Controller (nginx), tạo Ingress nhiều rule expose nhiều app theo path/host, cấu hình TLS bằng Secret loại `tls`. Sau đó cài 1 Gateway controller (VD: Envoy Gateway hoặc NGINX Gateway Fabric), tạo `GatewayClass` → `Gateway` (định nghĩa listener) → `HTTPRoute` (`parentRefs` trỏ đúng vào Gateway - lỗi hay gặp nhất là quên field này, route bị "mồ côi" mà `kubectl` không báo lỗi gì) trỏ tới cùng Service đã dùng ở bài Ingress, so sánh 2 cách viết cho cùng 1 kết quả.
    
*   Production check: thực hành đúng quy trình migrate thật - đọc `kubectl describe ingress` để lấy host/path/service/port, dựng Gateway + HTTPRoute tương đương, verify traffic qua Gateway hoạt động đúng, rồi mới xoá Ingress cũ (không xoá trước khi verify xong).
    

**Ngày 18 - NetworkPolicy**

*   Học: NetworkPolicy mặc định không chặn gì cả - phải tự viết default-deny rồi mở dần theo nhu cầu thật.
    
*   Làm liên tục trong 1 buổi: default-deny ingress cho 1 namespace → mở lại chỉ port 80/443 → thu hẹp theo `podSelector` (label) → mở thêm cho phép từ 1 namespace cụ thể qua `namespaceSelector` → thêm rule egress chặn Pod gọi ra ngoài trừ DNS.
    
*   Tại sao gộp: bản gốc có tới 5 bài NetworkPolicy rời rạc ở các ngày khác nhau - thực ra đây là một chuỗi rule cộng dồn, tách rời làm mất tính logic build-up.
    

**Ngày 19 - DNS & service discovery trong cluster**

*   Làm: gọi Service từ Pod khác bằng short name và bằng FQDN đầy đủ (`service.namespace.svc.cluster.local`); gọi cross-namespace, xác nhận phải dùng FQDN; debug 1 tình huống Pod gọi sai tên Service dẫn tới DNS not found.
    

## Tuần 5 (Ngày 20–22): Application Observability and Maintenance

**Ngày 20 - 3 loại Probe**

*   Học: `livenessProbe` (container còn sống không, fail thì restart), `readinessProbe` (đã sẵn sàng nhận traffic chưa, fail thì rút khỏi Endpoints chứ không restart), `startupProbe` (dành cho app khởi động chậm, che liveness probe trong lúc chờ).
    
*   Làm: cấu hình cả 3 probe trên 1 Pod có app khởi động chậm; cố tình để readinessProbe fail và quan sát Pod vẫn Running nhưng bị rút khỏi Service Endpoints (khác hẳn liveness fail).
    
*   Tại sao gộp: bản gốc học riêng 3 probe ở 3 ngày khác nhau - dễ dẫn tới hiểu nhầm liveness và readiness làm cùng một việc.
    

**Ngày 21 - Debug ứng dụng thực chiến**

*   Làm 5 tình huống tự tạo lỗi rồi tự chẩn đoán bằng `kubectl logs`, `kubectl describe`, `kubectl exec`, ephemeral debug container (`kubectl debug`): CrashLoopBackOff do lỗi code; ImagePullBackOff do sai tag; Pod Pending do thiếu resource; Pod Running nhưng app không nhận request do readinessProbe sai; container chính lỗi nhưng sidecar vẫn chạy khiến Pod trông như "Running" dù app chết.
    

**Ngày 22 - Giám sát cơ bản ở góc nhìn developer**

*   Học: khác CKA (tự vận hành Prometheus Operator), CKAD chỉ cần biết app cần expose metrics đúng chuẩn để hệ thống giám sát chung của công ty scrape được.
    
*   Làm: cài `metrics-server`, dùng `kubectl top pod/node`; thêm endpoint `/metrics` dạng Prometheus format vào app mẫu, xác nhận Prometheus (nếu có) scrape được.
    

## Tuần 6 (Ngày 23–26): API, CRD nâng cao & Mock exam

**Ngày 23 - Kubernetes API trực tiếp**

*   Làm: `kubectl explain <resource>` để tra cứu field nhanh hơn đọc docs; gọi thẳng API bằng `curl` qua `kubectl proxy` để lấy danh sách Pod; dùng thư viện client (Python hoặc Go) lấy danh sách Node - CKAD có thể hỏi các thao tác cơ bản này.
    

**Ngày 24 - Troubleshooting drill có giới hạn thời gian**

*   Làm 8-10 tình huống tổng hợp toàn bộ 5 domain, tự giới hạn 8 phút/task đúng nhịp độ thi thật, không xem gợi ý trước khi hết giờ.
    

**Ngày 25 - Mock exam CKAD đầy đủ (2 giờ, có dùng docs như thi thật)**

*   Làm 1 đề mock đầy đủ 15-20 task, chấm điểm theo ngưỡng pass 66%.
    

**Ngày 26 - Review**

*   Xem lại câu sai, luyện lại riêng phần yếu; luyện alias `k=kubectl`, `--dry-run=client -o yaml`, thao tác vim nhanh trong YAML - vì CKAD tính giờ theo task (trung bình 6-8 phút/task), tốc độ gõ quyết định điểm số ngang với kiến thức.
    

## Tuần 7+ (Ngày 27 trở đi): Mở rộng

**Ngày 27 - CI/CD gắn với app repo**

*   Làm: viết pipeline (GitHub Actions/GitLab CI) tự build image + push registry + deploy lên cluster mỗi khi có commit mới vào nhánh chính.
    

**Ngày 28 - GitOps từ góc nhìn dev**

*   Học: vì sao dev không nên `kubectl apply` trực tiếp vào production - mọi thay đổi nên đi qua Git rồi để ArgoCD/Flux tự sync.
    
*   Làm: chuyển app Ngày 27 sang deploy qua ArgoCD thay vì pipeline tự `kubectl apply`.
    

**Ngày 29 - Helm chart chuẩn production**

*   Làm: thêm schema validation cho `values.yaml` (`values.schema.json`), viết Helm test (`helm test`), đặt semantic versioning cho chart, đảm bảo `helm upgrade` không phá vỡ app đang chạy nếu values sai định dạng.
    

**Ngày 30 - Backup & Restore dữ liệu ứng dụng**

*   Học: một developer không cần vận hành etcd (đó là việc của CKA/cluster-admin), nhưng **phải biết backup đúng dữ liệu app mình quản lý** trước khi làm việc rủi ro (migration schema, `helm upgrade` lớn, đổi StorageClass...). Công cụ phổ biến nhất cho việc này ở tầng ứng dụng là **Velero** - backup cả manifest (Deployment, ConfigMap, Secret...) lẫn dữ liệu thật trong PVC theo namespace hoặc theo label, không cần đụng tới etcd.
    
*   Làm: cài Velero (hoặc dùng Velero có sẵn nếu team platform đã cài), backup namespace chứa app + PVC đang có dữ liệu thật của Ngày 4; cố tình xoá hỏng 1 phần dữ liệu; restore từ backup và xác nhận khôi phục đúng. Thử backup **trước khi** chạy `helm upgrade` ở Ngày 29 để có đường lùi nếu upgrade lỗi.
    
*   Tại sao: rất nhiều developer chỉ quen "nếu lỗi thì `kubectl rollout undo`" - nhưng rollback Deployment không khôi phục lại dữ liệu đã bị migration script phá hỏng trong PVC. Đây là khoảng trống phổ biến nhất giữa "biết deploy" và "deploy an toàn".
    

**Ngày 31 - Bài tập tổng hợp: debug một app "production" bị lỗi nhiều lớp**

*   Làm: tự dựng 1 app cố tình có 3 lỗi chồng lên nhau (VD: readinessProbe sai + Secret thiếu key + resource limit quá thấp gây OOMKilled), rồi tự debug từ triệu chứng ban đầu (user báo "không truy cập được app") đến khi tìm ra và sửa hết cả 3 lỗi - đúng dạng sự cố thật, không phải lỗi đơn lẻ như luyện tập thông thường.
    

# Bài tập thêm

## Nhóm 1 - Application Design and Build

1.  Dockerfile multi-stage cho app thật, build image, push lên registry.
    
2.  Sửa 1 image có sẵn (thêm dependency), build lại, push version mới - thực hành tag versioning, không dùng `:latest`.
    
3.  Pod với init container: chờ database sẵn sàng (kiểm tra bằng lệnh thật, không chỉ `sleep`) trước khi container chính start.
    
4.  Pod với sidecar: container phụ ghi log ra file riêng, chia sẻ qua volume với container chính.
    
5.  Pod với ambassador: container proxy hoá kết nối ra service khác nhau tuỳ môi trường (dev trỏ mock, prod trỏ service thật).
    
6.  **Production check cho bài 3–5:** xoá container chính, xác nhận sidecar/ambassador cư xử đúng - container phụ có tự chết theo không hay treo lại (ảnh hưởng tới Job hoàn thành đúng cách)?
    
7.  Volume `emptyDir` chia sẻ dữ liệu tạm giữa 2 container.
    
8.  Volume `hostPath` đọc file trên node - giải thích vì sao **không nên dùng trong production multi-node** (dữ liệu gắn chặt vào 1 node cụ thể).
    
9.  PV (không dùng hostPath - dùng CSI driver thật nếu có) + PVC + Pod ghi dữ liệu, restart Pod xác nhận dữ liệu còn.
    
10.  ServiceAccount gắn vào Pod (không dùng `default`) - chuẩn bị cho bài RBAC ở Nhóm 3.
     
11.  Đặt resource requests/limits cho container - chuẩn bị cho bài QoS ở Nhóm 3.
     
12.  Dùng 1 CRD có sẵn (VD: từ Prometheus Operator) - tạo 1 Custom Resource, xác nhận controller tương ứng phản ứng đúng.
     
13.  Job đảm bảo hoàn thành 1 tác vụ - thử set `backoffLimit` thấp, cố tình cho tác vụ fail, quan sát Job dừng retry đúng lúc.
     
14.  CronJob chạy mỗi giờ - cấu hình `concurrencyPolicy: Forbid` để tránh 2 lần chạy chồng nhau nếu lần trước chưa xong.
     

## Nhóm 2 - Application Deployment

1.  Deployment 3 replica, rolling update đổi image, theo dõi `kubectl rollout status` tới khi hoàn tất.
    
2.  Cố tình deploy image sai tag, quan sát rollout bị kẹt, `kubectl rollout undo` về bản trước.
    
3.  Scale 3→5→2, quan sát ReplicaSet cũ/mới trong quá trình.
    
4.  Blue/Green: 2 Deployment (label version khác nhau) chạy song song, đổi `selector` của Service để chuyển hết traffic ngay lập tức - đo thời gian gián đoạn thực tế (có hay không).
    
5.  Canary: Deployment mới cùng label được Service chọn nhưng ít replica hơn hẳn, tăng dần tỷ lệ traffic bằng tăng dần replica - so với bài 4 để thấy khác nhau ở tốc độ chuyển đổi.
    
6.  **Production check:** viết tiêu chí rollback tự động cho canary (VD: error rate tăng thì lập tức đưa replica canary về 0) - không có sẵn trong K8s, tự nghĩ cách kiểm tra bằng tay hoặc mô tả cách 1 công cụ như Argo Rollouts sẽ làm việc này tự động.
    
7.  Cài 1 Helm chart có sẵn, `helm upgrade` đổi version, `helm rollback` nếu lỗi.
    
8.  Tự viết 1 Helm chart đóng gói app riêng, tham số hoá bằng `values.yaml`.
    
9.  Cùng chart Ngày 8, chạy khác nhau ở 3 môi trường bằng `values-dev.yaml`/`values-staging.yaml`/`values-prod.yaml`.
    
10.  PodDisruptionBudget đảm bảo ≥50% replica khả dụng - drain thử 1 node có Pod của Deployment, xác nhận PDB được tôn trọng.
     
11.  HPA scale theo CPU > 80% - tạo tải giả (`stress` hoặc vòng lặp CPU) để tự kích hoạt scale thật, không chỉ đọc YAML.
     

## Nhóm 3 - Application Environment, Configuration and Security

1.  ConfigMap từ literal, từ file, từ thư mục.
    
2.  Inject ConfigMap bằng biến môi trường đơn lẻ, bằng `envFrom` toàn bộ, bằng volume mount - đổi ConfigMap sau khi Pod chạy, xác nhận cách nào tự cập nhật, cách nào không.
    
3.  Secret loại `generic`, `tls`, `docker-registry` - dùng `docker-registry` secret thật để pull image từ private registry.
    
4.  Inject Secret bằng biến môi trường và bằng volume mount, so sánh rủi ro lộ thông tin của từng cách (biến môi trường dễ lộ qua `kubectl describe`/crash dump hơn volume).
    
5.  SecurityContext: `runAsNonRoot`, `runAsUser` cụ thể, `readOnlyRootFilesystem: true`.
    
6.  Drop toàn bộ Linux capabilities (`drop: ["ALL"]`) rồi chỉ thêm lại đúng cái container cần (VD: `NET_BIND_SERVICE` nếu app bind port <1024).
    
7.  Gắn `pod-security.kubernetes.io/enforce: restricted` cho 1 namespace, thử deploy Pod chạy root - xác nhận bị chặn ngay khi tạo (không phải chặn sau khi chạy).
    
8.  ServiceAccount riêng cho app + Role + RoleBinding chỉ cho đọc ConfigMap trong namespace của nó - thử dùng token của ServiceAccount này gọi API để đọc Secret, xác nhận bị từ chối (least privilege đúng nghĩa).
    
9.  ResourceQuota giới hạn tổng CPU/memory 1 namespace - thử deploy Pod vượt quota, xác nhận bị từ chối ngay khi tạo.
    
10.  LimitRange đặt request/limit mặc định cho namespace không khai báo - tạo Pod không set gì, xác nhận LimitRange tự áp vào.
     
11.  Cố tình đặt limit memory thấp hơn nhu cầu thật của app - quan sát `OOMKilled`; cố tình đặt limit CPU thấp - quan sát bị throttle (app chạy chậm nhưng không chết) - phân biệt rõ 2 hành vi.
     

## Nhóm 4 - Services and Networking

1.  Cùng 1 app, expose lần lượt bằng `ClusterIP`, `NodePort`, `LoadBalancer` - xác nhận truy cập được từ đâu ứng với từng loại.
    
2.  `kubectl get endpoints` xác nhận Service chỉ là một tầng trỏ theo label selector - đổi label Pod, xác nhận Endpoints tự động rớt Pod đó.
    
3.  Ingress với nhiều rule expose nhiều app theo path/host khác nhau.
    
4.  TLS cho Ingress bằng Secret loại `tls` - xác nhận truy cập HTTPS thật, không chỉ khai báo YAML. 4b. **Gateway API** (đã vào đề CKAD ~5% điểm, nhưng Ingress vẫn còn trong đề song song - học cả hai): cài 1 Gateway controller, tạo `GatewayClass` → `Gateway` → `HTTPRoute` trỏ `parentRefs` đúng vào Gateway (lỗi hay gặp nhất: quên field này khiến route "mồ côi" mà không có lỗi hiển thị). Làm lại đúng bài 3-4 bằng Gateway API, so sánh cách viết. 4c. **Production check:** thực hành quy trình migrate 1 Ingress có sẵn sang Gateway API - đọc `kubectl describe ingress` lấy thông tin, dựng Gateway/HTTPRoute tương đương, verify traffic hoạt động đúng trước khi xoá Ingress cũ.
    
5.  Chuỗi NetworkPolicy cộng dồn cho 1 namespace (làm liên tục theo đúng thứ tự để thấy tính build-up):
    
    *   a) default-deny toàn bộ ingress
        
    *   b) mở lại chỉ port 80/443
        
    *   c) thu hẹp theo `podSelector` (chỉ Pod có label cụ thể mới nhận được)
        
    *   d) mở thêm cho phép từ 1 namespace khác qua `namespaceSelector`
        
    *   e) thêm rule egress chặn Pod gọi ra ngoài, chỉ cho phép DNS (port 53) và HTTPS ra ngoài
        
6.  Gọi Service từ Pod khác bằng short name và FQDN đầy đủ - gọi cross-namespace, xác nhận bắt buộc dùng FQDN.
    
7.  **Production check:** cố tình đặt sai `podSelector` trong NetworkPolicy (thiếu 1 label) - quan sát app "tự nhiên" mất kết nối dù NetworkPolicy tưởng như đúng, luyện kỹ năng debug NetworkPolicy sai.
    

## Nhóm 5 - Application Observability and Maintenance

1.  Cấu hình đủ 3 probe (`livenessProbe`, `readinessProbe`, `startupProbe`) trên 1 Pod có app khởi động chậm thật (không phải giả lập bằng `sleep`).
    
2.  Cố tình để `readinessProbe` fail - xác nhận Pod vẫn `Running` nhưng bị rút khỏi Service Endpoints (khác `livenessProbe` fail sẽ làm container restart).
    
3.  Cố tình để `livenessProbe` fail - xác nhận container bị restart liên tục, `kubectl describe` có sự kiện `Killing`/`Unhealthy`.
    
4.  `kubectl logs` xem log 1 container cụ thể trong Pod nhiều container.
    
5.  `kubectl exec` vào Pod kiểm tra trạng thái thực tế bên trong (biến môi trường, file cấu hình đã đúng chưa).
    
6.  `kubectl debug` tạo ephemeral container để debug 1 Pod đang chạy mà không cần sửa Pod gốc - dùng khi image gốc không có shell.
    
7.  Cài `metrics-server`, dùng `kubectl top pod`/`kubectl top node` xác định Pod nào ngốn tài nguyên nhất.
    
8.  Thêm endpoint `/metrics` chuẩn Prometheus vào app mẫu - xác nhận định dạng đúng để hệ thống giám sát chung scrape được.
    
9.  **Bài triệu chứng (không nói nguyên nhân, tự chẩn đoán):**
    
    *   Pod ở trạng thái `CrashLoopBackOff` ngay sau khi tạo.
        
    *   `kubectl get pods` báo `ImagePullBackOff`.
        
    *   Pod `Pending` mãi không được lập lịch.
        
    *   Người dùng báo "không gọi được app" dù `kubectl get pods` cho thấy Pod đang `Running`.
        
    *   Pod trông như hoạt động bình thường (1/1 container Ready) nhưng thực tế container chính đã crash - chỉ có sidecar còn sống.
        

## Nhóm 6 - Mở rộng

1.  Viết pipeline CI/CD tự build image + push registry + deploy khi có commit mới vào nhánh chính.
    
2.  Chuyển 1 app đang deploy bằng `kubectl apply` tay sang deploy qua ArgoCD/Flux (GitOps) - so sánh độ an toàn và tốc độ rollback giữa 2 cách.
    
3.  Thêm `values.schema.json` cho Helm chart đã viết ở Nhóm 2, xác nhận `helm install` báo lỗi ngay nếu ai đó truyền sai kiểu dữ liệu vào `values.yaml`.
    
4.  Viết `helm test` cho chart, xác nhận test tự chạy được sau khi deploy.
    
5.  **Velero (backup/restore ở góc nhìn dev):** cài Velero, backup namespace app + PVC đang có dữ liệu thật; cố tình xoá hỏng dữ liệu; restore từ backup, xác nhận cả manifest lẫn dữ liệu PVC khôi phục đúng.
    
6.  **Velero:** backup namespace **trước khi** chạy `helm upgrade` mang tính rủi ro (đổi version schema database chẳng hạn) - nếu upgrade lỗi, restore lại thay vì chỉ `helm rollback` (rollback không khôi phục dữ liệu đã bị migration script phá hỏng trong PVC, chỉ đưa manifest về bản cũ).
    
7.  **Bài tổng hợp (capstone):** tự dựng 1 app có 3 lỗi chồng lên nhau cùng lúc (VD: `readinessProbe` trỏ sai path + Secret thiếu 1 key khiến app crash khi đọc config + resource limit memory quá thấp gây `OOMKilled` không liên tục) - nhờ người khác (hoặc tự quên chi tiết sau vài ngày) báo triệu chứng đơn giản kiểu "user không vào được app", rồi tự debug từ đầu tới khi tìm hết cả 3 lỗi.
