---
id: kubernetes-rollout
title: "Rolling update và rollback"
shortTitle: "Rollout"
track: kubernetes
trackTitle: "Kubernetes"
order: 4
level: core
status: published
estimatedMinutes: 40
prerequisites:
  - kubernetes-kubectl
next: kubernetes-volumes
reviewedAgainst: 2026-10-15
sources:
  - title: "KodeKloud Engineer — 100 Days of DevOps"
    url: https://engineer.kodekloud.com/practice
  - title: "Deployments"
    url: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
  - title: "kubectl rollout"
    url: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_rollout/
---

# Rolling update và rollback

Đổi image trên Deployment không tắt hết replica một lúc. Controller tạo ReplicaSet mới, tăng dần bản mới, giảm bản cũ. Image sai thì rollback về revision trước.

## Để làm gì

Bạn đổi image bằng `kubectl set image` hoặc sửa YAML, theo dõi `rollout status`, đọc `rollout history`, và `rollout undo` khi bản mới không lên.

## Gồm những gì

- Strategy mặc định `RollingUpdate`: `maxUnavailable` 25%, `maxSurge` 25%.
- Revision chỉ khi đổi Pod template.
- `kubectl set image`, `rollout status` / `history` / `undo`.
- `--to-revision`.

`Recreate` (tắt hết rồi bật) không làm trong lab này. Đừng sửa ReplicaSet tay — docs bảo ReplicaSet của Deployment do controller giữ.

## Dùng khi nào

Đã có Deployment đang chạy. Cần bản image mới, hoặc bản vừa set bị `ImagePullBackOff` / crash.

Scale `replicas` không phải rollout. `undo` không trả số replica về trước khi scale.

## Kiến thức cốt lõi

Mỗi lần template đổi (image, env, label, …), Deployment tạo ReplicaSet mới và tăng revision. RS cũ scale về 0, chưa xóa ngay — còn để rollback.

Mặc định RollingUpdate: ít nhất 75% replica còn (25% `maxUnavailable`), nhiều nhất 125% (`maxSurge` 25%). Ví dụ 3 replica: luôn khoảng 3 available, tổng không quá 4 lúc chuyển. Số chính xác docs tính theo phần trăm, làm tròn.

Rollout kẹt (tag không tồn tại): controller ngừng scale RS mới theo `maxUnavailable`. Bản cũ vẫn phục vụ. `rollout status` treo chữ waiting. Ctrl+C thoát lệnh theo dõi, không dừng controller.

`kubectl set image deployment/web-app nginx=nginx:1.27-alpine` đổi image container tên `nginx`. Tên sau dấu `=` là image, tên trước là tên container trong spec, không phải tên Deployment.

```bash
kubectl rollout undo deployment/web-app
kubectl rollout undo deployment/web-app --to-revision=2
```

Không ghi `--to-revision` thì về revision ngay trước. `rollout history` liệt kê. `--record` cũ đã deprecated, đừng dùng.

`.spec.revisionHistoryLimit` là số ReplicaSet cũ được giữ. Docs Deployment: mặc định **10**. Hết RS thì `--to-revision` không còn bản đó.

`kubectl rollout restart deployment/web-app` tạo rollout mới với cùng template — để kéo secret/config mới, không đổi image.

## Lệnh và code mẫu

Giả sử Deployment `web-app`, container `nginx`, đã apply bài trước.

```bash
kubectl set image deployment/web-app nginx=nginx:1.27-alpine
kubectl rollout status deployment/web-app
kubectl get rs
kubectl rollout history deployment/web-app
```

Giả lập tag sai rồi hoàn tác (chỉ trên cluster lab):

```bash
kubectl set image deployment/web-app nginx=nginx:tag-khong-ton-tai
kubectl get pods
kubectl rollout undo deployment/web-app
kubectl rollout status deployment/web-app
```

Đọc một revision:

```bash
kubectl rollout history deployment/web-app --revision=2
```

Sửa YAML `spec.template.spec.containers[0].image` rồi `kubectl apply -f` cũng ra cùng một kiểu rollout. `set image` chỉ tiện khi không mở file.

## Thực hành

1. `rollout history` trước khi đổi — ghi revision hiện tại.
2. `set image` sang một tag Nginx khác đã kiểm trên Hub, hoặc apply lại file đã sửa image.
3. `rollout status` tới successful.
4. `get rs`: một RS desired=3, RS cũ desired=0.
5. (Lab) set image sai, thấy `ImagePullBackOff`, `undo`, đợi Ready lại.

## Kết quả mong đợi

- Status in `successfully rolled out`.
- Pod mới dùng image vừa chọn (`kubectl get pod -o yaml` hoặc `describe`).
- Sau undo, Pod trở lại image trước, READY đủ số replica.

## Checklist

- [ ] Đổi image bằng `set image` đúng tên container.
- [ ] `status` xong mới coi rollout thành công.
- [ ] `history` trước khi `undo --to-revision`.
- [ ] Không `kubectl delete rs` tay.
- [ ] Phân biệt scale và đổi template.

## Thẻ ôn

### kubernetes-rollout-set-image
kind: command
cue: `kubectl set image deploy/TÊN CONTAINER=image:tag`. Tên trước `=` là container, không phải Deployment.

Q: Deployment `web-app`, container trong spec tên `nginx`. Đổi sang `nginx:1.27-alpine` bằng một lệnh, rồi chờ xong. Viết thế nào?

A: `kubectl set image deployment/web-app nginx=nginx:1.27-alpine` rồi `kubectl rollout status deployment/web-app`. Sai tên container thì kubectl báo không tìm thấy. `apply -f` file đã sửa image cũng được, cùng cơ chế ReplicaSet mới.

### kubernetes-rollout-revision
kind: distinguish
cue: Revision chỉ khi đổi Pod template. Scale không tạo revision. `undo` không trả số replica.

Q: Bạn tăng `replicas` 3 → 5, rồi `set image` sai, rồi `undo`. Số replica sau undo là bao nhiêu?

A: Vẫn 5, nếu bạn không sửa `replicas`. Undo chỉ hoàn template (image, env, …). Scale không nằm trong revision.

### kubernetes-rollout-undo
kind: command
cue: `kubectl rollout undo deployment/TÊN` về bản ngay trước. `--to-revision=N` chọn đúng số trên `history`.

Q: Image mới `ImagePullBackOff`. `history` có revision 1 (ổn) và 2 (hỏng). Lệnh nào về bản ổn?

A: `kubectl rollout undo deployment/web-app` (2 → 1) hoặc `--to-revision=1`. Rồi `rollout status`. Xóa Pod đang pull không thay template; RS mới vẫn tạo Pod cùng image hỏng.

### kubernetes-rollout-defaults
kind: default
cue: RollingUpdate mặc định maxUnavailable 25%, maxSurge 25%. Controller không tắt hết replica một lúc.

Q: Deployment 4 replica, strategy để mặc định. Lúc đổi image, số Pod available tối thiểu khoảng bao nhiêu, và vì sao không `delete` hết Pod rồi apply?

A: Docs: ít nhất 75% desired còn — với 4 replica là khoảng 3 available; surge tối đa 25% nên tổng có lúc 5. Xóa hết Pod rồi tạo lại là downtime, đó gần `Recreate`, không phải RollingUpdate mặc định.

### kubernetes-rollout-rs
kind: trap
cue: Đừng sửa ReplicaSet của Deployment. Controller giữ RS. Xóa RS đang dùng thì Deployment tạo lại; xóa nhầm lịch sử thì mất chỗ undo.

Q: `get rs` thấy RS cũ desired=0. Xóa cho sạch được không?

A: Đừng, nếu còn muốn `undo`. RS cũ là revision. Hết RS thì không rollback được bản đó. Docs: không quản ReplicaSet thuộc Deployment.

## Lỗi thường gặp

**`set image` đúng tên Deployment, sai tên container.** Lệnh fail hoặc không đổi gì. `kubectl get deploy web-app -o yaml` đọc `containers[].name`.

**Undo rồi `get pod` vẫn image mới.** Rollout chưa xong, hoặc `--to-revision` nhầm. `status` + `history --revision=`.

**Dùng `--record`.** Flag deprecated. `CHANGE-CAUSE` để trống cũng được; cần ghi chú thì `kubectl annotate` `kubernetes.io/change-cause=`.

**Tưởng `rollout restart` là undo.** Restart tạo revision mới cùng template. Undo mới về image cũ.

## Nguồn

1. [KodeKloud Engineer — 100 Days of DevOps](https://engineer.kodekloud.com/practice) — đề cương ngày Execute Rolling Updates, Revert Deployment to Previous Version. Trang practice cần đăng nhập. Bài này viết lại lệnh, không chép lab.
2. [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) — RollingUpdate 25%/25%, revision, `set image`, `undo`, `--to-revision`.
3. [kubectl rollout](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_rollout/) — `status`, `undo`, `restart`, `history`.

## Tiếp theo

[Volume và PersistentVolume](./11-volumes.md)
