Deployment, rolling update và rollback
Mục tiêu bài học
Sau bài này, bạn phải làm được 4 việc trong khoảng 2 đến 3 phút mỗi việc, đúng kiểu đề CKA:
- Tạo Deployment bằng lệnh imperative và bằng YAML.
- Cập nhật image an toàn, hiểu
maxSurgevàmaxUnavailableđang làm gì. - Theo dõi rollout bằng
kubectl rollout statusvàkubectl get rs. - Rollback về revision trước, hoặc về một revision cụ thể bằng
--to-revision.
Ôn nhanh
Ở Module 1 bạn đã dùng kubectl create ... --dry-run=client -o yaml để sinh YAML nhanh và kubectl explain để tra field. Hai thói quen đó quay lại ngay trong bài này: mọi Deployment trong đề thi đều nên bắt đầu bằng dry-run rồi sửa YAML, thay vì gõ tay từ đầu.
Một điểm nhắc lại: kubectl trên cluster lab của bạn có quyền admin, nhưng đề thi luôn ghi rõ namespace. Mất điểm vì quên -n là lỗi phổ biến nhất của người mới.
Ví dụ mẫu từng bước
Đề bài kiểu thi: "Tạo Deployment tên web, image nginx:1.25, 3 replicas. Cập nhật lên nginx:1.26 theo kiểu rolling update với maxSurge=1, maxUnavailable=0. Sau đó rollback về revision 1."
Bước 1. Tạo Deployment bằng lệnh imperative.
kubectl create deployment web --image=nginx:1.25 --replicas=3
kubectl get deploy,rs,pod -l app=web
Bạn sẽ thấy một ReplicaSet tên dạng web-7d9f8c6b5 và 3 Pod. Tên ReplicaSet chính là tên-deployment + hash của pod template.
Bước 2. Đặt change-cause trước khi sửa, để history có ý nghĩa.
kubectl annotate deployment/web kubernetes.io/change-cause="nginx:1.26"
kubectl rollout history chỉ hiển thị cột CHANGE-CAUSE khi bạn gắn annotation này. Cờ --record ngày xưa đã bị bỏ, đừng dùng nữa.
Bước 3. Cấu hình rolling update.
kubectl patch deployment web -p '{"spec":{"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'
Lưu ý: sửa strategy không làm Pod restart, nó chỉ có hiệu lực ở lần update template tiếp theo.
Bước 4. Cập nhật image và theo dõi.
kubectl set image deployment/web nginx=nginx:1.26
kubectl rollout status deployment/web
Tên container ở đây là nginx (lấy từ tên image khi tạo bằng kubectl create deployment). Nếu sai tên container, lệnh báo unable to find container named ....
Kết quả mong đợi:
Waiting for deployment "web" rollout to finish: 1 out of 3 new replicas have been updated...
deployment "web" successfully rolled out
Trong lúc chạy, mở terminal thứ hai:
kubectl get rs -w
Bạn sẽ thấy ReplicaSet cũ giảm dần, ReplicaSet mới tăng dần. Với maxUnavailable=0, cluster luôn giữ đủ 3 Pod Ready, nên ReplicaSet mới phải scale lên trước (surge thêm 1 Pod) rồi mới hạ Pod cũ.
Bước 5. Xem history và rollback.
kubectl rollout history deployment/web
kubectl rollout undo deployment/web --to-revision=1
kubectl rollout status deployment/web
Kiểm tra lại image đang chạy:
kubectl get deploy web -o jsonpath='{.spec.template.spec.containers[0].image}'
Kết quả: nginx:1.25.
Thực hành có hướng dẫn
Làm lần lượt 4 bài, bấm giờ như thi thật. Bài nào quá thời gian thì ghi lại lệnh đã quên.
Bài tập 1: Tạo Deployment bằng YAML từ dry-run (5 phút)
- Sinh YAML:
kubectl create deployment api --image=nginx:1.25 --replicas=2 --dry-run=client -o yaml > api.yaml
- Mở file, sửa
replicasthành 4, thêmstrategyrolling update vớimaxSurge: 25%,maxUnavailable: 25%. - Apply và kiểm tra:
kubectl apply -f api.yaml
kubectl get deploy api -o wide
kubectl describe deploy api | grep -A5 StrategyType
Kết quả mong đợi: Deployment api có 4 Pod Ready, phần Strategy hiển thị RollingUpdateStrategy: 25% max unavailable, 25% max surge.
Tự kiểm tra: maxSurge tính theo đơn vị gì khi bạn ghi 25%? (Trả lời: phần trăm của số replicas, làm tròn lên.)
Bài tập 2: Rolling update có kiểm soát (8 phút)
- Đặt
maxSurge=1,maxUnavailable=0choapi. - Chạy song song hai lệnh:
kubectl rollout status deployment/apivàkubectl get pod -w. - Trong lúc rollout đang chạy, thực hiện
kubectl set image deployment/api nginx=nginx:1.26.
Quan sát: số Pod Ready không bao giờ xuống dưới 4, và tổng số Pod có lúc lên 5.
- Thử tiếp
kubectl rollout pause deployment/api, đổi image một lần nữa, rồikubectl rollout resume deployment/api.
Kết quả mong đợi: Khi pause, ReplicaSet mới bị đứng yên, không có Pod mới nào được tạo thêm. Sau resume, rollout chạy tiếp.
Bài tập 3: Gây lỗi ImagePullBackOff rồi rollback (7 phút)
Đây là tình huống đề thi rất hay gặp: "Deployment đang lỗi, hãy đưa nó về trạng thái chạy tốt trước đó".
- Gây lỗi:
kubectl set image deployment/api nginx=nginx:1.99-khong-ton-tai
kubectl rollout status deployment/api --timeout=30s
Lệnh thứ hai sẽ treo rồi thoát với lỗi timed out waiting for the condition.
- Chẩn đoán:
kubectl get pods
kubectl describe pod <pod-mới-nhất> | tail -n 15
Bạn sẽ thấy ErrImagePull rồi ImagePullBackOff trong phần Events.
- Rollback:
kubectl rollout undo deployment/api
kubectl rollout status deployment/api
Điểm quan trọng: các Pod cũ vẫn còn chạy trong suốt thời gian lỗi, vì maxUnavailable chặn việc hạ Pod cũ khi Pod mới chưa Ready. Đây chính là lý do rolling update an toàn hơn Recreate.
Bài tập 4: Rollback về revision cụ thể (5 phút)
- Thực hiện 3 lần đổi image, mỗi lần đặt
kubernetes.io/change-causemô tả rõ (ví dụnginx:1.26,nginx:1.27). - Xem history:
kubectl rollout history deployment/api
- Xem chi tiết một revision:
kubectl rollout history deployment/api --revision=2
- Quay về revision 2:
kubectl rollout undo deployment/api --to-revision=2
Kết quả mong đợi: image trở về đúng giá trị của revision 2. History sẽ có một revision mới mang template cũ, nên đừng ngạc nhiên nếu số revision cũ không còn xuất hiện với đúng số đó nữa.
flowchart TD
D["🚀<br/>Deployment api"]
RS1["📦<br/>ReplicaSet cũ<br/>nginx:1.25"]
RS2["📦<br/>ReplicaSet mới<br/>nginx:1.26"]
P1["🟢<br/>Pod cũ Ready"]
P2["🟡<br/>Pod mới đang khởi động"]
D --> RS1
D --> RS2
RS1 --> P1
RS2 --> P2
Bảng lệnh rollout cần thuộc lòng
| Lệnh | Tác dụng |
|---|---|
kubectl rollout status deployment/web |
Theo dõi tiến trình, thoát khi xong hoặc fail |
kubectl rollout history deployment/web |
Liệt kê các revision |
kubectl rollout history deployment/web --revision=2 |
Xem chi tiết image của revision 2 |
kubectl rollout undo deployment/web |
Quay về revision ngay trước |
kubectl rollout undo deployment/web --to-revision=1 |
Quay về revision chỉ định |
kubectl rollout pause / resume |
Tạm dừng / chạy tiếp |
kubectl rollout restart deployment/web |
Tạo revision mới, thay toàn bộ Pod, giữ nguyên image |
Lỗi thường gặp và cách xử lý
| Hiện tượng | Nguyên nhân | Cách xử lý |
|---|---|---|
unable to find container named ... |
Sai tên container trong kubectl set image |
Lấy tên đúng: kubectl get deploy web -o jsonpath='{.spec.template.spec.containers[*].name}' |
Rollout treo mãi, kubectl get pods thấy ImagePullBackOff |
Sai tag image hoặc private registry thiếu imagePullSecret | kubectl rollout undo để thoát nhanh, sau đó sửa tag |
field is immutable khi apply |
Bạn sửa spec.selector |
Xoá và tạo lại Deployment, selector của apps/v1 không đổi được |
History chỉ hiện <none> ở CHANGE-CAUSE |
Chưa gắn annotation kubernetes.io/change-cause |
Annotate trước mỗi lần đổi template |
invalid: spec.strategy.rollingUpdate.maxUnavailable: Invalid value: 0 ... maxSurge: 0 |
Cả hai field cùng bằng 0 | Phải để ít nhất một field lớn hơn 0 |
| Rollout xong nhưng Pod vẫn restart liên tục | Lỗi application, không phải lỗi Deployment | Kiểm tra kubectl logs và kubectl describe pod |
| Sửa strategy mà không thấy Pod đổi | Strategy không nằm trong pod template | Bình thường, strategy chỉ áp dụng ở lần update sau |
[!NOTE] Mẹo thi: khi đề cho một Deployment đang lỗi và yêu cầu "khôi phục phiên bản trước", đừng mất thời gian đọc log application. Chạy
kubectl rollout history, chọn revision đúng,kubectl rollout undo --to-revision=N, rồikubectl rollout status. Xong trong khoảng 60 giây.
[!WARNING] Sửa Deployment bằng
kubectl editrất dễ tạo revision rác, vì mỗi lần lưu có thể đổi template nhiều lần. Trong thi, dùngkubectl set imagehoặckubectl patchđể mỗi thao tác là một revision rõ ràng.
Góc chuyên gia
Vài điều mình ước biết trước khi thi:
kubectl set imagenhanh hơnkubectl editrất nhiều, và không sợ lưu nhầm. Dùng nó làm mặc định.- ==
rollout undokhông xoá revision, nó tạo revision mới từ template cũ.== Nên bạn có thể undo nhiều lần liên tiếp mà vẫn quay lại được các bản trước, miễn là chưa vượtrevisionHistoryLimit(mặc định 10). - Luôn đặt
kubernetes.io/change-causetrước khi đổi template. Đây là điểm "dễ lấy" trong các đề yêu cầu "ghi lại lý do thay đổi". - Trước khi nộp, chạy đúng 3 lệnh kiểm tra:
kubectl get deploy -o wide,kubectl rollout status deployment/<tên>,kubectl get pod. Ba lệnh này bắt được gần hết lỗi. - Với Deployment gắn PVC
ReadWriteOncenhiều replica, hãy dùngstrategy.type: Recreate, vì rolling update sẽ làm nhiều Pod tranh nhau mount cùng volume và bịMulti-Attach error. - Đề có thể yêu cầu "scale rồi update". Thứ tự an toàn: scale trước, update sau, để
maxUnavailabletính trên số replica cuối cùng. - Alias tiết kiệm thời gian thật:
alias k=kubectlvàexport do="--dry-run=client -o yaml". Gõk create deploy api --image=nginx $do > api.yamlnhanh hơn hẳn.
Tóm tắt và bước tiếp theo
Bốn điều cần mang ra khỏi bài này:
- Deployment quản lý ReplicaSet, ReplicaSet quản lý Pod. Mỗi lần đổi pod template là một ReplicaSet mới, tức một revision mới.
maxSurgevàmaxUnavailablequyết định tốc độ và độ an toàn của rolling update, mặc định 25% mỗi cái.- Bộ lệnh
kubectl rollout status / history / undo / pause / resume / restartlà công cụ chính, thuộc lòng cả 6. - Khi rollout lỗi,
kubectl rollout undolà cách thoát nhanh nhất, và Pod cũ vẫn an toàn nhờmaxUnavailable.
Kiểm tra nhanh bằng cloze dưới đây trước khi sang bài sau:
Deployment quản lý Pod thông qua object trung gian là {{c1::ReplicaSet}}.
Với strategy RollingUpdate, hai tham số kiểm soát tốc độ là maxSurge và {{c2::maxUnavailable}}, mặc định đều bằng {{c3::25%}}.
Lệnh theo dõi tiến trình rollout là: kubectl rollout {{c4::status}} deployment/web.
Quay về revision 1 bằng: kubectl rollout undo deployment/web {{c5::--to-revision=1}}.
Ghi lý do thay đổi vào history bằng annotation {{c6::kubernetes.io/change-cause}}.
Flashcard ôn tập
[
{ "front": "Deployment quản lý Pod thông qua object nào?", "back": "ReplicaSet, và ReplicaSet quản lý trực tiếp các Pod." },
{ "front": "Hai field điều khiển rolling update nằm ở đâu?", "back": "spec.strategy.rollingUpdate với maxSurge và maxUnavailable." },
{ "front": "Giá trị mặc định của maxSurge và maxUnavailable?", "back": "25% cho cả hai." },
{ "front": "Lệnh theo dõi tiến trình rollout?", "back": "kubectl rollout status deployment/<tên>" },
{ "front": "Lệnh xem lịch sử revision?", "back": "kubectl rollout history deployment/<tên>, thêm --revision=N để xem chi tiết." },
{ "front": "Lệnh rollback về revision ngay trước?", "back": "kubectl rollout undo deployment/<tên>" },
{ "front": "Lệnh rollback về một revision cụ thể?", "back": "kubectl rollout undo deployment/<tên> --to-revision=N" },
{ "front": "Làm sao để cột CHANGE-CAUSE có dữ liệu?", "back": "Gắn annotation kubernetes.io/change-cause trước khi đổi pod template." },
{ "front": "Strategy nào gây downtime?", "back": "Recreate: xoá hết Pod cũ rồi mới tạo Pod mới." },
{ "front": "Field giới hạn số revision được giữ lại?", "back": "spec.revisionHistoryLimit, mặc định là 10." },
{ "front": "Điều gì xảy ra khi sửa spec.selector của Deployment?", "back": "Báo lỗi field is immutable, phải xoá và tạo lại Deployment." },
{ "front": "Lệnh restart toàn bộ Pod mà không đổi image?", "back": "kubectl rollout restart deployment/<tên>" },
{ "front": "Vì sao rollout bị treo khi image sai tag?", "back": "Pod mới ImagePullBackOff và không Ready, maxUnavailable giữ Pod cũ nên rollout chờ mãi." },
{ "front": "Lệnh tạm dừng và chạy tiếp rollout?", "back": "kubectl rollout pause deployment/<tên> và kubectl rollout resume deployment/<tên>" }
]
Bài tiếp theo: DaemonSet, Job, CronJob. Deployment phù hợp với workload chạy dài hạn và cần rolling update, nhưng CKA còn hỏi hai kiểu khác: chạy đúng một Pod trên mỗi node (DaemonSet) và workload chạy một lần hoặc theo lịch (Job, CronJob). Bài sau sẽ đi vào cú pháp và các bẫy thường gặp của hai loại này.