Docker for Developers · Part 10 — Kubernetes in Practice & Debugging
Đưa workload Kubernetes tới mức production-shaped với config, probes, resources, rollout và playbook debug sự cố bằng kubectl.
Đây là Phần 10 — bài chốt chặng nền tảng của lộ trình đưa bạn từ “mới nghe Docker” đến tự tin build, chạy và debug ứng dụng container trên Docker, Compose và Kubernetes. Mỗi phần kết thúc bằng bài tập; hãy làm, đừng chỉ đọc.
Ở Phần 9 bạn đã deploy app tối thiểu bằng Deployment và Service trên cluster local (kind hoặc minikube). Điều đó chứng minh control plane schedule được pod — nhưng manifest demo chưa phải cách chạy production. Phần 10 dùng một workload web nhất quán để thêm config bên ngoài, health probe, resource policy, rolling update — rồi dạy debug khi có sự cố.
Điều kiện:
kubectltrỏ đúng cluster; bạn đã quen debug theo Phần 8; và hiểu healthcheck Compose từ Phần 6.
Gọi đây là “production-shaped”, không phải “production-complete”: production thật còn cần Gateway/TLS, RBAC, NetworkPolicy, secret manager, backup, observability và delivery pipeline. Các phần 14–18 sẽ đi sâu từng lớp còn thiếu.
ConfigMap & Secret
Nhúng config cứng trong Deployment giống nhúng secret vào image — chạy được một lần rồi đau. Kubernetes tách config không nhạy cảm (ConfigMap) khỏi giá trị nhạy cảm (Secret) rồi inject vào pod.
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
APP_ENV: production
LOG_LEVEL: info
---
apiVersion: v1
kind: Secret
metadata:
name: web-secret
type: Opaque
stringData:
DATABASE_URL: postgres://postgres:secret@db:5432/app
stringData cho phép ghi text thường; Kubernetes lưu Secret dạng base64 trong etcd — mặc định không mã hóa trừ khi bạn bật encryption at rest. Coi Secret như file .env: không commit giá trị production thật lên git.
Senior rule: Secret Kubernetes là cơ chế phân phối secret cho Pod, không phải secret manager hoàn chỉnh. Với production, thường kết hợp cloud secret manager/Vault/External Secrets, RBAC chặt và encryption at rest.
Ghép ConfigMap + Secret phía trên với Deployment và Service dưới đây thành part10.yaml. Workload dùng nginx public để lab chạy được ngay, nhưng vẫn minh họa cả biến môi trường và file mount:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
containers:
- name: web
image: nginx:1.28-alpine
ports:
- { name: http, containerPort: 80 }
envFrom:
- configMapRef: { name: web-config }
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: web-secret
key: DATABASE_URL
volumeMounts:
- name: config-vol
mountPath: /etc/app/config
readOnly: true
volumes:
- name: config-vol
configMap:
name: web-config
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector: { app: web }
ports:
- { name: http, port: 80, targetPort: http }
kubectl apply -f part10.yaml
kubectl rollout status deployment/web
kubectl get configmap,secret
kubectl describe configmap web-config
Probe — liveness, readiness & startup
Healthcheck Compose (Phần 6) hỏi “container có healthy không?” Kubernetes tách thành ba loại probe với hậu quả khác nhau:
| Probe | Câu hỏi | Khi fail |
|---|---|---|
| liveness | Tiến trình còn sống? | Restart container |
| readiness | Pod có nhận traffic? | Gỡ khỏi endpoint Service (không traffic) |
| startup | Khởi động chậm xong chưa? | Tạm hoãn liveness/readiness; fail quá ngưỡng thì restart container |
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 5
failureThreshold: 3
Probe exec chạy lệnh trong container (giống Compose CMD-SHELL):
readinessProbe:
exec:
command: ["pg_isready", "-U", "postgres", "-q"]
periodSeconds: 5
Readiness chặn traffic — Service chỉ gửi request tới pod pass readiness:
Liveness cấu hình sai trên endpoint chậm gây vòng restart khi app vẫn đang khởi động. Quy tắc: liveness = “còn sống không?” rẻ; readiness = “phục vụ được chưa?”; startupProbe = ngân sách khởi động trước khi liveness/readiness bắt đầu có hiệu lực.
Request & limit tài nguyên
Không khai báo resource, scheduler thiếu dữ liệu để đặt pod và một workload có thể gây pressure cho cả node. Request là lượng scheduler dùng khi xếp Pod; khi tranh chấp, CPU request còn ảnh hưởng trọng số và memory request ảnh hưởng nguy cơ eviction. Limit là biên thực thi: CPU bị throttle, memory có thể bị OOM kill.
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
| Resource | Vượt limit |
|---|---|
| CPU | Bị throttle, không kill |
| Memory | OOMKilled — container bị kill, thường exit 137 (Phần 8) |
kubectl top pods # needs metrics-server on the cluster
kubectl describe pod web-xxx | grep -A5 "Last State"
# OOMKilled → raise memory limit or fix a leak
Rolling update & scale
Deployment rolling update giúp thay image dần; để tiến gần zero downtime vẫn cần readiness đúng, đủ capacity, graceful termination và phiên bản ứng dụng tương thích:
kubectl set image deployment/web web=nginx:1.29-alpine
# or: kubectl edit deployment web
kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web # rollback to previous ReplicaSet
Scale replica tách khỏi image:
kubectl scale deployment/web --replicas=5
kubectl get pods -l app=web
Chiến lược RollingUpdate mặc định: maxSurge (pod thêm khi update) và maxUnavailable (pod cũ có thể down) — ví dụ 25% giữ capacity khi deploy.
v1 pods: [A][B][C]
v2 pods: [D][E] ← maxSurge allows extra before old terminate
result: [D][E][F] ← all on new ReplicaSet
Sổ tay debug kubectl
Dùng cùng một quy trình — status → events → logs → exec:
1. kubectl get pods READ STATUS (Pending? CrashLoop? ImagePull?)
2. kubectl describe pod READ EVENTS (bottom of output = truth)
3. kubectl logs stdout/stderr; --previous if restarted
4. kubectl exec -it shell inside running container
5. kubectl get events cluster-wide timeline, newest first
kubectl get pods -o wide
kubectl get pods -w # watch until Ready / CrashLoop
kubectl describe pod web-7d4f8c9-xk2mq
# scroll to Events: — FailedScheduling, Pulling, BackOff, Unhealthy, OOMKilled
kubectl logs deployment/web
kubectl logs web-7d4f8c9-xk2mq -c web
kubectl logs web-7d4f8c9-xk2mq -c web --previous # last crashed instance
kubectl exec -it web-7d4f8c9-xk2mq -c web -- sh
kubectl get events --sort-by='.lastTimestamp' -A | tail -20
describe là docker inspect + events của Kubernetes — khi mơ hồ, bắt đầu từ đó.
Cẩm nang lỗi Kubernetes
┌─────────────────────┬──────────────────────────────┬────────────────────────────────────────┐
│ STATUS / SYMPTOM │ LIKELY CAUSE │ FIX │
├─────────────────────┼──────────────────────────────┼────────────────────────────────────────┤
│ ImagePullBackOff │ Bad name/tag; private │ Fix image ref; imagePullSecrets; on │
│ ErrImagePull │ registry; local image only │ kind: kind load docker-image myapp:tag │
│ │ on laptop, not in cluster │ │
├─────────────────────┼──────────────────────────────┼────────────────────────────────────────┤
│ CrashLoopBackOff │ App exits on start (config, │ logs --previous; describe Events; fix │
│ │ missing env, bad command) │ entrypoint / env / dependencies │
├─────────────────────┼──────────────────────────────┼────────────────────────────────────────┤
│ Pending │ No schedulable node; CPU/mem │ describe Events; kubectl top nodes; │
│ │ requests too high; PVC stuck │ fix requests or add capacity; PVC │
├─────────────────────┼──────────────────────────────┼────────────────────────────────────────┤
│ OOMKilled │ memory limit too low / leak │ Raise limit or fix leak; check exit 137│
├─────────────────────┼──────────────────────────────┼────────────────────────────────────────┤
│ Running 0/1 Ready │ Readiness probe failing │ logs + curl probe path inside pod; │
│ │ │ fix app or probe timing/path │
├─────────────────────┼──────────────────────────────┼────────────────────────────────────────┤
│ CreateContainer │ ConfigMap/Secret missing or │ kubectl get cm,secret; key name typo; │
│ ConfigError │ wrong key name │ apply manifest before Deployment │
└─────────────────────┴──────────────────────────────┴────────────────────────────────────────┘
ImagePullBackOff trên kind: image build local chưa có trong cluster cho đến khi bạn load:
docker build -t myapp:1.0.0 .
kind load docker-image myapp:1.0.0 --name kind
CrashLoopBackOff: container hiện tại có thể quá mới — --previous cho thấy lần crash gây restart.
Các bẫy thường gặp
- Bỏ qua Events trong
describe— cuốikubectl describe podlà nơi Kubernetes giải thích vì sao. - Quên
logs --previouskhi CrashLoopBackOff — container đang chạy có thể là lần thử mới, log trống. - Cho rằng Secret được mã hóa — base64 là encoding, không phải bảo mật; production dùng secret manager bên ngoài.
- Image local chưa load vào kind —
docker buildtrên host ≠ image trong cluster. - Liveness gọi
/readyhoặc DB — check chậm → restart vô hạn; dùng startupProbe cho khởi động chậm. - Readiness quá chặt khi deploy — mọi pod NotReady → không endpoint → “sập” khi rollout.
Bảng tra nhanh
# status & events
kubectl get pods -o wide
kubectl describe pod <name>
kubectl get events --sort-by='.lastTimestamp' -A | tail -30
# logs & shell
kubectl logs deploy/web
kubectl logs <pod> -c <container> --previous
kubectl exec -it <pod> -c <container> -- sh
# config & rollout
kubectl apply -f .
kubectl get cm,secret
kubectl set image deployment/web web=nginx:1.29-alpine
kubectl rollout status deployment/web
kubectl rollout undo deployment/web
kubectl scale deployment/web --replicas=3
# kind local images
kind load docker-image myapp:tag --name kind
Bài tập / Exercises
Dùng cluster kind (hoặc minikube) và file part10.yaml ở đầu bài làm nền. Cố tình làm hỏng — đó là cách debug in vào đầu.
1. Deploy pod image nginx:does-not-exist. Sửa ImagePullBackOff bằng tag đúng.
Lời giải
kubectl run broken --image=nginx:does-not-exist
kubectl get pods # ImagePullBackOff / ErrImagePull
kubectl describe pod broken | tail -20
kubectl delete pod broken
kubectl run broken --image=nginx:1.28-alpine2. Chạy container ghi lỗi rồi thoát. Chẩn đoán CrashLoopBackOff bằng describe và logs --previous.
Lời giải
kubectl run crasher --image=busybox:1.37 --restart=Always \
-- sh -c 'echo "boom: missing configuration" >&2; exit 1'
kubectl get pods
kubectl describe pod crasher | tail -15
kubectl logs crasher --previous # boom: missing configuration
kubectl delete pod crasher3. Đổi ConfigMap web-config thành LOG_LEVEL=debug. Chứng minh env của Pod cũ không tự đổi, sau đó restart rollout và kiểm tra Pod mới.
Lời giải
kubectl create configmap web-config \
--from-literal=APP_ENV=production \
--from-literal=LOG_LEVEL=debug \
--dry-run=client -o yaml | kubectl apply -f -
kubectl exec deploy/web -- printenv LOG_LEVEL # Pod cũ vẫn có giá trị lúc start
kubectl rollout restart deployment/web
kubectl rollout status deployment/web
kubectl exec deploy/web -- printenv LOG_LEVEL # debug4. Thêm readinessProbe (httpGet / port 80). Làm hỏng (sai port), xem pod Running nhưng không Ready, curl Service.
Lời giải
readinessProbe:
httpGet: { path: /, port: 9999 } # wrong — failskubectl apply -f part10.yaml
kubectl get pods # 0/1 Ready
kubectl get endpointslices -l kubernetes.io/service-name=web
kubectl run tmp --rm -i --restart=Never --image=busybox:1.37 \
-- wget -qO- -T 2 http://web:80/ || true
# fix port to `http` (or 80), re-apply — EndpointSlice becomes ready again5. Rollout nginx stable 1.28 → mainline 1.29 bằng kubectl set image, theo dõi rollout status, rồi undo.
Lời giải
kubectl set image deployment/web web=nginx:1.29-alpine
kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout status deployment/web6. Deploy một workload chủ đích cấp phát 128 MiB nhưng limit 32 MiB; xác nhận OOMKilled, rồi nâng limit và chứng minh Pod ổn định.
Lời giải
# memory-hog.yaml — workload chỉ dùng để học OOM, không phải app production
apiVersion: apps/v1
kind: Deployment
metadata:
name: memory-hog
spec:
replicas: 1
selector:
matchLabels: { app: memory-hog }
template:
metadata:
labels: { app: memory-hog }
spec:
containers:
- name: hog
image: python:3.13-alpine
command: ["python", "-c"]
args: ["x=bytearray(128*1024*1024); import time; time.sleep(3600)"]
resources:
requests: { memory: 16Mi }
limits: { memory: 32Mi }kubectl apply -f memory-hog.yaml
kubectl get pods -l app=memory-hog -w
kubectl describe pod -l app=memory-hog | grep -E 'OOM|Last State|Exit Code'
# change limits.memory to 256Mi, then:
kubectl apply -f memory-hog.yaml
kubectl rollout status deployment/memory-hog
kubectl delete deployment memory-hogCapstone chặng nền tảng: hoàn thiện part10.yaml với ba probe và resources cho web; sau đó inject hai sự cố — image sai và ConfigMap mất — rồi thu evidence, rollback/re-apply về healthy.
Lời giải
kubectl apply -f part10.yaml
kubectl rollout status deployment/web
kubectl get deploy,pod,svc,cm,secret
kubectl get endpointslices -l kubernetes.io/service-name=web
# Incident 1: bad image → evidence → rollback
kubectl set image deployment/web web=nginx:does-not-exist
kubectl rollout status deployment/web --timeout=30s || true
kubectl get pods
kubectl describe pod -l app=web | tail -30
kubectl rollout undo deployment/web
kubectl rollout status deployment/web
# Incident 2: dependency config disappears during a restart
kubectl delete configmap web-config
kubectl rollout restart deployment/web
kubectl get pods
kubectl describe pod -l app=web | tail -30
kubectl apply -f part10.yaml
kubectl rollout status deployment/web
# Final evidence
kubectl get pods -l app=web
kubectl get endpointslices -l kubernetes.io/service-name=web
kubectl get events --sort-by='.lastTimestamp' | tail -30Điểm chính
- ConfigMap tách config; Secret giữ giá trị nhạy cảm — lưu base64, không phải mã hóa thần.
- Readiness quyết định traffic Service; liveness restart — đừng nhầm (Phần 6).
- Request/limit ảnh hưởng schedule và OOM — exit 137 là memory (Phần 8).
- Rollout (
set image,rollout undo) vàscalelà thao tác vận hành hằng ngày. - Debug theo thứ tự:
get→describe(Events) →logs(--previous) →exec— cùng kỷ luật Docker, object mới.
Kết thúc chặng nền tảng
Bạn đã hoàn thành 10 phần nền tảng, một hành trình từ một container đến Deployment Kubernetes debug được. Dùng checklist này để ôn lại từng chủ đề:
- Part 1 — Containers, Images & the Mental Model
- Part 2 — Images & the Dockerfile
- Part 3 — Persisting Data: Volumes, Bind Mounts & Env
- Part 4 — Networking: Bridge, Ports & Service Discovery
- Part 5 — Docker Compose Fundamentals
- Part 6 — Compose in Depth: Env, Profiles, Healthchecks & Scaling
- Part 7 — Optimizing & Securing Images
- Part 8 — Debugging & Troubleshooting Docker
- Part 9 — Kubernetes Fundamentals
- Part 10 — Kubernetes in Practice & Debugging (you are here)
Giờ bạn có thể build image, điều phối stack bằng Compose, cứng hóa runtime, debug Docker/Kubernetes có hệ thống và vận hành workload với config, probe, resources, rollout. Đây là nền; nhánh chuyên sâu tiếp theo sẽ giải thích điều gì thật sự xảy ra bên dưới và cách vận hành các lớp production còn thiếu.
Tài liệu chính thức
- ConfigMaps
- Secrets và các lưu ý bảo mật
- Liveness, readiness và startup probes
- Resource management cho Pod và container
- Debug một Pod đang chạy
Tiếp theo
Phần 11 — Container Internals — theo docker run xuống OCI runtime, namespace, cgroup v2, copy-on-write và bài toán PID 1/signal.