Kubernetes: Zero to Hero

Sê-ri 60 phần về Kubernetes, mỗi phần một phép đo chạy được trên kind hoặc k3d.

60/60 phần đã đăng DevOps
1 Docker chạy 3 container trong nửa giây; Kubernetes mất 10,6 giây và ngốn thêm 743 MB trước khi làm gì — đây là thứ 743 MB đó mua được Tôi dựng đúng một ứng dụng nginx ba bản bằng Docker thuần rồi bằng Kubernetes, và đo cả hai — kể cả cái giá. Docker thắng mọi chỉ số tốc độ và bộ nhớ. Rồi tôi giết một thứ đang chạy, và mọi thứ đảo chiều: Docker chạy thứ bạn bảo nó chạy, Kubernetes giữ thứ bạn khai là phải có. 30/08/2026 · 8 phút đọc 2 Tôi giết một node Kubernetes, ba pod chết theo — nhưng kubectl vẫn thản nhiên báo đủ 6 Running suốt gần mười phút; 'pod ma' và vì sao đếm Running là dối kind dựng cụm 3 node trong 30 giây và 791 MB. Nhưng phép đo đáng nhớ nhất là khi tôi docker stop một worker: gần mười phút trôi qua, không pod nào được chuyển đi, và kubectl get pods vẫn báo đủ sáu Running — trong khi ba bản đã ngừng phục vụ từ giây thứ 61. Đây là lý do bảng theo dõi dựa trên đếm pod sẽ lừa bạn. 30/08/2026 · 9 phút đọc 3 Tôi tắt cả apiserver lẫn etcd của một cụm Kubernetes — bốn container nginx vẫn phục vụ bình thường; control plane chết không giết ứng dụng, nó giết khả năng tự chữa Sơ đồ kiến trúc Kubernetes thì ở đâu cũng có. Tôi làm khác: tắt từng thành phần một trên cụm đang chạy rồi đo cái gì hỏng. Kết quả đáng nhớ nhất — mất control plane, mọi thứ vẫn chạy y như thường, cho tới đúng lần tiếp theo có một pod chết mà không ai thay nó. Và apiserver nặng gần gấp năm lần etcd. 30/08/2026 · 8 phút đọc 4 Hai container khác nhau trong một pod gọi nhau qua localhost — và 'kubectl delete pod' mất đúng bằng grace period dù ứng dụng tắt ngay lập tức hay bướng bỉnh không chịu chết Câu 'pod là đơn vị nhỏ nhất, không phải container' nghe như chi tiết thuật ngữ. Đo thử thì ra hai điều thú vị: hai container trong một pod chung một IP và nói chuyện qua localhost, và thời gian xoá pod hoàn toàn không phụ thuộc việc ứng dụng tắt nhanh hay chậm — nó chỉ đo đúng terminationGracePeriodSeconds. 30/08/2026 · 8 phút đọc 5 Toàn bộ trạng thái một cụm Kubernetes nằm gọn trong 2,6 MB — và 173 trong ~450 khoá chỉ là event; mất cái tệp bé tí này là mất cả cụm etcd là nơi duy nhất Kubernetes lưu trạng thái, và nó nhỏ đến bất ngờ: 2,6 MB cho cả một cụm. Trần mỗi object là đúng 1.048.576 byte, áp cho mọi loại. Ghi vào etcd chỉ tốn 5 mili giây. Bài này mở nó ra xem bên trong, và vì sao bốn node etcd chịu lỗi không hơn gì ba. 30/08/2026 · 8 phút đọc 6 83% thời gian một lệnh kubectl chỉ là khởi động binary, không phải việc của cụm — và một cụm Kubernetes không ai đụng vào vẫn gọi API server ba lượt mỗi giây kube-apiserver là cửa duy nhất vào cụm, nặng 265 MB — gấp năm lần etcd. Nhưng phần việc thật của nó cho một lệnh kubectl chỉ 4–8 mili giây; 24 mili giây còn lại là binary khởi động. Bài này đo chi phí một lệnh, cơ chế watch thay cho hỏi liên tục, và chỗ nguy hiểm nhất: webhook admission nằm ngay giữa đường ghi. 30/08/2026 · 8 phút đọc 7 Bộ lập lịch Kubernetes xếp chỗ trong 50 mili giây — nhưng nó đặt chỗ theo tờ khai 'requests', không theo mức bạn dùng thật, và một pod ưu tiên cao sẵn sàng giết pod đang chạy để có chỗ Xếp một pod lên node chỉ tốn khoảng 50 mili giây. Nhưng bộ lập lịch cộng 'requests' chứ không nhìn CPU dùng thật — nên node có thể 'đầy' trong khi thật ra rỗng, và một pod PriorityClass cao sẽ đuổi pod đang chạy để lấy chỗ. Bài này đo cả ba hành vi đó. 30/08/2026 · 8 phút đọc 8 Cả một cụm Kubernetes chỉ là sổ sách trong etcd — kubelet là chỗ duy nhất sổ sách biến thành tiến trình thật, và toàn bộ độ trễ khởi động pod nằm ở một chỗ bạn không ngờ Ba mốc cuối của việc khởi động một pod xảy ra gọn trong cùng một giây — kubelet gần như không tốn thời gian. Chỗ đắt là kéo ảnh: 5,9 giây cho 4 MB, 15,7 giây cho 400 MB. Bài này đo đoạn đường từ 'bộ lập lịch ghi tên node' tới 'container đang chạy', và chỉ ra vì sao kubelet là thành phần duy nhất trong Kubernetes không phải là một pod. 30/08/2026 · 9 phút đọc 9 Quay lui một phiên bản Kubernetes chỉ mất 2,3 giây, nhưng node chết thì phải chờ bốn phút mới có bản thay thế — cùng một tầng ẩn giải thích cả hai Người ta viết Deployment và nghĩ nó quản pod. Nó không — nó quản ReplicaSet, và tầng ở giữa đó giải thích cả việc quay lui tức thì lẫn việc phản ứng chậm. Đổi ảnh thì ReplicaSet cũ không bị xoá, chỉ đặt mong muốn=0, nên quay lui chỉ là dịch số giữa hai ReplicaSet đã có sẵn (2,3 giây, không kéo ảnh). Node chết thì ReplicaSet chờ bốn phút vì pod ma vẫn báo Ready. Và trong bốn phút đó, kubectl get deployment vẫn hiện 4/4 — chỉ Endpoints nói thật. 30/08/2026 · 7 phút đọc 10 Cùng một lệnh đổi ảnh: có readinessProbe thì cả ba cấu hình đều 0 lỗi, bỏ nó đi thì 8% yêu cầu chết — thứ tạo ra 'không gián đoạn' không phải RollingUpdate Đo kỹ rolling update, đổi từng tham số để tìm thứ nào thật sự tạo ra 'không gián đoạn'. Có readinessProbe thì maxUnavailable 0, 1, hay 3 đều 0 lỗi — nhanh gấp sáu lần vẫn không mất yêu cầu nào, vì cấu hình quyết định TỐC ĐỘ chứ không quyết định AN TOÀN. Bỏ readinessProbe đi thì cùng lệnh đó mất 8% yêu cầu (Recreate 18%), và kubectl rollout status báo hoàn tất sau 2,6 giây trong khi container còn tám giây nữa mới nghe cổng. Thứ tạo ra không gián đoạn là readinessProbe, không phải RollingUpdate. 30/08/2026 · 7 phút đọc 11 Ba probe của Kubernetes nghe như cùng một việc 'kiểm tra sức khoẻ' — nhưng khi hỏng, một cái giết container, một cái chỉ ngừng gửi việc, một cái bảo hai cái kia chờ Ba probe trả lời ba câu hỏi khác nhau và Kubernetes phản ứng theo ba cách khác hẳn. livenessProbe hỏng thì container bị khởi động lại (và nếu lỗi không tự khỏi thì lặp mãi); readinessProbe hỏng thì pod bị gỡ khỏi Endpoints nhưng KHÔNG khởi động lại (RESTARTS=0, vẫn Running, chỉ 0/1); startupProbe thì tạm dừng cả hai cái kia cho ứng dụng khởi động lâu kịp lên. Đo cả ba, và vì sao dùng startupProbe đúng hơn hẳn việc nâng initialDelaySeconds — cái sau chữa lúc khởi động nhưng làm ì việc phát hiện lỗi suốt đời container. 30/08/2026 · 8 phút đọc 12 Cơ sở dữ liệu chớp tắt vài giây, và cả bốn pod ứng dụng — vốn đang khoẻ mạnh — rơi vào vòng lặp khởi động lại chỉ vì một dòng livenessProbe nghe rất hợp lý Probe là thứ hiếm hoi mà cấu hình sai còn tệ hơn không cấu hình. Dựng ba kiểu sai phổ biến nhất và đo hậu quả: livenessProbe kiểm phụ thuộc ngoài khiến cả fleet restart khi DB chết (và restart không sửa được gì, còn làm phục hồi tệ hơn); timeoutSeconds mặc định 1 giây giết pod hoàn toàn khoẻ chỉ vì nó trả lời chậm hơn một giây; readinessProbe với failureThreshold 1 làm cả dịch vụ mất hết endpoint khi mọi pod cùng trượt một nhịp — mà kubectl get pods trông gần như bình thường. Và vì sao nhiều ứng dụng KHÔNG nên có livenessProbe. 30/08/2026 · 8 phút đọc 13 Vượt giới hạn CPU thì container bị bóp chậm lại nhưng sống nhăn; vượt giới hạn bộ nhớ thì bị giết ngay — cùng một cặp tham số, hai số phận trái ngược requests và limits trông như một cặp, nhưng là hai cơ chế khác nhau, và CPU với bộ nhớ còn được xử lý khác nhau nữa. Đo cả bốn tổ hợp: limit CPU 100m thì container bị bóp 100% chu kỳ mà RESTARTS vẫn 0 (chậm mà không chết), còn limit RAM 128Mi thì OOMKilled exitCode 137 và RESTARTS tăng đều. Khác biệt cốt lõi: CPU nén được nên bị bóp, bộ nhớ không nén được nên bị giết. Kèm ba lớp QoS, vì sao không khai requests là tự nhận vé chết trước, và vì sao limits.cpu thường nên bỏ. 30/08/2026 · 7 phút đọc 14 Ứng dụng của tôi bắt đủ SIGTERM, SIGINT, SIGHUP để dọn dẹp trước khi chết — khi bị OOMKill, nó không nhận được một tín hiệu nào và Kubernetes cũng chẳng sinh event nào OOMKilled là trạng thái pod hay bị chẩn đoán sai nhất. Dựng nó có chủ ý: container vượt limits.memory bị kernel giết bằng SIGKILL (exitCode 137) — không tín hiệu, không giai đoạn dọn dẹp, nên mọi cơ chế tắt sạch bạn viết đều vô dụng, thứ cần giữ phải ghi LIÊN TỤC chứ không phải lúc tắt. Kubernetes không sinh event nào cho OOM — thông tin chỉ nằm ở status pod, nên bảng theo dõi dựa vào event mù hoàn toàn. Và OOMKilled (mức container, không đổi node) khác hẳn Evicted (mức node, đổi node) cả cơ chế lẫn cách chữa. 30/08/2026 · 8 phút đọc 15 Pod Guaranteed mang điểm chết -997, BestEffort mang 1000 — và tôi đọc được thứ tự bị OOM giết mà không phải làm cạn RAM node nào Lớp QoS của Kubernetes không phải quy ước mềm — nó được chống lưng bằng một con số kernel thật: oom_score_adj, cộng thẳng vào điểm mà OOM killer dùng để chọn nạn nhân. Guaranteed mang -997 (gần như bất tử), BestEffort mang 1000 (chết đầu tiên), Burstable theo công thức 1000-(1000×requests/RAM node) khớp chính xác ba lần đo. Nghĩa là requests.memory không chỉ dùng để xếp lịch — nó trực tiếp hạ điểm chết của pod. Và mẹo đo: hiện tượng khó dựng (làm cạn node 16 GB) thì đọc thẳng con số quyết định nó. 30/08/2026 · 7 phút đọc 16 ClusterIP của Kubernetes không có tiến trình nào lắng nghe, không card mạng nào mang nó, ping cũng không nổi — vì nó chỉ là một dòng luật iptables Service cho bạn một IP ổn định trong khi pod đến rồi đi — nhưng mở ra thì ClusterIP không tồn tại ở đâu cả: 0 socket, 0 giao diện mạng, chỉ là luật iptables DNAT đổi địa chỉ đích sang pod thật. Điều đó giải thích vì sao không ping được, telnet cổng lạ thì im lặng, tcpdump không thấy nó. Và một sự thật hay bị bỏ sót: cân bằng tải xảy ra ở mức KẾT NỐI không phải mức yêu cầu, nên HTTP keep-alive và gRPC dồn mọi yêu cầu về cùng vài pod. Cộng thêm vì sao iptables 55.000 dòng không co giãn và IPVS ra đời. 30/08/2026 · 8 phút đọc 17 Thêm đúng một dấu chấm vào cuối tên miền cắt số truy vấn DNS từ 3,7 xuống 1,3 mỗi lần phân giải — vì ndots:5 đang bắt mọi tên ngoài cụm đoán trượt ba lần Cái tên web-svc biến thành IP thế nào, và vì sao tên NGOÀI cụm tốn hơn bạn nghĩ. resolv.conf mặc định có ndots:5, nghĩa là tên ít hơn 5 dấu chấm phải thử cả ba hậu tố search trước — nên google.com trượt ba lần trước khi đúng. Đo bằng chỉ số CoreDNS: google.com tốn 3,7 truy vấn mỗi lần, google.com. (một dấu chấm cuối) chỉ 1,3, và độ trễ nhanh 4,4 lần. Cùng cái bẫy đo: dig trả NXDOMAIN trong khi ứng dụng chạy tốt, vì dig không dùng search list còn getaddrinfo thì có. 30/08/2026 · 7 phút đọc 18 Định tuyến Ingress theo tên miền chạy ngay, còn theo đường dẫn thì trả 404 — và nó hoàn toàn đúng đặc tả, chỉ có kỳ vọng của tôi là sai Service cho một IP một dịch vụ; Ingress cho một điểm vào nhiều dịch vụ, phân biệt bằng tên miền hoặc đường dẫn ở tầng HTTP. Đo cái giá: thêm một chặng proxy mà độ trễ không đo được (0,28 so 0,29 ms), nhưng định tuyến theo đường dẫn trả 404 vì Ingress chuyển nguyên đường dẫn xuống backend — phải viết rewrite-target, mà đó là annotation riêng của ingress-nginx chứ không phải chuẩn Kubernetes. Đổi sang bộ điều khiển khác là viết lại toàn bộ. Và thiếu ingressClassName thì Ingress nằm im, không controller nào nhận, không lỗi. 30/08/2026 · 7 phút đọc 19 Dựng namespace riêng rồi tưởng đã có tường lửa — nhưng pod ở 'dev' vẫn gọi thẳng được cơ sở dữ liệu ở 'prod', chỉ cần đoán đúng tên Namespace chia không gian TÊN, không chia không gian MẠNG — mặc định mọi pod gọi được mọi pod xuyên namespace. Đo cách dựng ranh giới thật bằng NetworkPolicy: chặn hết rồi mở dần theo nhãn, và cái bẫy VÀ-so-với-HOẶC chỉ khác một dấu gạch đầu dòng nhưng nới rộng quyền một cách im lặng. NetworkPolicy thực thi ở tầng CNI nên chặn cả khi gọi thẳng podIP, nhưng chính CNI phải hỗ trợ — plugin không hỗ trợ thì chính sách của bạn chỉ là văn bản, không lỗi, không cảnh báo. 31/08/2026 · 8 phút đọc 20 Cùng một máy, cùng một phép thử: kindnet đạt 87,4 Gbit/s giữa hai node, Calico chỉ 45,6 — và cả 48% chênh lệch nằm gọn trong một dòng bảng định tuyến CNI quyết định pod nhận IP thế nào và gói tin đi giữa các node ra sao. Dựng hai cụm giống hệt, đổi mỗi plugin mạng: kindnet định tuyến thuần đạt 87,4 Gbit/s liên node, Calico với đường hầm IPIP chỉ 45,6 — chậm 48%, vì mỗi gói bị bọc thêm một lớp IP header (dev tunl0). Qua Service chỉ tốn thêm 1,7%: cái đắt là đóng gói chứ không phải chuyển địa chỉ. Con số tuyệt đối cao bất thường vì đo trong một máy với MTU 65535, nhưng TỈ LỆ giữ nguyên vì nguyên nhân là phí trên mỗi gói, không phải tốc độ dây. 31/08/2026 · 8 phút đọc 21 Sửa ConfigMap thì tệp gắn cập nhật sau 65 giây, còn biến môi trường thì không bao giờ đổi — cùng một giá trị, hai cách giao, hai số phận Cấu hình vào pod bằng bốn cách và chúng hành xử khác hẳn khi bạn sửa. Đo: đổi ConfigMap thì tệp gắn cập nhật sau 65 giây (qua một cú hoán đổi liên kết mềm nguyên tử), còn biến môi trường KHÔNG BAO GIỜ đổi vì nó bị đóng băng lúc container khởi động — đó là bản chất của biến môi trường ở mức HĐH, không phải giới hạn Kubernetes. Và base64 của Secret không phải mã hoá: ai đọc được là đọc được mật khẩu, khác biệt thật chỉ là RBAC tách riêng và gắn qua tmpfs không chạm đĩa. 31/08/2026 · 7 phút đọc 22 emptyDir trong RAM ghi nhanh gấp bốn lần trên đĩa — nhưng nó âm thầm ăn vào giới hạn bộ nhớ của pod, và cú OOMKilled sau đó đổ lỗi nhầm chỗ Pod là thứ tạm thời, dữ liệu thì thường không — đo bốn cách gắn lưu trữ và cái nào sống sót qua việc pod bị xoá. emptyDir trên tmpfs nhanh gấp bốn emptyDir trên đĩa nhưng tính vào limits.memory nên ghi quá tay là OOMKilled dù ứng dụng chẳng cấp phát gì. emptyDir gắn vòng đời POD không phải container: sống qua restart, chết qua xoá pod. PVC local-path cấp phát muộn (WaitForFirstConsumer) và bị ghim vào một node, nên node chết là pod kẹt Pending. Và reclaimPolicy Delete mặc định nghĩa là kubectl delete pvc xoá dữ liệu vĩnh viễn, không thùng rác. 31/08/2026 · 7 phút đọc 23 Xoá pod db-1 của StatefulSet rồi để nó dựng lại: IP đổi, nhưng tên, PVC và dữ liệu 'du lieu cua db-1' còn nguyên vẹn — đó là cả điểm khác biệt với Deployment Deployment giả định các bản thay thế được cho nhau; với cơ sở dữ liệu thì không. Đo ba thứ StatefulSet có mà Deployment không: khởi động tuần tự (4,3 / 9,6 / 14,9 giây, chờ bản trước), danh tính ổn định (tên DNS và PVC riêng cho từng bản — xoá pod rồi dựng lại thì chỉ IP đổi, dữ liệu còn), và thu gọn không xoá PVC (an toàn dữ liệu nhưng vẫn trả tiền đĩa). Cùng cái bẫy nguy hiểm nhất: node chết thì StatefulSet CHỜ, vì hai bản cùng tên ghi cùng dữ liệu là hỏng — và --force là lời cam đoan 'bản cũ đã chết' mà nếu sai thì mất dữ liệu. 31/08/2026 · 7 phút đọc 24 DaemonSet hứa 'một bản trên mỗi node' — nhưng cụm 4 node của tôi chỉ có 3 bản, vì chữ 'mỗi' luôn kèm một điều kiện ẩn DaemonSet chạy đúng một bản trên mỗi node — nghe đơn giản, nhưng 'mỗi node' có một điều kiện ẩn: node control plane bị bỏ qua vì có vết, nên cụm 4 node ra 3 bản. Thêm một dòng toleration là đủ 4. Đo tiếp: DaemonSet phản ứng với thay đổi tập node trong 0,1 giây (theo dõi qua watch) so với gần 6 phút khi node CHẾT — vì độ trễ nằm ở việc kết luận node đã chết, không phải ở việc phản ứng. Và vì sao DaemonSet không có replicas: số bản là hệ quả của số node, nó co giãn theo hạ tầng chứ không theo tải. 31/08/2026 · 6 phút đọc 25 Đặt activeDeadlineSeconds=15 nhưng Job bị cắt sau 46 giây — và tôi suýt viết sai thành quy luật vì chỉ đo một điểm Deployment giữ thứ chạy mãi, Job chạy một lần rồi thôi. Đo ba cách Job hỏng: thử lại với độ lùi gấp đôi (10/20/40 giây, backoffLimit mặc định 6 mất ~20 phút), activeDeadlineSeconds cộng thêm đúng 31 giây cố định (không phải nhân — đo một điểm thấy như gấp 3,1 lần, đo ba điểm mới lộ ra là hằng số). Và cái bẫy mặc định của CronJob: concurrencyPolicy Allow cho các lượt chồng lên nhau, nên một job chạy lâu hơn chu kỳ lịch sẽ tự nhân bản tới khi hết tài nguyên mà không có gì cảnh báo. 31/08/2026 · 7 phút đọc 26 HPA phóng từ 2 lên 8 bản trong 30 giây, nhưng thu về mất tới 258 giây — bốn phút rưỡi ôm 8 bản ở 0% CPU, và đó là cố ý HPA tự tăng giảm số bản theo tải — đo cả hai chiều thì chúng chênh nhau 8,6 lần. Mở rộng 30 giây (HPA tính thẳng số bản cần bằng ceil(bản × CPU/mục tiêu), không nhích từng cái), thu gọn 258 giây vì stabilizationWindow mặc định của scaleDown là 300 giây còn scaleUp là 0. Bất đối xứng này có chủ ý: mở rộng chậm thì người dùng khổ, thu gọn chậm chỉ tốn ít tiền vài phút. Thêm hai cái bẫy: thiếu requests.cpu thì HPA im lặng không bao giờ chạy, và co giãn theo bộ nhớ hiếm khi đúng vì JVM/Go không trả RAM. 31/08/2026 · 7 phút đọc 27 Manifest khai cpu=50m nhưng pod chạy với cpu=410m — VPA sửa con số giữa đường, và kubectl get deployment vẫn thản nhiên hiện số cũ HPA thêm bản, VPA sửa requests của từng bản. Đo VPA trên cụm thật: khuyến nghị đầu tiên sau ~60 giây (target 410m dùng được, nhưng upperBound 8.856 nhân là vô nghĩa vì chưa đủ lịch sử). Auto mode làm hai việc dễ tưởng là một: bộ admission ghi đè requests lúc tạo pod (manifest 50m → pod 410m, mà get deployment vẫn hiện 50m — vỡ nguồn-sự-thật của GitOps), và bộ updater chỉ giết pod khi requests nằm NGOÀI khoảng tin cậy. Vì sao VPA hay trông như không làm gì, và vì sao VPA với HPA-theo-CPU đá nhau. 31/08/2026 · 7 phút đọc 28 Tín hiệu 'cụm cần thêm máy' sinh ra sau 0,2 giây, dọn một node hết 14,3 giây — còn khúc giữa tôi không đo được, và tôi nói thẳng điều đó HPA thêm pod, Cluster Autoscaler thêm NODE khi hết chỗ. Đo hai đầu của vòng lặp trên cụm thật: pod vào Pending sau 0,2 giây, drain một node hết 14,3 giây. Còn khúc giữa — nhà cung cấp đám mây thật sự tạo ra một máy — thì kind không dựng được, nên tôi không có số đo và không mượn số của người khác rồi gọi là phép đo. Bài học rơi ra: hai đầu đều nhanh, toàn bộ thời gian chờ nằm ở khúc bạn không kiểm soát — nên tối ưu thời gian cụm phản ứng thì đừng nhìn Kubernetes, hãy nhìn thời gian khởi động máy và kéo ảnh. 31/08/2026 · 7 phút đọc 29 Vết NoExecute đuổi sạch pod khỏi node trong 1,3 giây, còn anti-affinity cứng khiến 2 trong 5 bản không bao giờ chạy — bốn cơ chế điều pod, và chỗ ai cũng lẫn Đo bốn cơ chế điều pod chạy ở đâu trên cụm thật. NoExecute đuổi mọi pod đang chạy trong 1,3 giây (so với node chết mất gần 6 phút). Điểm hay nhầm nhất: toleration chỉ CHO PHÉP pod lên node có vết, nó KHÔNG ép — muốn ép pod lên node GPU phải thêm nodeSelector, vì 'giữ kẻ khác ra' và 'đưa mình vào' là hai việc tách biệt. Và anti-affinity cứng chặn thẳng (3 Running, 2 Pending, số bản bị trần bởi số node) trong khi mềm rải đều mà không bao giờ để pod Pending. 31/08/2026 · 7 phút đọc 30 Đặt minAvailable=4 cho một deployment 4 bản, và cái node đó không bao giờ rút ra bảo trì được nữa — cùng một lệnh drain: 2,1 giây, hay treo vĩnh viễn PodDisruptionBudget bảo Kubernetes đừng đuổi quá nhiều bản cùng lúc. Đo bằng cách rút node ba lần: không PDB xong trong 2,1 giây, minAvailable=4 trên 4 bản treo mãi không xong, minAvailable=3 xong trong 6,6 giây. Bài học lớn: minAvailable là con số tuyệt đối nên thành bẫy khi HPA thu gọn số bản ban đêm — cho-phep-duoi tụt về 0 đúng lúc bạn muốn bảo trì; maxUnavailable=1 thì an toàn ở mọi quy mô. Và PDB chỉ chặn việc đuổi TỰ NGUYỆN, nó không phải cơ chế bảo đảm tính sẵn sàng. 31/08/2026 · 7 phút đọc 31 kubectl báo tạo Deployment thành công, nhưng không một pod nào chạy và màn hình chỉ hiện 0/1 câm lặng — lời từ chối nằm một tầng dưới, chỗ chẳng ai nhìn ResourceQuota biến namespace thành ranh giới tài nguyên thật. Nhưng nó chặn ở tầng POD chứ không phải tầng Deployment: create deployment báo thành công, get deployment hiện 0/1, và lý do thật giấu trong event của ReplicaSet. Đo bốn loại giới hạn — giới hạn trên object báo lỗi ngay cho người gõ lệnh, giới hạn trên pod báo lỗi ở chỗ ít ai nhìn. Cùng bài học phụ: quota tính theo requests khai báo không theo mức dùng thật, và ai có quyền xoá quota thì quota không giới hạn được họ. 31/08/2026 · 7 phút đọc 32 Tôi cấp một ServiceAccount đúng quyền 'chỉ đọc' Secret — rồi đọc ra ngay SieuBiMat123, và ba quyền nghe vô hại khác hoá ra tương đương quyền quản trị Cấp quyền từng lớp cho một ServiceAccount rồi đo chính xác nó mở ra được gì. Role bó rất chặt (đúng động từ, đúng tài nguyên, đúng namespace), nhưng 'chỉ đọc' Secret là đọc được mọi mật khẩu vì base64 không phải mã hoá. Ba quyền nghe vô hại trong một bản YAML — create pods, get secrets, create rolebindings — đều là đường leo thang lên quyền toàn cụm. Và kubectl auth can-i là cách duy nhất kiểm quyền thực tế thay vì đọc YAML rồi đoán. 31/08/2026 · 7 phút đọc 33 Token gắn vào pod Kubernetes in kèm cả tên pod lẫn tên node — pod chết là token vô hiệu ngay lập tức, không đợi hết hạn Mỗi pod tự xưng danh với API server bằng một token ngắn hạn nằm ở /var/run/secrets, và nó không phải chìa khóa vạn năng: token gắn với đúng pod và đúng node, pod bị xoá thì token chết ngay nên kẻ nhặt được token của pod đã chết chẳng dùng được gì. Đo tiếp: cùng token đó gọi API cho kết quả 200/403 khớp CHÍNH XÁC RBAC — vì token chỉ trả lời 'anh là ai', còn 'anh được làm gì' vẫn do RBAC quyết. Và token dài hạn cho CI thì không gắn pod nào, dùng được ở bất cứ đâu, không thu hồi nổi trừ khi xoá ServiceAccount. 31/08/2026 · 7 phút đọc 34 Mức restricted của Kubernetes từ chối thẳng một pod nginx hoàn toàn vô hại — không vì nó nguy hiểm, mà vì nó không chịu khai rằng mình an toàn Pod Security Standards bật bằng đúng một nhãn namespace, không cài gì thêm. Đo trên cùng một pod ở ba mức: restricted từ chối cả một nginx bình thường, và toàn văn lời từ chối cho thấy lý do — nó đòi bốn dòng securityContext khai báo tường minh, vì mặc định của Kubernetes là mở (chạy root, giữ đủ capability, cho leo thang quyền) nên im lặng bị coi là nguy hiểm. Thêm cái bẫy: enforce chỉ xét lúc TẠO pod nên pod vi phạm đang chạy vẫn sống, và sự cố chỉ nổ ở lần deploy hoặc scale tiếp theo. 31/08/2026 · 7 phút đọc 35 Ai cũng bảo nginx non-root chết vì không mở nổi cổng 80 — tôi đo thì cổng 80 mở ngon lành, thứ giết nó là một lệnh mkdir Thêm runAsUser: 1000 vào nginx thì pod chết — nhưng không phải vì cổng. Log chỉ thẳng: lỗi mkdir /var/cache/nginx, tức lỗi ghi hệ tệp, sửa bằng chown chứ chẳng cần đặc quyền gì. Đo tiếp cái ai cũng lặp lại 'cổng dưới 1024 cần root hoặc NET_BIND_SERVICE': uid 1000 mở cổng 80 vẫn được, capability kia chẳng làm gì, vì ip_unprivileged_port_start trong netns container là 0. Và một lần thử fsGroup không tái tạo nổi lỗi vì thư mục emptyDir là 0777 — bài học về phép thử dễ dãi cho kết quả xanh vô nghĩa. 31/08/2026 · 7 phút đọc 36 Job xong việc trong 5 giây nhưng Kubernetes báo chạy hết 41 giây — thủ phạm là một sidecar không biết khi nào nên dừng Từ Kubernetes 1.29, một dòng restartPolicy: Always biến init container thành sidecar — không chặn bước sau, sống suốt đời pod, và tự bị giết khi container chính thoát (chữa hẳn cái bệnh CronJob-có-service-mesh chạy mãi không kết thúc). Nhưng đo dấu thời gian ra một điều bất ngờ: Job 'xong' vẫn tốn 41 giây cho việc 5 giây, vì sidecar busybox không bắt SIGTERM nên cộng thẳng cả hạn ân hạn 30 giây vào mỗi lần chạy. Hạ hạn xuống 5 thì DURATION về 16 giây, chênh đúng 25. 31/08/2026 · 7 phút đọc 37 Chỉ dời một lệnh echo từ trước ra sau, thời gian xoá pod nhảy từ 0,6 lên 31 giây — và câu 'dùng sh -c là mất tín hiệu' hoá ra sai Cùng một chương trình Python bắt SIGTERM, bốn cách khai lệnh, thời gian dừng chênh nhau 50 lần. Bí mật nằm ở một tối ưu hoá vô hình của shell: nếu lệnh cuối không còn gì phía sau, shell tự exec thành chính nó nên ứng dụng thành PID 1 và nhận tín hiệu; thêm một echo phía sau là shell phải ở lại giữ PID 1, và kernel bỏ qua mọi tín hiệu PID 1 không đăng ký. Thêm phép đo thứ hai nghịch trực giác: ứng dụng đóng cổng càng NHANH càng mất nhiều yêu cầu, và preStop sleep 5 đưa 3 lỗi về 0. 31/08/2026 · 8 phút đọc 38 ulimit thề rằng 'unlimited' — nhưng container của tôi chết đứng ở đúng 19.162 tiến trình, và cái trần đó không nằm ở chỗ ulimit nhìn Container có hai cái trần vô hình mà nó thường chạm trước cả trần RAM. ulimit -u báo unlimited trong khi trần thật là 19.162 (pids.max của cgroup, đúng 15% threads-max) — công cụ cũ tự tin trả lời sai câu hỏi vì nó chỉ thấy cơ chế RLIMIT có từ trước cgroup. Chạm trần thì lỗi tên là EAGAIN 'thử lại sau' nhưng không bao giờ hết, nên phần lớn thư viện thử lại vô hạn và biến một cái trần cứng thành vòng lặp đốt CPU không báo lỗi. Và lời khuyên 'nhớ tăng ulimit -n' đã hết việc vì trình chạy container đặt sẵn một triệu. 31/08/2026 · 8 phút đọc 39 Pod in ra 400.000 dòng nhật ký, nằm sờ sờ 53 MB trên đĩa — kubectl logs trả về đúng 0 dòng, không một lời cảnh báo kubectl logs là lệnh ai cũng gõ đầu tiên khi có sự cố — bài này đo nó thực sự đọc gì và không đọc gì. Log là tệp văn bản trên đĩa node, không qua control plane; kubectl logs chỉ đọc tệp HIỆN TẠI, nên 400.000 dòng đã xoay vòng ra file khác trả về 0 dòng không lỗi. Hệ quả đáng sợ: pod ồn ào tự xoá log của chính mình, và đúng lúc sự cố thì nó in nhiều nhất nên chính phần đầu sự cố bị đẩy ra khỏi tầm với. Thêm: containerLogMaxSize 10Mi không phải trần cứng — đo được 55 MB vì kubelet chỉ kiểm định kỳ. 31/08/2026 · 8 phút đọc 40 Cùng một container, kubectl top báo 12 Mi bộ nhớ còn Grafana vẽ 413 Mi — cả hai đều đúng, và biết vì sao thì bảng cảnh báo của bạn hết kêu oan Bốn con số bộ nhớ cho một container, chênh nhau 34 lần, tất cả đều đúng vì chúng trả lời những câu hỏi khác nhau. memory.current 413 Mi gồm 400 Mi bộ đệm tệp mà kernel vứt trong tích tắc; working_set chỉ 13 Mi mới là phần OOM killer nhìn vào. Đây là nguồn của phần lớn báo động giả 'rò bộ nhớ'. Thêm: kubectl top chậm hơn thực tế 30 giây và lặp lại y hệt mẫu cũ — nó không đo, chỉ đọc lại mẫu gần nhất, nên một đợt tải ngắn hơn 15 giây có thể không lọt vào mẫu nào. 31/08/2026 · 8 phút đọc 41 Sự kiện cũ nhất trong cụm Kubernetes của tôi chỉ 61 phút tuổi — và 'BackOff x26' không hề nghĩa là pod khởi động lại 26 lần Sự kiện là nơi Kubernetes tự nói ra vì sao nó làm điều vừa làm — nhưng nó tự xoá sau đúng một giờ (mặc định --event-ttl, trần cứng, không bản sao ở đâu). Đo ba cái bẫy: sự kiện hết hạn là mất sạch khác hẳn nhật ký còn trên đĩa; BackOff x26 nhưng restart thật chỉ 5 vì count đếm số lần kubelet NÓI về một tình trạng chứ không đếm số lần nó xảy ra; và sự kiện sống lâu hơn chính pod đã bị xoá, nên nó là dấu vết duy nhất trả lời 'pod của tôi đâu rồi'. 31/08/2026 · 8 phút đọc 42 Pod distroless không có cả sh lẫn ls, kubectl exec bó tay hoàn toàn — nhưng vẫn đọc được cấu hình và biến môi trường thật của nó qua /proc/1/root Image càng gọn thì kẻ tấn công càng ít thứ để dùng — và bạn cũng vậy lúc gỡ rối. Khi exec vô dụng với distroless, kubectl debug luồn một ephemeral container vào ĐÚNG pod đang hỏng mà không khởi động lại gì. Đo cái bẫy lớn nhất: thiếu --target thì container gỡ rối nằm không gian PID riêng, không thấy tiến trình nào của ứng dụng (PID 1 là sh của chính nó, không phải /pause của mục tiêu). Có --target rồi thì đọc cả hệ tệp lẫn /proc/1/environ thật của container mục tiêu — thứ exec không bao giờ với tới. 31/08/2026 · 8 phút đọc 43 Pod báo CrashLoopBackOff nhưng container chưa từng chạy một giây nào — cột STATUS của Kubernetes đang mô tả việc nó đang làm, không phải việc đã hỏng Dựng bảy sự cố thật rồi đo xem công cụ nào nói thật. CrashLoopBackOff che hai nguyên nhân hoàn toàn khác nhau — ứng dụng chết (đọc logs) và lệnh khởi động không tồn tại (logs rỗng, exit 128, phải đọc events). ContainerCreating nghe như đang tiến triển nhưng đứng đó vĩnh viễn vì thiếu Secret. Quy tắc dùng ngay: nhật ký rỗng mà restarts>0 thì container không crash, nó không khởi động nổi — và bảng mã thoát 128/137/143 nói container chết thế nào. 31/08/2026 · 8 phút đọc 44 Hai pod cùng một dòng mã, đồ thị CPU vẽ y hệt nhau — nhưng một cái chậm gấp 23 lần, và cái chỉ số ai cũng nhìn không hề thấy điều đó Pod chạy được nhưng chậm khó lần hơn pod hỏng hẳn, vì mọi thứ đều xanh. Hai pod khác nhau đúng mỗi limits.cpu: p50 gần như bằng nhau (4,9 so 3,9 ms) mà p95 chênh 23 lần — và ~96 ms không ngẫu nhiên, đó chính là chu kỳ 100 ms của CFS. Đồ thị 'CPU usage' vẽ hai pod trùng khít vì usage giống nhau; bằng chứng nằm ở cpu.stat: 36/50 chu kỳ bị bóp, 3,28 giây bị treo. Và từ K8s 1.33, đổi limit không cần khởi động lại pod nên p95 rơi từ 95,9 xuống 4,1 ms ngay trong lúc gỡ. 31/08/2026 · 9 phút đọc 45 Ba lỗi mạng hoàn toàn khác nhau trong Kubernetes cho ra đúng một thông báo và đúng một thời gian — và một lệnh duy nhất phân biệt được cả ba Nhãn sai, cổng sai, pod chưa sẵn sàng — ba nguyên nhân khác hẳn nhau nhưng đều ra ConnectionRefused ~0 ms, và DNS thì phân giải tốt ở cả ba nên nslookup chẳng chứng minh gì. Đo cách một bảng endpointslices tách được ba bệnh cùng triệu chứng, và vì sao khác biệt Refused-ngay-lập-tức so với Timeout-đúng-hạn-chờ (3004 ms khi bật NetworkPolicy) là phép chia đôi tốt nhất khi lần lỗi mạng. 31/08/2026 · 8 phút đọc 46 Tôi xoá tay một Service do Helm tạo, rồi hỏi helm status — nó vẫn thản nhiên báo 'deployed', vì Helm chỉ đọc lại biên nhận của chính mình chứ không nhìn vào cụm Đo hai thứ ở Helm: nó thêm gì vào manifest, và nó KHÔNG biết gì. 380 dòng chart nguồn chỉ sinh ra 100 dòng YAML — giá trị của Helm không nằm ở viết ít mà ở tham số hoá. Trạng thái release là một Secret base64 hai lần rồi gzip, giữ cả mã nguồn chart nên 1,9 KB manifest phình thành 12 KB. Nhưng chỗ đáng nhớ nhất: xoá tay một Service thì helm status vẫn báo deployed, vì Helm không phải bộ điều khiển — nó đọc lại cái nó ĐÃ làm, không phải cái đang CÓ. Và uninstall để lại PVC của StatefulSet lẫn CRD, vì Helm chỉ dọn được thứ chính nó tạo. 31/08/2026 · 8 phút đọc 47 Tôi từng nói Kustomize gọn hơn Helm — đo lại thì 41 dòng so 37 dòng, gần như hoà; khác biệt thật nằm ở chỗ Kustomize không nhớ nó đã tạo ra gì Đo Kustomize trên đúng bài toán đã đo Helm, và phát hiện phép so 380-dòng ở phần trước là so nhầm chart mẫu với overlay tối giản. Đo cho công bằng: Helm 37 dòng, Kustomize 41 dòng — đếm dòng là hoà. Khác biệt thật ít ai nói tới: Kustomize không có trạng thái release (0 Secret trong namespace), nên bỏ một tài nguyên khỏi khai báo thì nó vẫn còn nguyên trong cụm — Helm xoá được vì nhớ lần trước, Kustomize phải mượn --applyset. Cùng lúc configMapGenerator băm nội dung nên đổi cấu hình là Deployment tự cuộn lại, thứ Helm phải tự gắn checksum mới có. 31/08/2026 · 8 phút đọc 48 30 dòng YAML mua được kiểm tra dữ liệu và cột hiển thị riêng cho Kubernetes — nhưng nó tạo ra 0 pod, vì một CRD là danh từ còn Operator mới là động từ Tự viết cả CRD lẫn Operator rồi đo. 30 dòng YAML cho validation, giá trị mặc định và cột hiển thị mà không viết dòng mã nào — nhưng tạo xong TrangWeb thì namespace vẫn 0 Deployment, 0 pod. 38 dòng Python biến nó thành Operator thật (soBan 2→4 sau ~5 giây, xoá là Deployment tự biến mất nhờ ownerReferences). Chi phí thật lộ ra ở chỗ khác: vòng lặp quét 20 đối tượng gọi 660 lệnh API trong 60 giây, 11 lệnh/giây kể cả khi không có gì đổi — và với 200 đối tượng là 110 lệnh/giây không bao giờ nghỉ. 31/08/2026 · 9 phút đọc 49 Ghi 50 MB vào ổ đĩa nhưng snapshot cả cụm Kubernetes chỉ phình thêm 135 KB — vì nó sao lưu bản thiết kế chứ không sao lưu đồ đạc trong nhà Đo hai nửa của việc sao lưu Kubernetes. Snapshot etcd nhỏ hơn thư mục /var/lib/etcd 14,9 lần và chụp trong 0,1 giây — nhưng ghi 50 MB vào một PersistentVolume chỉ làm nó tăng 0,26%, vì etcd lưu định nghĩa đối tượng chứ không lưu nội dung volume. Và cái bẫy tên gọi: 'kubectl get all' bỏ sót 119 đối tượng, kể cả MỌI Secret. Bản sao lưu đó khôi phục lại một cụm không mật khẩu, không cấu hình, không phân quyền. 31/08/2026 · 8 phút đọc 50 Khôi phục cả cụm Kubernetes từ snapshot mất đúng 77 giây — và công cụ để làm việc đó lại nằm trong chính cái container bạn vừa tắt Khôi phục thật từ một snapshot 1,1 MB trên cụm dựng riêng để phá: 77 giây từ lúc dừng API server tới lúc nó sống lại. Ba cái bẫy đo được — etcdutl không có trên node mà nằm trong pod etcd đã chết, thư mục khôi phục phình thành 63 MB, và điều quan trọng nhất: khôi phục etcd không chọn lọc được. Cái tạo trước khi chụp về đủ, cái tạo sau khi chụp mất sạch không một dòng cảnh báo — đây là nút hoàn tác toàn cục, không phải Ctrl+Z một thao tác. 01/09/2026 · 8 phút đọc 51 Nâng cấp Kubernetes: bỏ PodDisruptionBudget, hạ còn đúng một bản sao, vẫn không mất một yêu cầu nào — thứ cứu được là preStop, không phải PDB Nâng cấp Kubernetes gồm hai phần: nâng control plane (kubeadm lo) và thay từng node (việc của bạn, và là phần gây gián đoạn). Đo drain node giữa lúc phục vụ ở ba cấu hình: 2 bản + PDB + preStop = 0 lỗi, nhưng 1 bản không PDB mà VẪN có preStop cũng 0 lỗi, còn bỏ preStop + probe thì mất 3 yêu cầu mỗi lần. Cái tạo khoảng chồng lấn không phải số bản sao mà là 5 giây trì hoãn của preStop — và PDB bảo vệ một thứ hoàn toàn khác. 01/09/2026 · 8 phút đọc 52 Hai cụm Kubernetes dựng theo mặc định dùng chung một dải IP 10.244.0.0/16 nên không tài nào định tuyến cho nhau — và mỗi control plane trống tốn 536 MB RAM 'Chia thành nhiều cụm' nghe như một quyết định kiến trúc. Đo trên hai cụm thật chạy cạnh nhau: mỗi control plane trống tốn 536 MB RAM cố định (nhân với số cụm), và cả hai cụm mặc định của kubeadm dùng chung 10.244.0.0/16 nên pod cụm này không gọi được pod cụm kia — mạng không định tuyến, DNS không biết nhau. Cấp dải IP khác nhau ngay từ khi tạo, vì đổi sau là dựng lại cả cụm. 01/09/2026 · 8 phút đọc 53 Sidecar của service mesh chỉ thêm 0,19 ms và 5 MB RAM — nhưng một pod ngoài mesh vẫn gọi thẳng vào được không mã hoá; cài mesh không tự nó chặn gì Service mesh hứa mTLS, quan sát và điều khiển lưu lượng mà không sửa mã. Cài Linkerd lên cụm thật rồi đo: sidecar thêm ~0,19 ms (2,4 lần tương đối nhưng dưới 5% với dịch vụ thật) và chỉ 3–5 MB RAM. mTLS bật sẵn không cần khai gì. Nhưng điểm quan trọng nhất: mTLS là CƠ HỘI, không phải bắt buộc — pod ngoài mesh gọi vào vẫn chạy bình thường bằng văn bản rõ, cho tới khi bạn thêm chính sách cưỡng chế. 01/09/2026 · 8 phút đọc 54 PostgreSQL trong Kubernetes chạy 10.366 tps và khởi động lại trong 0,3 giây — nhưng cordon đúng một node là pod Pending vĩnh viễn, vì dữ liệu không đi theo được Dựng PostgreSQL bằng StatefulSet, đo phần chạy tốt rồi đo đúng chỗ nó gãy. Phần tốt: 10.366 tps, dừng sạch trong 13 ms, khởi động lại 0,3 giây, giữ nguyên định danh và volume. Phần không sửa được bằng YAML: PersistentVolume ghim vào một node, cordon node đó là pod Pending mãi mãi vì dữ liệu nằm trên đĩa node đó. Kubernetes dựng lại pod giỏi, nhưng chỉ ở nơi có dữ liệu — và nó không biết cái nào là bản chính. 01/09/2026 · 9 phút đọc 55 Trần thật của một node Kubernetes không phải RAM mà là con số 110 — node to gấp đôi không cho gấp đôi số pod, và cụm kind báo 62,4 GiB trên một máy chỉ có 15,6 Đo chi phí bên trong một cụm: bao nhiêu tài nguyên biến mất trước khi ứng dụng đầu tiên chạy. kubelet + containerd 207 MB mỗi node cố định; một pod rỗng tốn 0,44 MB. Nhưng con số đảo ngược trực giác nhất là trần 110 pod/node — độc lập với kích thước node, nên node 16 GB xếp đầy chỉ còn 145 Mi mỗi pod, và pod nhỏ chạm trần SỐ LƯỢNG trước khi chạm trần RAM. Cẩn thận: đo trên kind lạc quan gấp bốn lần vì mỗi node container thấy RAM của cả máy. 01/09/2026 · 8 phút đọc 56 python:3.12 có 2.889 lỗ hổng, bản alpine cùng Python đó có 25 — ít hơn 115 lần mà không sửa một dòng mã; và đổi ảnh trên cùng một thẻ làm chữ ký biến mất sạch Pod Security Standards kiểm soát pod LÀM GÌ; bài này lùi một bước, kiểm soát cái NẰM TRONG pod. Quét sáu ảnh: python:3.12 Debian 2.889 lỗ hổng so với python:3.12-alpine chạy cùng Python chỉ 25 — chênh 115 lần chỉ vì mọi thứ đi kèm. Và phép đo quan trọng nhất: chữ ký gắn với digest chứ không với thẻ, nên đổi ruột ảnh dưới cùng một thẻ v1 làm chữ ký mất hoàn toàn. Dùng digest, không dùng thẻ. 01/09/2026 · 9 phút đọc 57 Tăng 20 lần số đối tượng làm một lệnh kubectl get chậm gấp 85 lần — nhưng tạo pod vẫn mất đúng một giây; cụm không chậm nói chung, chỉ LIST chậm Tài liệu nói Kubernetes chịu 5.000 node và 150.000 pod. Bài này đo cụm thật bắt đầu chậm ở đâu và chậm ở CÁI GÌ: 20.001 ConfigMap làm LIST mất 6,79 giây (85 lần so với 1.001), nhưng tạo pod vẫn ~1 giây. Cái vỡ không phải thao tác điểm mà là LIST/WATCH và bộ nhớ controller — informer giữ toàn bộ đối tượng trong RAM. Kèm hai trần kích thước khác nhau: 1 MB với create, chỉ 262 KB với apply. 01/09/2026 · 8 phút đọc 58 Sửa tay trên cụm Kubernetes bị Argo CD hoàn tác sau 2 giây — nhưng một commit mới trong Git phải chờ 159 giây, vì Argo theo dõi cụm còn Git thì nó chỉ quét Helm không chống được trôi cấu hình. Bài này cài Argo CD lên cụm thật rồi đo hai độ trễ chênh nhau gần trăm lần: sửa trực tiếp trên cụm bị kéo về sau 2,1 giây (Argo watch cụm), còn commit mới phải chờ tới 159 giây (Argo poll Git, mặc định 180s). selfHeal nhanh nhưng có giải lui khi tranh với controller khác, và quyền ghi Git giờ chính là quyền ghi cụm. 01/09/2026 · 8 phút đọc 59 Cùng một ứng dụng: Compose khởi động 0,9 giây, Kubernetes 1,2 giây — và Kubernetes trần trụi vẫn mất yêu cầu khi cập nhật y hệt Compose Câu hỏi 'Kubernetes hay chỉ Docker Compose' thường được trả lời bằng niềm tin. Dựng cùng một ứng dụng hai container bằng cả hai rồi đo song song: bốn chỉ số đầu gần hoà (K8s không chậm hơn, không tốn RAM container hơn), nhưng K8s mang 743 MB hạ tầng cố định. Khác biệt thật ở cập nhật: K8s một bản không probe mất yêu cầu y như Compose; chỉ nhiều bản + readinessProbe + preStop mới cho 0 lỗi. Kubernetes không tặng uptime, nó tặng cơ chế. 01/09/2026 · 8 phút đọc 60 Sáu mươi phép đo Kubernetes gom thành một danh sách kiểm trước khi lên production — cùng ba lần tôi công bố sai rồi tự phát hiện lúc chạy lại Sáu mươi phần, mỗi phần một phép đo trên cụm dựng bằng kind rồi xoá đi, gom lại thành thứ dùng được: mười con số đáng nhớ nhất, ba dạng lỗi lặp lại nhiều nhất, một danh sách kiểm trước khi lên chạy thật mà mỗi dòng dẫn về một phép đo cụ thể, và ba lần tôi viết sai rồi phải sửa. Bài học còn lại không phải sáu mươi mẹo, mà một thói quen: đo trước khi tin, kể cả tài liệu chính thức. 01/09/2026 · 7 phút đọc