3. Pods, Replication and Deployments
Mục lục
Pods
1. Cấu trúc file định nghĩa Kubernetes là gì?
Kubernetes dùng file YAML làm input để tạo object — có thể là Pod, ReplicaSet, Deployment, Service... Tất cả đều theo chung 1 cấu trúc: mọi file định nghĩa Kubernetes luôn có đúng 4 field bắt buộc ở root level: apiVersion, kind, metadata, spec.
💡 Hình dung như thế này: file định nghĩa Kubernetes giống 1 kiện hàng gửi bưu điện.
kindvàapiVersiongiống nhãn dán ngoài kiện hàng — cho biết ngay đây là loại hàng gì, theo quy chuẩn đóng gói phiên bản nào để xử lý đúng cách.metadatagiống thông tin người gửi/người nhận kèm theo — dùng để định danh, tra cứu, phân loại kiện hàng sau này (nhãn phụ như "dễ vỡ", "ưu tiên" cũng nằm ở đây, tương tựlabels). Cònspecmới là nội dung thật sự bên trong kiện hàng — thứ Kubernetes cần biết chi tiết để "đóng gói" đúng ứng dụng của bạn.
Nhìn tổng quan cấu trúc cây trước khi đi vào chi tiết từng field:
(app: myapp bên trong labels không thể hiện riêng trên diagram để tránh rối — xem chi tiết trong ví dụ YAML ngay bên dưới.)
apiVersion — phiên bản Kubernetes API dùng để tạo object. Tùy loại object cần tạo mà chọn đúng version. Với Pod, dùng v1. Với các object khác từng dùng các group version như apps/v1beta1, apps/v1beta2, extensions/v1beta1 — nhưng các group version này đã bị xóa khỏi Kubernetes từ v1.16, hiện tại đều gộp về apps/v1.
kind — loại object cần tạo, ở đây là Pod. Các giá trị khác có thể là ReplicaSet, Deployment, Service.
metadata — dữ liệu mô tả về object: tên, label... Khác với apiVersion/kind (chỉ là 1 giá trị string đơn), metadata là 1 dictionary — bên trong nó chứa nhiều field con, thụt vào 1 cấp so với metadata. Với Pod, 2 field hay dùng nhất là name (tên object, 1 giá trị string) và labels (1 dictionary con khác, chứa các cặp key-value tùy ý để gắn nhãn).
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
⚠️ Lưu ý về indentation: nhìn ví dụ trên —
namevàlabelsđều thụt vào đúng bằng nhau so vớimetadata, nghĩa là 2 field này cùng 1 cấp (cùng là con trực tiếp củametadata). Số khoảng trắng cụ thể là bao nhiêu không quan trọng, miễn nhất quán trong cả file — quan trọng là nếulabelslỡ thụt sâu hơnname1 cấp, Kubernetes sẽ hiểu nhầmlabelslà con củanamechứ không phải con củametadatanữa — sai ý muốn.
name giữ giá trị string đơn giản (ví dụ đặt tên Pod là myapp-pod), còn labels là dictionary lồng bên trong, có thể chứa bất kỳ key-value pair nào tùy ý (ví dụ app: myapp ở trên). Gắn label giúp nhóm và lọc lại các object sau này — ví dụ có hàng trăm Pod chạy frontend và hàng trăm Pod chạy backend/database, nếu gắn label frontend, backend, database ngay từ đầu thì sau này lọc theo label sẽ dễ dàng hơn nhiều.
💡 Dưới
metadatachỉ được dùng field Kubernetes đã định nghĩa sẵn (name,labels,...) — không tự thêm field tùy ý. Nhưng bên tronglabelsthì ngược lại: thêm bao nhiêu key-value tùy thích, không giới hạn.
spec — phần khai báo nội dung cụ thể của object, format khác nhau tùy loại object (cần tra tài liệu để biết đúng format). Với Pod, spec là 1 dictionary chỉ chứa 1 field containers — 1 list, vì 1 Pod có thể chứa nhiều container (như đã học ở bài trước).
spec:
containers:
- name: nginx
image: nginx
Dấu - ngay trước name: nginx đánh dấu đây là phần tử đầu tiên (và duy nhất, trong ví dụ này) của list containers. Mỗi phần tử trong list lại là 1 dictionary riêng, gồm name (tên container, đặt tùy ý) và image (tên Docker image cần chạy — ở đây là image: nginx, lấy từ Docker repository).
Sau khi có file, chạy:
kubectl create -f pod-definition.yaml
# Output minh họa
pod/myapp-pod created
Kubernetes sẽ tạo Pod theo đúng nội dung file.
Tóm tắt: nhớ đúng 4 top-level property — apiVersion, kind, metadata, spec — rồi điền giá trị tùy theo loại object cần tạo.
Sau khi Pod đã tạo, kiểm tra bằng:
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 5s
Pod myapp-pod đã Running, 1/1 container sẵn sàng.
Xem thông tin chi tiết hơn (thời điểm tạo, label đã gán, container nào, events liên quan) bằng:
kubectl describe pod
# Output minh họa (rút gọn) — không truyền tên Pod thì kubectl mô tả toàn bộ Pod đang có trong namespace
Name: myapp-pod
Namespace: default
Labels: app=myapp
Status: Running
IP: 172.17.0.3
Containers:
nginx:
Image: nginx
State: Running
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 8s default-scheduler Successfully assigned default/myapp-pod to minikube
Normal Pulled 6s kubelet Successfully pulled image "nginx"
Normal Created 6s kubelet Created container nginx
Normal Started 6s kubelet Started container nginx
Mục Events ở cuối cho thấy đúng trình tự Pod đã trải qua: gán vào node, pull image nginx thành công, rồi container được tạo và khởi động.
2. Demo: Tạo Pod bằng YAML với Vim
Lần này tạo Pod bằng file YAML thay vì lệnh kubectl run. Có nhiều cách tạo file YAML — trên Windows dùng Notepad, trên Linux dùng editor có sẵn như VI/Vim. Một editor có hỗ trợ ngôn ngữ YAML sẽ giúp viết đúng cú pháp dễ hơn hẳn — ví dụ Notepad++ (Windows) thay vì Notepad thường. Phần này vẫn dùng cách cơ bản nhất: Vim trên Linux (các công cụ/IDE hỗ trợ nhiều hơn sẽ nói ở phần sau).
Tạo file pod.yaml, khai báo đủ 4 root property:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
tier: frontend
spec:
containers:
- name: nginx
image: nginx
Vài điểm cần lưu ý khi gõ:
kind: Pod— chữ P viết hoa, vì Kubernetes case-sensitive.namevàlabelsphải cùng 1 cấp thụt (cùng là con củametadata), tương tựapp/tierphải cùng 1 cấp (cùng là con củalabels).- Khoảng trắng dùng để thụt: thường là 2 space hoặc 1 tab — nhưng khuyến nghị không dùng tab, chỉ dùng 2 space và giữ nhất quán xuyên suốt file.
containerslà 1 list — container đầu tiên đặt tên trùng tên image cho dễ nhớ (ở đây lànginx); có thể thêm container thứ 2 (ví dụ dùng imagebusybox) bằng cách thêm phần tử tiếp theo trong list, nhưng demo này chỉ giữ 1 container.
Lưu file trong Vim bằng Escape rồi :wq, sau đó bạn dùng cat để xem lại nội dung file vừa lưu, xác nhận đúng định dạng.
Tạo Pod bằng 1 trong 2 lệnh sau — create và apply hoạt động như nhau khi tạo mới object:
kubectl create -f pod.yaml
# hoặc
kubectl apply -f pod.yaml
# Output minh họa
pod/nginx created
Kiểm tra trạng thái Pod — ban đầu ở ContainerCreating, sau đó chuyển sang Running:
kubectl get pods
# Output minh họa — gọi ngay sau khi tạo
NAME READY STATUS RESTARTS AGE
nginx 0/1 ContainerCreating 0 2s
kubectl get pods
# Output minh họa — gọi lại vài giây sau, image đã pull xong và container đã start
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 9s
Chuyển từ ContainerCreating sang Running đúng như dự kiến, sau khi image nginx pull xong.
Xem thông tin chi tiết hơn:
kubectl describe pod nginx
# Output minh họa (rút gọn)
Name: nginx
Namespace: default
Labels: app=nginx
tier=frontend
Status: Running
IP: 172.16.0.3
Node: minikube/192.168.99.100
Containers:
nginx:
Image: nginx
State: Running
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 9s default-scheduler Successfully assigned default/nginx to minikube
Normal Pulled 7s kubelet Successfully pulled image "nginx"
Normal Created 7s kubelet Created container nginx
Normal Started 7s kubelet Started container nginx
Cả 2 label app=nginx và tier=frontend đã gán đúng như khai trong pod.yaml.
3. Dùng IDE để viết YAML cho Kubernetes
Nhưng nếu file YAML dài hơn, nhiều field lồng nhau hơn — chỉ dựa vào mắt để đếm khoảng trắng như ở Vim liệu có đủ an toàn? Không hẳn. Vim (hay Notepad trên Windows) khá cơ bản, dễ gặp lỗi khi viết YAML dài hay nhiều file — cả 2 đều không tự báo lỗi khi gõ sai. Dùng IDE sẽ dễ phát triển file định nghĩa Kubernetes hơn hẳn. Nhiều IDE miễn phí hỗ trợ YAML sẵn: JetBrains (IntelliJ IDEA, PyCharm), Atom, Eclipse, NetBeans — nhưng chúng chỉ validate đúng cú pháp YAML nói chung, không tự báo lỗi riêng cho Kubernetes manifest (ví dụ gõ sai tên property, hay dùng dictionary thay vì list) — cần thêm extension/plugin (miễn phí hoặc trả phí) mới có được phần đó.
IDE được chọn cho phần còn lại của khóa học: Microsoft Visual Studio Code (VS Code) — miễn phí, chạy được trên mọi hệ điều hành, có sẵn extension miễn phí validate YAML cho Kubernetes, dễ setup. Có thể dùng IDE khác tùy thích, miễn tìm được plugin/extension tương đương.
Cài đặt:
- Vào
code.visualstudio.com, tải bản phù hợp hệ điều hành (ví dụ Debian package cho Ubuntu). - Cài đặt, mở VS Code.
- Vào mục Extensions, tìm YAML — chọn extension do Red Hat phát hành (extension này có hỗ trợ Kubernetes YAML). Nhiều trường hợp extension này đã được cài sẵn mặc định.
Cấu hình để bật Kubernetes support (mặc định extension chỉ validate YAML thường):
- Bấm icon bánh răng trên extension YAML → Extension Settings → cuộn xuống mục YAML: Schemas → Edit in settings.json. (Cách khác:
Ctrl+P→ gõsettings.json.) - Thêm 1 đoạn khai báo Kubernetes schema áp dụng cho pattern
*.yamlvào filesettings.json— nghĩa là dùng Kubernetes schema cho mọi file.yamlđược viết trong VS Code (có thể giới hạn theo pattern khác nếu không muốn áp dụng cho mọi file.yaml). Chữkubernetestrong khai báo này case-sensitive — phải viết thường toàn bộ.
{
"yaml.schemas": {
"kubernetes": "*.yml"
},
"yaml.disableSchemaDetection": [
]
- Reload/restart lại VS Code để thay đổi có hiệu lực.
Trải nghiệm với YAML extension khi viết file mới (nginx.yaml, cùng thư mục kubernetes-for-beginners với pod.yaml đã tạo ở demo trước):
apiVersionphải là dòng đầu tiên thì các gợi ý (suggestion) phía sau mới hoạt động đúng.- Gõ
k, extension gợi ý luônkind— nhấn Tab để auto-complete. Ctrl+Spacerồi gõp→ gợi ý danh sách object bắt đầu bằng P → chọnPod. Tương tự cho các phần phía sau.
⚠️ Extension không validate mọi thứ — tên image (image name) không được kiểm tra có tồn tại thật trên Docker Registry hay không, tên Pod/container/label cũng không bị giới hạn giá trị (có thể đặt bất kỳ tên gì). Nhưng field nào Kubernetes yêu cầu giá trị cố định — ví dụ
kindphải là 1 trong các giá trị Kubernetes hỗ trợ nhưPodhayDeployment— thì vẫn được validate, gõ sai sẽ báo lỗi ngay.
Có thể thêm container thứ 2 vào list containers bằng cách thêm phần tử mới với dấu - (ví dụ container tên busybox, image busybox) rồi xóa đi nếu không cần — minh họa cách mở rộng list.
Xem cấu trúc file bằng Outline: VS Code có mục Outline hiển thị toàn bộ cấu trúc file YAML dạng cây (tree) — dictionary hiện dưới dạng dấu ngoặc nhọn, array nhận diện qua dấu -. Ví dụ: 4 root element (apiVersion: v1, kind: Pod, metadata chứa labels với giá trị env, spec chứa array containers — phần tử đầu tiên có 2 key-value: image: nginx và name: nginx). Rất hữu ích để dò lỗi cấu trúc khi không chắc sai ở đâu.
Sau khi lưu file, bạn quay lại terminal, chạy ls để xác nhận file nginx.yaml đã có trong thư mục kubernetes-for-beginners, rồi tạo Pod bằng kubectl create hoặc kubectl apply, truyền file YAML làm input như bình thường.
Replication
4. Replication Controller và ReplicaSet là gì?
Controller là "bộ não" của Kubernetes — các tiến trình liên tục theo dõi những Kubernetes object và tự động phản ứng khi cần. Phần này nói về 1 loại controller cụ thể: Replication Controller.
Vì sao cần đến replica (nhiều bản sao) và Replication Controller? Quay lại kịch bản 1 Pod duy nhất chạy ứng dụng: nếu vì lý do nào đó ứng dụng crash và Pod chết, user sẽ mất quyền truy cập ứng dụng ngay lập tức. Để tránh việc này, nên chạy nhiều hơn 1 instance/Pod cùng lúc — nếu 1 cái chết, vẫn còn cái khác phục vụ user. Replication Controller giúp chạy nhiều instance của cùng 1 Pod trong cluster, nhờ đó có được high availability.
Vậy nếu ứng dụng chỉ cần đúng 1 Pod thì có dùng được Replication Controller không? Có. Kể cả khi chỉ cần 1 Pod, Replication Controller vẫn hữu ích — nó tự động dựng lại 1 Pod mới ngay khi Pod hiện tại chết. Replication Controller đảm bảo luôn có đúng số lượng Pod đã chỉ định đang chạy, dù con số đó là 1 hay 100.
Lý do thứ 2 cần Replication Controller: tạo nhiều Pod để chia tải giữa chúng. Ví dụ: ban đầu 1 Pod phục vụ 1 nhóm user; khi số user tăng, deploy thêm Pod để chia tải giữa 2 Pod. Nếu nhu cầu tiếp tục tăng và node hiện tại hết tài nguyên, có thể deploy thêm Pod trên các node khác trong cluster — Replication Controller trải rộng trên nhiều node, vừa cân bằng tải vừa giúp scale ứng dụng khi nhu cầu tăng.
💡 Hình dung như thế này: Replication Controller/ReplicaSet giống 1 người quản lý ca trực luôn phải đảm bảo đúng số nhân viên yêu cầu có mặt. Nếu 1 nhân viên đột xuất nghỉ (Pod chết), người quản lý lập tức gọi người khác vào thay — không cần biết ai vừa nghỉ, chỉ cần biết đúng quân số luôn đủ. Kể cả ca trực chỉ cần 1 người, quy tắc này vẫn áp dụng: vắng là gọi ngay người bù vào.
Có 2 thuật ngữ dễ nhầm: Replication Controller và ReplicaSet — cùng mục đích nhưng không phải 1 thứ. Replication Controller là công nghệ cũ, đang được thay thế bởi ReplicaSet — cách làm mới được khuyến nghị. Mọi khái niệm vừa nói ở trên đều áp dụng cho cả 2, chỉ khác nhau về vài chi tiết cách hoạt động (sẽ nói ở phần dưới). Từ đây trở đi, các demo và ví dụ thực hành sẽ ưu tiên dùng ReplicaSet. Trên thực tế, khuyến nghị chính thức hiện nay còn đi xa hơn 1 bước: nên dùng Deployment (tự quản lý ReplicaSet bên dưới) thay vì tạo ReplicaSet trực tiếp, trừ khi cần tùy biến cách update — nội dung này sẽ nói kỹ ở phần Deployments sắp tới.
5. Cấu trúc file định nghĩa Replication Controller và ReplicaSet
Phần dưới đây trình bày cấu trúc file của cả 2 — Replication Controller trước để hiểu nguyên lý gốc, nhưng trọng tâm cần nhớ kỹ là ReplicaSet, vì đây mới là thứ dùng trong thực hành và các demo tiếp theo.
Replication Controller. Cũng như mọi file định nghĩa Kubernetes khác, file định nghĩa Replication Controller (đặt tên rc-definition.yaml) có đủ 4 field gốc: apiVersion, kind, metadata, spec.
apiVersion— Replication Controller được hỗ trợ ở Kubernetes API versionv1.kind—ReplicationController.metadata— thêmname(ví dụmyapp-rc) và vàilabelstùy chọn (ví dụapp,type).spec— phần quan trọng nhất. Vì Replication Controller tạo ra nhiều instance của 1 Pod,speccần biết Pod nào cần nhân bản — khai báo qua fieldtemplate, chứa nguyên pod template dùng để tạo replica.
Cách xây template không khó, vì thực chất chỉ là tái sử dụng file định nghĩa Pod đã tạo ở phần trước: copy toàn bộ nội dung file Pod (trừ 2 dòng đầu apiVersion/kind) vào bên trong template. Lưu ý: toàn bộ phần copy vào phải thụt sâu hơn dòng template, tức là con của template. Kết quả là file lúc này có 2 khối metadata (1 của Replication Controller, 1 của Pod) và 2 khối spec (1 cho mỗi cái) — thực chất là lồng 2 file định nghĩa vào nhau, Replication Controller là cha, Pod definition là con.
Vẫn còn thiếu 1 thứ: số lượng replica cần có. Thêm field replicas ngay dưới spec, cùng cấp với template (2 field này là sibling, phải thụt cùng 1 mức).
apiVersion: v1
kind: ReplicationController
metadata:
name: myapp-rc
# labels: app, type (ví dụ minh họa)
spec:
template:
# toàn bộ nội dung metadata + spec của Pod definition, thụt vào 1 cấp
replicas: 3
Sau khi file sẵn sàng, chạy kubectl create -f rc-definition.yaml. Replication Controller sẽ tạo Pod theo đúng pod template, đủ số lượng đã khai báo trong replicas. Xem danh sách Replication Controller đã tạo bằng kubectl get replicationcontroller, hoặc xem các Pod nó tạo ra bằng kubectl get pods — mọi Pod đều có tên bắt đầu bằng tên Replication Controller (myapp-rc), cho biết chúng được tạo tự động bởi Replication Controller.
ReplicaSet. Rất giống Replication Controller — cũng đủ 4 field gốc, cũng có template và replicas dưới spec — nhưng có 2 khác biệt quan trọng:
apiVersionkhác: ReplicaSet dùngapps/v1, không phảiv1như Replication Controller. Nếu khai nhầmv1, kubectl sẽ báo lỗi dạng "no match for kind ReplicaSet" vì phiên bản API đó không hỗ trợReplicaSet.speccủa ReplicaSet bắt buộc phải có thêm fieldselector— đây chính là khác biệt lớn nhất so với Replication Controller (chi tiết ở phần tiếp theo).
6. Selector trong ReplicaSet, và 2 cách Scale
selector giúp ReplicaSet xác định Pod nào thuộc quyền quản lý của nó. Tại sao cần khai báo rõ việc này, trong khi template đã mô tả sẵn Pod cần tạo? Vì ReplicaSet không chỉ quản lý Pod do chính nó tạo ra — nó còn có thể "nhận nuôi" cả những Pod đã tồn tại từ trước, miễn Pod đó có label khớp với selector. Đây là điểm khác biệt lớn với Replication Controller: selector không bắt buộc ở Replication Controller (nếu bỏ trống, nó tự hiểu là dùng đúng label khai trong pod template) — còn ở ReplicaSet, user bắt buộc phải tự khai selector, dưới dạng matchLabels. matchLabels đơn giản là khớp các label đã khai với label trên Pod.
Ví dụ tình huống cần đến khả năng "nhận nuôi" này: giả sử đã có sẵn 3 Pod frontend đang chạy (không qua ReplicaSet), giờ muốn tạo 1 ReplicaSet để đảm bảo luôn có tối thiểu 3 Pod đó chạy — ReplicaSet với selector khớp đúng label của 3 Pod sẽ nhận quản lý luôn 3 Pod có sẵn, không tạo thêm Pod mới nào (vì đã đủ số replicas yêu cầu). Vậy trường hợp này còn cần khai template không, nếu ReplicaSet không tạo Pod mới ngay lúc deploy? Có, vẫn cần — vì nếu sau này 1 trong 3 Pod chết, ReplicaSet phải biết dùng template nào để tạo Pod thay thế.
Scale ReplicaSet — có 2 cách:
- Sửa giá trị
replicastrong file định nghĩa rồi chạykubectl replace -f <file>để áp dụng lại (replacethay thế toàn bộ object — trong thực tế,kubectl apply -f <file>thường được khuyến nghị hơn cho việc cập nhật, vì chỉ merge đúng phần thay đổi thay vì ghi đè toàn bộ). Lưu ý: nếu scale bằng cách 2 (bên dưới) thay vì sửa file, giá trịreplicasghi trong file sẽ không tự cập nhật theo — file vẫn giữ nguyên số cũ dù cluster đã chạy với số replica mới. - Dùng thẳng
kubectl scale, truyền số replica mới qua tham số--replicas, không cần sửa file — có thể trỏ tới file định nghĩa hoặc trỏ trực tiếp theo dạng<type>/<name>. Kubernetes còn hỗ trợ tùy chọn tự động scale theo tải hệ thống, nhưng đây là chủ đề nâng cao, sẽ nói ở phần sau.
Tổng hợp lệnh:
kubectl create -f <file>— tạo ReplicaSet hay bất kỳ object nào khác tùy nội dung file, luôn cần-fđể chỉ định file input.kubectl get replicaset— xem danh sách ReplicaSet đã tạo.kubectl delete replicaset <name>— xóa ReplicaSet (mặc định xóa luôn các Pod nó đang quản lý, xem lưu ý bên dưới).kubectl replace— cập nhật ReplicaSet theo file đã sửa.kubectl scale— scale trực tiếp từ command line, không cần sửa file.
⚠️ Lưu ý khi xóa ReplicaSet:
kubectl delete replicaset <name>mặc định xóa luôn toàn bộ Pod mà ReplicaSet đang sở hữu — kể cả Pod nó chỉ "nhận nuôi" chứ không tự tạo ra, vì quyền sở hữu được tính theoownerReferencehiện tại trên Pod, không phải ai là người tạo ra Pod ban đầu. Muốn xóa ReplicaSet mà vẫn giữ lại các Pod, dùngkubectl delete replicaset <name> --cascade=orphan.
7. Demo: Tạo và quản lý ReplicaSet bằng VS Code
Demo này tạo ReplicaSet dựa trên chính file định nghĩa Pod đã tạo ở phần Pods trước đó. Thư mục pods (trong project kubernetes-for-beginners) đã có sẵn 2 file từ demo trước — pod.yaml và nginx.yaml. Giờ tạo thêm 1 thư mục mới tên replicasets, bên trong tạo file replicaset.yaml.
Khai báo root property:
apiVersion: apps/v1(đúng như đã học ở phần lý thuyết).kind: ReplicaSet— dùng gợi ý autocomplete của VS Code YAML extension.metadata—name: myapp-replicaset, và gắn thêm 1 label riêng cho ReplicaSet:app: my-app(label này không cần khớp với label trên Pod — chỉ là label riêng của bản thân object ReplicaSet).
VS Code YAML extension đủ thông minh để nhận ra kind: ReplicaSet cần có field selector, nên tự tạo sẵn khung selector — chỉ cần điền vào. Extension đưa 2 lựa chọn: matchExpressions hoặc matchLabels; demo chọn matchLabels.
Ban đầu, label dùng cho selector được copy thẳng từ file nginx.yaml đã tạo trước đó (label env: production). Nhưng có 1 nguyên tắc bắt buộc: label trên Pod template và label khai trong selector phải khớp nhau y hệt — nếu đổi 1 bên (ví dụ đổi sang app: myapp) thì bên còn lại cũng phải đổi theo, nếu không ReplicaSet sẽ không nhận diện đúng Pod của nó. Demo chuẩn hóa lại cả 2 về cùng app: myapp, tách biệt với label app: my-app riêng ở metadata cấp cao nhất của ReplicaSet (label này, như đã nói, không cần khớp với ai).
spec.template được lấy bằng cách copy nguyên khối metadata + spec từ file Pod definition đã tạo trước đó, dán vào bên trong template. Dán xong, indentation bị lệch hết — cách sửa thủ công: chọn toàn bộ đoạn vừa dán (trừ dòng đầu tiên), nhấn Tab 2 lần cho tới khi khớp đúng cấp. Extension đang dùng không tự động sửa indentation khi dán (có 1 extension khác tên đại loại "Paste and Indent" làm được việc này, nhưng demo không dùng).
Sau khi hoàn tất các bước trên, file replicaset.yaml có nội dung như sau:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-replicaset
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: nginx
image: nginx
Tạo và kiểm tra ReplicaSet.
cat replicaset.yaml # xem lại nội dung file trước khi tạo, xác nhận đúng
kubectl create -f replicaset.yaml
# Output minh họa
replicaset.apps/myapp-replicaset created
kubectl get replicaset
# Output minh họa
NAME DESIRED CURRENT READY AGE
myapp-replicaset 3 3 3 8s
Đủ 3 desired / 3 current / 3 ready — ReplicaSet đã tạo đúng số Pod yêu cầu.
Chạy tiếp kubectl get pods để xem 3 Pod đã tạo — tên mỗi Pod đều bắt đầu bằng tên ReplicaSet (myapp-replicaset-...), giúp dễ nhận biết Pod nào do ReplicaSet nào tạo ra:
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
myapp-replicaset-4jm2x 1/1 Running 0 9s
myapp-replicaset-8nxx1 1/1 Running 0 9s
myapp-replicaset-qz7lp 1/1 Running 0 9s
Test tự phục hồi (self-healing). Copy tên 1 Pod bất kỳ (ví dụ tên kết thúc bằng 8nxx1), xóa nó:
kubectl delete pod myapp-replicaset-8nxx1
# Output minh họa
pod "myapp-replicaset-8nxx1" deleted
Chờ vài giây để Pod terminate hẳn, rồi kubectl get pods lại:
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
myapp-replicaset-4jm2x 1/1 Running 0 45s
myapp-replicaset-qz7lp 1/1 Running 0 45s
myapp-replicaset-r5t9k 1/1 Running 0 15s
Vẫn thấy đúng 3 Pod đang chạy: Pod cũ (...8nxx1) đã biến mất, thay bằng 1 Pod mới (...r5t9k, tạo được khoảng 15 giây trước đó). Đây chính là cách ReplicaSet đảm bảo luôn đủ số Pod cấu hình.
Xem chi tiết hơn bằng kubectl describe replicaset myapp-replicaset:
kubectl describe replicaset myapp-replicaset
# Output minh họa (rút gọn)
Name: myapp-replicaset
Namespace: default
Selector: app=myapp
Labels: app=my-app
Replicas: 3 current / 3 desired
Pod Template:
Labels: app=myapp
Containers:
nginx:
Image: nginx
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 60s replicaset-controller Created pod: myapp-replicaset-4jm2x
Normal SuccessfulCreate 60s replicaset-controller Created pod: myapp-replicaset-8nxx1
Normal SuccessfulCreate 60s replicaset-controller Created pod: myapp-replicaset-qz7lp
Normal SuccessfulCreate 15s replicaset-controller Created pod: myapp-replicaset-r5t9k
Output cho thấy:
- Số replica mong muốn (
3). - Tên,
selector, và label của ReplicaSet. - Các label mà
selectorđang dùng để khớp Pod. - Định nghĩa container NGINX.
- Mục Events: log lại toàn bộ lịch sử — lúc đầu tạo đủ 3 replica, sau đó 1 Pod bị xóa, rồi ReplicaSet tự tạo thêm 1 Pod mới (
...r5t9k) để duy trì đúng số lượng.
Test giới hạn số replica. Thử tạo thêm 1 Pod mới (nginx-2, khai trong nginx.yaml, đã sửa label để khớp app: myapp — trùng với selector của ReplicaSet) nhưng tạo trực tiếp, không thông qua ReplicaSet:
kubectl get pods # xác nhận đang có đúng 3 Pod do ReplicaSet tạo
kubectl create -f nginx.yaml # tạo thêm Pod nginx-2, label trùng selector
# Output minh họa
pod/nginx-2 created
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
myapp-replicaset-4jm2x 1/1 Running 0 2m
myapp-replicaset-qz7lp 1/1 Running 0 2m
myapp-replicaset-r5t9k 1/1 Running 0 90s
nginx-2 0/1 Terminating 0 2s
Pod nginx-2 đã ở trạng thái Terminating gần như ngay lập tức. ReplicaSet phát hiện Pod này khớp selector của nó nhưng làm vượt quá số replicas đã cấu hình, nên chủ động xóa Pod thừa này đi — xác nhận lại bằng kubectl describe replicaset, mục Events ở cuối sẽ ghi rõ:
kubectl describe replicaset myapp-replicaset
# Output minh họa (trích Events)
Normal SuccessfulDelete 2s replicaset-controller Deleted pod: nginx-2
Scale ReplicaSet lên (qua kubectl edit). Giả sử muốn tăng từ 3 lên 4 replica:
kubectl edit replicaset myapp-replicaset
Lệnh này mở ra 1 file cấu hình tạm (không phải file replicaset.yaml gốc) trong Vim — Kubernetes tự sinh file này trong bộ nhớ để cho phép sửa trực tiếp cấu hình 1 object đang chạy, nên sẽ thấy nhiều field khác ngoài những gì đã tự tay khai báo. Mọi thay đổi lưu trong file này áp dụng thẳng vào cluster ngay khi save — cần cẩn trọng khi sửa. Cuộn tới mục spec, đổi replicas từ 3 thành 4, lưu và thoát khỏi editor.
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
myapp-replicaset-4jm2x 1/1 Running 0 3m
myapp-replicaset-qz7lp 1/1 Running 0 3m
myapp-replicaset-r5t9k 1/1 Running 0 2m
myapp-replicaset-w2fd8 1/1 Running 0 6s
Có thêm 1 Pod mới (...w2fd8, tạo khoảng 6 giây trước đó).
Scale ReplicaSet xuống (qua kubectl scale). Cách nhanh hơn, không cần sửa file — lưu ý cú pháp có 2 dấu gạch ngang trước --replicas:
kubectl scale replicaset myapp-replicaset --replicas=2
# Output minh họa
replicaset.apps/myapp-replicaset scaled
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
myapp-replicaset-4jm2x 1/1 Running 0 4m
myapp-replicaset-qz7lp 1/1 Running 0 4m
ReplicaSet đã scale xuống còn 2 Pod, terminate bớt 2 Pod dư.
Deployments
8. Deployment là gì? Vì sao cần Deployment?
Tạm gác lại Pod và ReplicaSet, thử nhìn bài toán deploy ứng dụng trong môi trường production theo góc khác: giả sử có 1 web server cần deploy — không phải 1 mà nhiều instance chạy song song, vì lý do hiển nhiên. Ngoài ra, mỗi khi có phiên bản build mới xuất hiện trên Docker registry, cần nâng cấp (upgrade) các Docker instance đó 1 cách liền mạch (seamlessly). Nhưng khi nâng cấp, không muốn nâng cấp tất cả instance cùng lúc — làm vậy có thể ảnh hưởng tới user đang truy cập ứng dụng. Thay vào đó, nên nâng cấp lần lượt từng cái một — kiểu nâng cấp này gọi là rolling update.
Giả sử 1 lần nâng cấp nào đó gây ra lỗi ngoài ý muốn, và cần undo thay đổi vừa rồi — cần khả năng rollback về thay đổi gần nhất.
Cuối cùng, giả sử cần thực hiện nhiều thay đổi cùng lúc trong môi trường — như nâng cấp version web server bên dưới, đồng thời scale môi trường, và chỉnh resource allocation... — không muốn mỗi thay đổi được áp dụng ngay lập tức ngay sau khi lệnh chạy. Thay vào đó, muốn pause môi trường lại, thực hiện các thay đổi, rồi resume để mọi thay đổi được rollout cùng lúc.
Tất cả các khả năng trên đều có sẵn với Kubernetes Deployment.
Nhìn lại hệ thống phân cấp đã học: Pod deploy 1 instance đơn lẻ của ứng dụng. Nhiều Pod như vậy được deploy bằng Replication Controller hoặc ReplicaSet. Và Deployment là 1 Kubernetes object đứng cao hơn trong hệ thống phân cấp — cung cấp khả năng nâng cấp instance bên dưới 1 cách liền mạch bằng rolling update, undo thay đổi, và pause/resume thay đổi khi cần.
💡 Hình dung như thế này: nếu ReplicaSet giống người quản lý ca trực chỉ lo đúng 1 việc — đảm bảo quân số luôn đủ — thì Deployment giống cấp quản lý cao hơn, phụ trách cả "chiến dịch thay quân" mỗi khi có đợt nâng cấp. Không tự tay đuổi hết nhân viên cũ rồi tuyển hết nhân viên mới cùng lúc (dễ để ca trực trống người), Deployment điều phối việc thay dần từng người một, ca trực không bao giờ bị hở. Và nếu đợt thay quân mới hóa ra có vấn đề, chỉ cần yêu cầu Deployment "quay lại đội hình lần trước" — nó biết chính xác đội hình nào vừa hoạt động tốt để khôi phục.
9. Cấu trúc file Deployment và Demo: Tạo Deployment
Cách tạo Deployment cũng giống các object trước: tạo file định nghĩa Deployment trước. Nội dung file định nghĩa Deployment gần như giống hệt file định nghĩa ReplicaSet, chỉ khác ở kind — giờ là Deployment thay vì ReplicaSet:
apiVersion—apps/v1.kind—Deployment.metadata— cónamevàlabels.spec— cótemplate,replicas,selector.templatechứa nguyên Pod definition bên trong — đúng nguyên lý đã học ở ReplicaSet.
Sau khi file sẵn sàng, chạy kubectl create kèm file định nghĩa Deployment làm input. Rồi chạy kubectl get deployments để xem Deployment vừa tạo — Deployment tự động tạo ra 1 ReplicaSet. Chạy kubectl get replicaset sẽ thấy 1 ReplicaSet mới mang tên theo Deployment. ReplicaSet đó lại tạo ra Pod — chạy kubectl get pods sẽ thấy các Pod mang tên theo cả Deployment lẫn ReplicaSet. Tới bước này, Deployment chưa khác biệt nhiều so với ReplicaSet thuần, ngoại trừ việc nó tạo thêm 1 Kubernetes object mới gọi là Deployment. Để xem toàn bộ object đã tạo trong cluster cùng lúc, dùng kubectl get all — sẽ thấy 1 Deployment, kèm 1 ReplicaSet, kèm các Pod được tạo từ ReplicaSet đó.
Demo: Tạo Deployment. Tạo 1 thư mục mới tên deployments trong project directory, bên trong tạo file deployment.yaml. Vì nội dung gần như giống hệt ReplicaSet, demo tái sử dụng luôn file replicaset.yaml đã tạo trước đó (mở Split Editor, deployment.yaml bên trái, replicaset.yaml bên phải để tham chiếu):
apiVersion: apps/v1— giống ReplicaSet.kind: Deployment.metadata—name: myapp-deployment, kèm vài label:app: frontend,tier: frontend, và có thể thêm 1 label nữa tênnginx.spec— copy nguyên khốispectừreplicaset.yamldán vào, đổinamethànhmyapp-deployment, và giảmreplicasxuống còn3.
Quay lại terminal, kiểm tra thư mục gốc project thấy thư mục deployments mới, rồi tạo Deployment bằng kubectl create kèm -f deployment.yaml:
kubectl create -f deployment.yaml
# Output minh họa
deployment.apps/myapp-deployment created
kubectl get deployments
# Output minh họa
NAME READY UP-TO-DATE AVAILABLE AGE
myapp-deployment 3/3 3 3 10s
1 Deployment đã tạo, đủ 3/3 Pod ready, 3 available.
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
myapp-deployment-6cd57d9d4f-4jm2x 1/1 Running 0 10s
myapp-deployment-6cd57d9d4f-8nxx1 1/1 Running 0 10s
myapp-deployment-6cd57d9d4f-qz7lp 1/1 Running 0 10s
3 Pod đang Running, tên vừa mang tiền tố Deployment (myapp-deployment), vừa mang hash của ReplicaSet trung gian (6cd57d9d4f).
kubectl describe deployment myapp-deployment
# Output minh họa (rút gọn)
Name: myapp-deployment
Namespace: default
Labels: app=frontend
tier=frontend
Selector: app=myapp
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
Pod Template:
Labels: app=myapp
Containers:
nginx:
Image: nginx
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 10s deployment-controller Scaled up replica set myapp-deployment-6cd57d9d4f to 3
selector giống hệt ReplicaSet trước đó (myapp), đủ 3 desired và 3 available. Mục Events chỉ có đúng 1 message từ Deployment Controller — ReplicaSet trung gian của Deployment này đã được scale lên 3.
kubectl get all
# Output minh họa
NAME READY STATUS RESTARTS AGE
pod/myapp-deployment-6cd57d9d4f-4jm2x 1/1 Running 0 10s
pod/myapp-deployment-6cd57d9d4f-8nxx1 1/1 Running 0 10s
pod/myapp-deployment-6cd57d9d4f-qz7lp 1/1 Running 0 10s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/myapp-deployment 3/3 3 3 10s
NAME DESIRED CURRENT READY AGE
replicaset.apps/myapp-deployment-6cd57d9d4f 3 3 3 10s
Đúng 3 tầng object đã học: 1 Deployment (vừa tạo), 1 ReplicaSet (tên bắt đầu bằng myapp-deployment-, do Deployment tạo ra), và 3 Pod (được tạo từ ReplicaSet đó).
10. Rollout, Revision và 2 chiến lược Deployment
Trước khi học cách upgrade ứng dụng, cần hiểu khái niệm rollout và versioning trong Deployment.
Khi tạo 1 Deployment lần đầu, nó kích hoạt 1 rollout. Rollout mới tạo ra 1 ReplicaSet mới, được ghi nhận là 1 deployment revision mới (Revision 1). Sau này, khi ứng dụng được upgrade — tức khi version container được update sang bản mới — 1 rollout mới lại được kích hoạt và 1 deployment revision mới được tạo, đặt tên Revision 2. Cơ chế này giúp theo dõi các thay đổi đã thực hiện trên Deployment, và cho phép rollback về version trước đó khi cần.
Xem trạng thái rollout bằng:
kubectl rollout status <deployment-name>
Xem revision và lịch sử rollout bằng:
kubectl rollout history <deployment-name>
2 chiến lược deployment. Ví dụ có 5 replica của 1 web app instance đang chạy. Có 2 cách upgrade lên version mới:
- Recreate — hủy hết 5 instance cũ trước, rồi mới tạo 5 instance mới của version mới. Vấn đề: trong khoảng thời gian sau khi instance cũ đã tắt và trước khi instance mới lên, ứng dụng bị down, user không truy cập được. Đây không phải chiến lược mặc định.
- RollingUpdate — không hủy hết Pod cùng lúc, mà hạ dần version cũ và nâng dần version mới. Ứng dụng không bao giờ down, quá trình upgrade diễn ra liền mạch (seamless). Nếu không chỉ định strategy nào khi tạo Deployment, Kubernetes mặc định dùng RollingUpdate.
Cách update Deployment. "Update" có thể là nhiều thứ khác nhau: đổi version Docker container, đổi label, đổi số replica... Vì đã có sẵn file định nghĩa Deployment, chỉ cần sửa file rồi chạy kubectl apply để áp dụng thay đổi — 1 rollout mới được kích hoạt, 1 revision mới của Deployment được tạo. Có 1 cách khác để làm cùng việc: dùng lệnh kubectl set image để update image của ứng dụng — nhưng cần nhớ, làm theo cách này khiến file định nghĩa Deployment có config khác với thực tế trên cluster, nên phải cẩn trọng khi sau này dùng lại chính file đó để thực hiện thay đổi tiếp theo.
Khác biệt Recreate vs RollingUpdate khi xem chi tiết. Chạy kubectl describe deployment để xem thông tin chi tiết. Với chiến lược Recreate, mục Events cho thấy ReplicaSet cũ bị scale xuống 0 trước, sau đó ReplicaSet mới mới được scale lên (ví dụ lên 5). Với chiến lược RollingUpdate, ReplicaSet cũ bị scale xuống từng Pod một, đồng thời ReplicaSet mới được scale lên từng Pod một.
Cơ chế bên dưới khi upgrade. Khi tạo 1 Deployment mới (ví dụ deploy 5 replica), nó trước tiên tự tạo 1 ReplicaSet, ReplicaSet đó lại tạo ra số Pod cần thiết để đủ số replica yêu cầu. Khi upgrade ứng dụng, Deployment object tạo 1 ReplicaSet mới bên dưới và bắt đầu deploy container vào đó, đồng thời hạ dần Pod ở ReplicaSet cũ theo chiến lược rolling update. Liệt kê ReplicaSet bằng kubectl get replicasets sẽ thấy rõ: ReplicaSet cũ còn 0 Pod, ReplicaSet mới có đủ số Pod (ví dụ 5).
Rollback. Giả sử sau khi upgrade phát hiện có vấn đề với version build mới, muốn rollback lại update. Kubernetes Deployment cho phép rollback về revision trước đó bằng:
kubectl rollout undo <deployment-name>
Deployment sẽ hủy các Pod ở ReplicaSet mới và đưa Pod ở ReplicaSet cũ chạy trở lại — ứng dụng quay về đúng format cũ. So sánh output kubectl get replicasets trước và sau rollback sẽ thấy sự đảo ngược: trước rollback, ReplicaSet đầu tiên có 0 Pod và ReplicaSet mới có 5 Pod; sau rollback, kết quả đảo ngược lại.
Tổng hợp lệnh:
kubectl create— tạo Deployment.kubectl get deployments— liệt kê Deployment.kubectl applyvàkubectl set image— cập nhật Deployment.kubectl rollout status— xem trạng thái rollout.kubectl rollout undo— rollback 1 thao tác Deployment.
11. Demo: Update và Rollback Deployment
Demo này thực hiện trên Kubernetes master node. Tên Deployment dùng xuyên suốt demo là my-app-deployment.
Tạo Deployment lần đầu. Xác nhận chưa có gì tồn tại (ngoài Kubernetes service mặc định):
kubectl get all
# Output minh họa
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 9m
Tạo Deployment bằng lệnh kubectl create deployment, rồi ngay sau đó chạy kubectl rollout status để xem trạng thái rollout:
kubectl create deployment my-app-deployment --image=nginx
# Output minh họa
deployment.apps/my-app-deployment created
kubectl rollout status deployment/my-app-deployment
# Output minh họa
Waiting for deployment "my-app-deployment" rollout to finish: 2 of 6 updated replicas are available...
Waiting for deployment "my-app-deployment" rollout to finish: 3 of 6 updated replicas are available...
Waiting for deployment "my-app-deployment" rollout to finish: 4 of 6 updated replicas are available...
Waiting for deployment "my-app-deployment" rollout to finish: 5 of 6 updated replicas are available...
deployment "my-app-deployment" successfully rolled out
Pod được deploy lần lượt từng cái một — đúng nhịp "hai trên sáu, ba trên sáu, bốn trên sáu..." — cho tới khi đủ sáu trên sáu thì rollout được coi là thành công (demo dùng 6 replica).
Chạy kubectl rollout history (sửa lệnh trước đó từ status sang history):
kubectl rollout history deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment
REVISION CHANGE-CAUSE
1 <none>
Đã tạo 1 revision (Revision 1), nhưng cột CHANGE-CAUSE ghi <none>, vì chưa yêu cầu Kubernetes ghi lại (record) thay đổi vừa thực hiện. Trường hợp này không quan trọng lắm vì đây là revision đầu tiên, tạo ngay khi khởi tạo Deployment.
Xóa và tạo lại có ghi change-cause. Xóa Deployment — lệnh này xóa cả Deployment, ReplicaSet liên quan, và các Pod được tạo bởi Deployment:
kubectl delete deployment my-app-deployment
# Output minh họa
deployment.apps "my-app-deployment" deleted
kubectl get all
# Output minh họa
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11m
Mọi thứ đã terminate sạch, chỉ còn lại Kubernetes service mặc định. Tạo lại Deployment bằng đúng lệnh như cũ, nhưng lần này thêm flag --record (bản kubectl gần đây đã deprecate flag này — xem ghi chú fact-check ở mục Nguồn tham khảo):
kubectl create deployment my-app-deployment --image=nginx --record
# Output minh họa
Flag --record has been deprecated, --record will be removed in the future release
deployment.apps/my-app-deployment created
kubectl rollout status deployment/my-app-deployment
# Output minh họa
Waiting for deployment "my-app-deployment" rollout to finish: 2 of 6 updated replicas are available...
Waiting for deployment "my-app-deployment" rollout to finish: 3 of 6 updated replicas are available...
Waiting for deployment "my-app-deployment" rollout to finish: 4 of 6 updated replicas are available...
Waiting for deployment "my-app-deployment" rollout to finish: 5 of 6 updated replicas are available...
deployment "my-app-deployment" successfully rolled out
Rollout thành công, đủ 6/6 Pod.
kubectl rollout history deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment
REVISION CHANGE-CAUSE
1 kubectl create deployment my-app-deployment --image=nginx --record=true
Vẫn là Revision 1, nhưng lần này cột CHANGE-CAUSE đã ghi đúng lệnh dùng để tạo Deployment (nhờ có flag --record).
Upgrade bằng kubectl apply. Image NGINX ban đầu không chỉ định tag/version cụ thể, nên mặc định lấy latest — tại thời điểm demo, latest tương ứng version 1.13.9. Để mô phỏng 1 lần upgrade (hoặc downgrade), đổi sang version khác — cụ thể là 1.12, tức bản cũ hơn 1.13.9. Sau khi sửa image trong file định nghĩa Deployment, áp dụng thay đổi:
kubectl apply -f <deployment-definition-file> --record
# Output
deployment my-app-deployment configured
kubectl rollout status deployment/my-app-deployment
# Output minh họa
Waiting for deployment "my-app-deployment" rollout to finish: 1 out of 6 new replicas have been updated...
Waiting for deployment "my-app-deployment" rollout to finish: 3 out of 6 new replicas have been updated...
Waiting for deployment "my-app-deployment" rollout to finish: 5 out of 6 new replicas have been updated...
deployment "my-app-deployment" successfully rolled out
Vì image đã đổi, Kubernetes phải update image trên toàn bộ ReplicaSet — rollout thành công, đủ 6 node up to date. Chú ý câu chữ khác với lúc tạo mới ở trên: đây là update trên Deployment đã tồn tại nên kubectl dùng cụm "new replicas have been updated" thay vì "updated replicas are available".
kubectl describe deployment my-app-deployment
# Output minh họa (rút gọn)
Image: nginx:1.12
...
OldReplicaSets: <none>
NewReplicaSet: my-app-deployment-6f8d9c7b5 (6/6 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 60s deployment-controller Scaled up replica set my-app-deployment-6f8d9c7b5 from 0 to 1
Normal ScalingReplicaSet 58s deployment-controller Scaled down replica set my-app-deployment-7d9f8c6b57 from 6 to 5
Normal ScalingReplicaSet 58s deployment-controller Scaled up replica set my-app-deployment-6f8d9c7b5 from 1 to 2
... (lặp lại tương tự cho tới khi ReplicaSet cũ về 0, ReplicaSet mới đủ 6)
Image chi tiết đã đổi thành nginx:1.12; ở mục Events có 2 ReplicaSet được nhắc tới: ReplicaSet cũ (...7d9f8c6b57, image nginx gốc) bị scale xuống từng Pod một, đồng thời ReplicaSet mới (...6f8d9c7b5, image nginx:1.12) được scale lên từng Pod một.
kubectl rollout history deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment
REVISION CHANGE-CAUSE
1 kubectl create deployment my-app-deployment --image=nginx --record=true
2 kubectl apply --filename=deployment-definition.yaml --record=true
Revision mới, Revision 2, được tạo bởi đúng lệnh kubectl apply vừa chạy để upgrade image.
Upgrade bằng kubectl set image. Đây là cách khác để làm cùng việc: chạy lệnh set image, chỉ định version mới theo định dạng tên=giá trị — tên ở đây là tên container nginx, giá trị mới là 1.12-perl (1 version khác của image NGINX):
kubectl set image deployment/my-app-deployment nginx=nginx:1.12-perl --record
# Output minh họa
Flag --record has been deprecated, --record will be removed in the future release
deployment.apps/my-app-deployment image updated
kubectl rollout status deployment/my-app-deployment
# Output minh họa
Waiting for deployment "my-app-deployment" rollout to finish: 1 out of 6 new replicas have been updated...
Waiting for deployment "my-app-deployment" rollout to finish: 3 out of 6 new replicas have been updated...
Waiting for deployment "my-app-deployment" rollout to finish: 5 out of 6 new replicas have been updated...
deployment "my-app-deployment" successfully rolled out
kubectl rollout history deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment
REVISION CHANGE-CAUSE
1 kubectl create deployment my-app-deployment --image=nginx --record=true
2 kubectl apply --filename=deployment-definition.yaml --record=true
3 kubectl set image deployment/my-app-deployment nginx=nginx:1.12-perl --record=true
Revision mới, Revision 3, được tạo bởi lệnh kubectl set image vừa chạy. Tới đây, có 3 revision: Revision 1 = image nginx gốc (không tag, thực chất là 1.13.9), Revision 2 = tạo khi đổi sang nginx:1.12, Revision 3 = tạo khi đổi sang nginx:1.12-perl.
kubectl describe deployment my-app-deployment
# Output minh họa (rút gọn)
Image: nginx:1.12-perl
Xác nhận image container hiện tại.
Rollback về version trước. Giả sử phát hiện version 1.12-perl gây lỗi, user report vấn đề, cần rollback lại thay đổi gần nhất:
kubectl rollout undo deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment rolled back
Lệnh này cũng kích hoạt 1 rollout, và rollout thành công.
kubectl rollout history deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment
REVISION CHANGE-CAUSE
1 kubectl create deployment my-app-deployment --image=nginx --record=true
3 kubectl set image deployment/my-app-deployment nginx=nginx:1.12-perl --record=true
4 kubectl apply --filename=deployment-definition.yaml --record=true
Có revision mới — Revision 4 — nhưng Revision 2 đã biến mất khỏi danh sách. Lý do: khi rollback, Kubernetes quay lại đúng Revision 2 (vì đang ở Revision 3), và tạo ra Revision 4 mới áp dụng đúng lệnh + đúng definition file đã dùng ở Revision 2 trước đó — Kubernetes tự lưu lại nội bộ thông tin này.
kubectl describe deployment my-app-deployment
# Output minh họa (rút gọn)
Image: nginx:1.12
NewReplicaSet: my-app-deployment-6f8d9c7b5 (6/6 replicas created)
Xác nhận image đã quay về 1.12 (đúng như Revision 2). Chú ý tên ReplicaSet ...6f8d9c7b5 trùng với lúc ở Revision 2: khi rollback trùng khớp 1 config đã từng chạy trước đó và ReplicaSet cũ chưa bị dọn dẹp, Kubernetes tái sử dụng lại đúng ReplicaSet đó thay vì tạo mới — đây cũng là lý do rollback thường nhanh hơn hẳn 1 lần upgrade bình thường, vì không cần pull lại image.
Mô phỏng lỗi image không tồn tại. Tạo tình huống lỗi bằng cách đặt tên 1 image NGINX không có thật — bịa ra tên 1.5err. Cố ý biết image này không tồn tại, mục đích để mô phỏng lỗi deployment. Cập nhật file định nghĩa Deployment và áp dụng:
kubectl apply -f <deployment-definition-file> --record
# Output minh họa
Flag --record has been deprecated, --record will be removed in the future release
deployment.apps/my-app-deployment configured
Chạy kubectl rollout status để theo dõi:
kubectl rollout status deployment/my-app-deployment
# Output minh họa
Waiting for deployment "my-app-deployment" rollout to finish: 1 out of 6 new replicas have been updated...
Waiting for deployment "my-app-deployment" rollout to finish: 2 out of 6 new replicas have been updated...
Waiting for deployment "my-app-deployment" rollout to finish: 3 out of 6 new replicas have been updated...
Rollout bị kẹt ở đây, không tiến thêm được nữa — dừng lại đúng ở mốc 3 trên 6 replica mới đã update. Ở backend, Kubernetes đang cố hạ Pod cũ và nâng Pod mới, nhưng bị kẹt ở 3/6.
kubectl describe deployment my-app-deployment
# Output minh họa (rút gọn)
Replicas: 6 desired | 3 updated | 8 total | 5 available | 3 unavailable
Deployment ở trạng thái khác thường: desired = 6 (đúng như khai báo), nhưng total (một số phiên bản kubectl cũ hơn gọi cột này là "current") = 8, up-to-date (updated) chỉ = 3. Nếu desired là 6, total lẽ ra phải là 6 và updated cũng phải là 6.
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
my-app-deployment-6f8d9c7b5-a1b2c 1/1 Running 0 25m
my-app-deployment-6f8d9c7b5-d3e4f 1/1 Running 0 25m
my-app-deployment-6f8d9c7b5-g5h6i 1/1 Running 0 25m
my-app-deployment-6f8d9c7b5-j7k8l 1/1 Running 0 25m
my-app-deployment-6f8d9c7b5-m9n0o 1/1 Running 0 25m
my-app-deployment-b7c8d9e6f-p1q2r 0/1 ImagePullBackOff 0 2m
my-app-deployment-b7c8d9e6f-s3t4u 0/1 ImagePullBackOff 0 2m
my-app-deployment-b7c8d9e6f-v5w6x 0/1 ImagePullBackOff 0 2m
5 Pod của version cũ (...6f8d9c7b5, đang hoạt động tốt) vẫn Running, chỉ có 3 Pod của version mới (...b7c8d9e6f, không tồn tại) được tạo, ở trạng thái ImagePullBackOff — nghĩa là Deployment không pull được image từ Docker Hub.
Giải thích cơ chế: đây vẫn được coi là 1 upgrade — Deployment object cố terminate 1 instance từ version cũ đang chạy tốt (đưa tổng số Pod cũ đang chạy xuống còn 5), đồng thời cố tạo 3 Pod của version mới. Nhưng vì version mới không hợp lệ, và không thể deploy bất kỳ Pod nào trong 3 Pod đó, Kubernetes chủ động dừng việc tiếp tục upgrade — nó chủ động không terminate hết ReplicaSet cũ, vì biết nếu làm vậy và cố deploy hết Pod mới thì user sẽ bị ảnh hưởng do image mới không tồn tại. Kubernetes dừng lại và chờ image mới khả dụng.
kubectl rollout history deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment
REVISION CHANGE-CAUSE
1 kubectl create deployment my-app-deployment --image=nginx --record=true
3 kubectl set image deployment/my-app-deployment nginx=nginx:1.12-perl --record=true
4 kubectl apply --filename=deployment-definition.yaml --record=true
5 kubectl apply --filename=deployment-definition.yaml --record=true
Có revision mới được tạo, Revision 5, tạo bởi chính lệnh lỗi vừa chạy. Vì biết rõ image bị lỗi, undo thay đổi gần nhất bằng:
kubectl rollout undo deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment rolled back
kubectl rollout history deployment/my-app-deployment
# Output minh họa
deployment.apps/my-app-deployment
REVISION CHANGE-CAUSE
1 kubectl create deployment my-app-deployment --image=nginx --record=true
3 kubectl set image deployment/my-app-deployment nginx=nginx:1.12-perl --record=true
5 kubectl apply --filename=deployment-definition.yaml --record=true
6 kubectl apply --filename=deployment-definition.yaml --record=true
Có revision mới, Revision 6, và nó quay về Revision 4 (Revision 4 giờ đã biến mất khỏi danh sách, tương tự cơ chế đã thấy ở lần rollback trước).
kubectl describe deployment my-app-deployment
# Output minh họa (rút gọn)
Image: nginx:1.12
kubectl get pods
# Output minh họa
NAME READY STATUS RESTARTS AGE
my-app-deployment-6f8d9c7b5-a1b2c 1/1 Running 0 30m
my-app-deployment-6f8d9c7b5-d3e4f 1/1 Running 0 30m
my-app-deployment-6f8d9c7b5-g5h6i 1/1 Running 0 30m
my-app-deployment-6f8d9c7b5-j7k8l 1/1 Running 0 30m
my-app-deployment-6f8d9c7b5-m9n0o 1/1 Running 0 30m
my-app-deployment-6f8d9c7b5-w1x2y 1/1 Running 0 45s
Image đã quay về 1.12, và đủ 6 Pod, tất cả đều ở trạng thái Running.
Nguồn tham khảo
Nguồn gốc: Khóa "Kubernetes for the Absolute Beginners - Hands-on Tutorial" — module "Kubernetes Concepts - Pods, ReplicaSets, Deployments", bài "Replication Controllers" và "Demo - ReplicaSets"; bài "Deployments", "Demo - Deployments", "Rolling Updates and Rollbacks" và "Demo - Rolling updates and rollbacks in Deployments", nền tảng KodeKloud.
Đính chính/cập nhật (fact-check):
- Group version
apps/v1beta1,apps/v1beta2,extensions/v1beta1đã bị xóa khỏi Kubernetes từ v1.16, hiện dùngapps/v1— 2026, Kubernetes Deprecated API Migration Guide, Kubernetes blog: API deprecations in 1.16 - Khuyến nghị chính thức hiện nay: nên dùng Deployment (tự quản lý ReplicaSet bên dưới) thay vì tạo ReplicaSet trực tiếp, trừ khi cần tùy biến cách update — 2026, Kubernetes ReplicationController docs
kubectl apply -flà lựa chọn mặc định được khuyến nghị để cập nhật object từ file (merge thay đổi);kubectl replace -fchỉ cần khi phải thay thế toàn bộ object hoặc đổi field immutable — 2026, tổng hợp từ tài liệu tham khảo kubectl apply/replacekubectl delete replicaset <name>mặc định xóa cả các Pod đang được ReplicaSet sở hữu (kể cả Pod được nhận nuôi); dùng--cascade=orphanđể chỉ xóa ReplicaSet, giữ lại Pod — 2026, Kubernetes ReplicaSet docs- Flag
--recordtrênkubectl create/kubectl apply(dùng để tự ghi change-cause) đã bị deprecated từ Kubernetes v1.22 và đã bị gỡ bỏ khỏi các bản kubectl mới; muốn ghi change-cause, dùngkubectl annotate deployment <name> kubernetes.io/change-cause="..." --overwritethay thế — 2026