Người ta viết Deployment và nghĩ nó quản pod. Nó không. Bài này đo tầng ở giữa và cho thấy tầng đó giải thích cả việc quay lui nhanh lẫn việc phản ứng chậm.
Ba tầng
deployment.apps/app 3/4
replicaset.apps/app-856595fd8d 4 4 3
pod/app-856595fd8d-6c5f4 Running
pod/app-856595fd8d-k2lmw Running
...
Deployment không quản pod. Nó quản ReplicaSet, và ReplicaSet quản pod.
Chú ý tên pod: app-856595fd8d-6c5f4. Phần giữa là hàm băm của template pod — mọi pod sinh ra từ cùng một cấu hình đều mang cùng phần băm đó.
Mỗi phiên bản một ReplicaSet
Đổi ảnh rồi xem lại:
rs app-7fbbdcbddc mong muốn=4 hiện có=4 sẵn sàng=4
rs app-856595fd8d mong muốn=0 hiện có=0 sẵn sàng=0
ReplicaSet cũ không bị xoá. Nó chỉ bị đặt mong muốn=0.
Đây là toàn bộ cơ chế của Deployment: nó tạo một ReplicaSet mới cho template mới, rồi dịch chuyển dần số bản mong muốn từ cái cũ sang cái mới. Phần sau đo tốc độ dịch chuyển đó.
Quay lui mất 2,3 giây
kubectl rollout undo deployment/app
2,3 giây
rs app-7fbbdcbddc mong muốn=0
rs app-856595fd8d mong muốn=4 <- ReplicaSet cũ sống lại
ảnh hiện tại: nginx:1.26-alpine
Nhanh vì nó không làm gì mới: chỉ đổi số "mong muốn" giữa hai ReplicaSet đã có sẵn. Ảnh cũ vẫn nằm trên node (phần 48 đo kéo ảnh mất 6–16 giây), template cũ vẫn nằm trong etcd.
revisionHistoryLimit mặc định 10 — giữ mười ReplicaSet cũ, tức mười phiên bản lùi được. Đặt về 0 thì tiết kiệm một chút etcd nhưng mất hẳn khả năng quay lui, và đó là đánh đổi rất tệ.
kubectl rollout history deployment/app
kubectl rollout undo deployment/app --to-revision=3
CHANGE-CAUSE hiện <none> vì tôi không khai. Thêm annotation kubernetes.io/change-cause khi triển khai thì lịch sử đọc được:
kubectl annotate deployment/app kubernetes.io/change-cause="nang nginx len 1.27"
Node chết: bốn phút mới phản ứng
Bốn pod, hai trong số đó nằm trên node bị docker stop:
| Thời điểm | Running | Trên node chết | Tổng pod |
|---|---|---|---|
| 0 s | 4 | 2 | 4 |
| 101 s | 4 | 2 | 4 |
| 202 s | 4 | 2 | 4 |
| 222 s | 4 | 2 | 4 |
| 243 s | 4 | 2 | 6 |
| 505 s | 4 | 2 | 6 |
Bốn phút không làm gì, rồi tạo hai bản thay thế. Và pod ma trên node chết vẫn nằm đó tới hết phép đo — hơn tám phút.
Lý do đã đo ở phần 42: pod ma vẫn báo Ready. kubelet trên node chết không cập nhật được trạng thái, nên chúng đứng nguyên. ReplicaSet đếm đủ 4 và kết luận mọi thứ đúng.
Chỉ khi cơ chế đuổi theo vết gỡ pod ma ra thì ReplicaSet mới thấy thiếu và tạo bản thay thế.
Đây là phép đo độc lập thứ hai cho cùng hiện tượng, lần này với Deployment thay vì pod trần, và nó cho thêm một mốc: bản thay thế xuất hiện ở khoảng 240 giây.
Hệ quả: kubectl get deployment nói dối
Trong suốt bốn phút đó:
deployment.apps/app 4/4 READY
Bốn trên bốn. Xanh hoàn toàn. Trong khi một nửa số bản đã không phục vụ được.
Thứ nói thật là Endpoints — nó chỉ chứa pod qua được kiểm tra sẵn sàng:
kubectl get endpoints app-svc -o jsonpath='{.subsets[*].addresses[*].ip}' | wc -w
Nếu bảng theo dõi của bạn đọc deployment.status.readyReplicas, nó sẽ bỏ lỡ đúng loại sự cố này.
Ba trường status và ý nghĩa khác nhau
kubectl get deployment app -o jsonpath='{.status}' | python3 -m json.tool
replicas— số pod ReplicaSet đang quản.readyReplicas— số pod báoReady. Đây là con số nói dối ở trên.availableReplicas— số pod đãReadyliên tục trongminReadySeconds.
minReadySeconds mặc định 0, nên hai con số cuối thường bằng nhau. Đặt nó lên 10–30 giây là cách rẻ để tránh triển khai một bản hỏng nhanh: pod khởi động rồi sập trong 5 giây sẽ không bao giờ được tính là available, và việc triển khai cuốn chiếu dừng lại.
Khi nào không dùng Deployment
Deployment giả định các bản thay thế được cho nhau: không danh tính riêng, không lưu trữ riêng, thứ tự khởi động không quan trọng.
Không đúng với ba trường hợp:
- Cơ sở dữ liệu, hàng đợi, bất cứ thứ gì có trạng thái →
StatefulSet. Nó cho tên ổn định (db-0,db-1), thứ tự khởi động, và volume riêng cho từng bản. - Một bản trên mỗi node — thu log, đo đạc, plugin mạng →
DaemonSet. - Chạy một lần rồi thôi →
JobhoặcCronJob.
Dùng Deployment cho cơ sở dữ liệu là sai lầm hay gặp, và nó chỉ lộ ra khi hai bản cùng ghi vào một volume.
Thử ba mươi giây
Xem lịch sử phiên bản và khả năng lùi của bạn:
kubectl get rs -l app=<ten> \
-o custom-columns=TEN:.metadata.name,MONG_MUON:.spec.replicas,ANH:.spec.template.spec.containers[0].image
Số dòng có MONG_MUON=0 là số phiên bản bạn lùi về được ngay lập tức, không cần kéo ảnh. Nếu chỉ có một dòng, revisionHistoryLimit đã bị đặt về 0 — và lần triển khai hỏng tiếp theo sẽ phải sửa bằng cách xây lại ảnh.
Phần sau đo rolling update: cùng một lần đổi ảnh, có readinessProbe thì 0 lỗi, không có thì 8% yêu cầu thất bại.