Mở đầu — vì sao bài này quan trọng
Ở các bài trước, bạn đã học cách đóng gói ứng dụng vào Docker, viết Dockerfile chuẩn, chạy nhiều service cùng lúc bằng Docker Compose và đẩy image lên registry. Mọi thứ hoạt động mượt mà — trên máy của bạn, hoặc trên một con server duy nhất.
Nhưng thực tế sản xuất thì khác. Hãy tưởng tượng bạn là DevOps Engineer tại một công ty fintech ở TP.HCM. Sản phẩm được tách thành 50 microservices. Mỗi service chạy 3 replicas để chịu tải và không bị gián đoạn khi một bản lỗi. Bạn có 3 môi trường: dev, staging, production. Nhân con số lại: 50 × 3 × 3 = 450 container đang chạy cùng lúc. Chúng nằm rải rác trên khoảng 15–20 con server.
Bây giờ hãy hỏi những câu rất đời thường: Container nào chạy trên server nào? Một con server chết lúc 2 giờ sáng, ai sẽ khởi động lại 30 container vừa biến mất trên đó? Khi bạn deploy phiên bản mới, làm sao thay thế từng container mà không làm rớt toàn bộ dịch vụ? Khi lượng người dùng tăng gấp đôi vào dịp sale, ai sẽ tự động thêm replica? Nếu bạn định trả lời những câu hỏi này bằng script bash và sức người, bạn sẽ kiệt sức trong một tuần.
Đây chính xác là bài toán mà Kubernetes (thường viết tắt là K8s — "K", 8 chữ cái ở giữa, rồi "s") sinh ra để giải quyết. Bài học này không dạy bạn gõ lệnh kubectl — điều đó để dành cho các bài sau. Mục tiêu hôm nay là giúp bạn hiểu vì sao Kubernetes tồn tại và kiến trúc bên trong nó vận hành ra sao. Hiểu được hai điều này, mọi khái niệm Pod, Deployment, Service ở các bài kế tiếp sẽ trở nên hiển nhiên thay vì học vẹt.
Khái niệm cốt lõi
Container orchestration là gì
Khi bạn chỉ có vài container, bạn "điều phối" chúng bằng tay: gõ docker run, theo dõi, restart khi lỗi. Khi số lượng lên hàng trăm, hàng nghìn container trên nhiều máy, bạn cần một hệ thống làm việc đó tự động. Công việc đó gọi là container orchestration — điều phối container. Kubernetes là công cụ orchestration phổ biến nhất hiện nay, được Google phát triển dựa trên hệ thống nội bộ tên Borg đã vận hành trong hơn một thập kỷ, rồi mở nguồn năm 2014 và giao cho tổ chức CNCF quản lý.
Orchestration cần đảm nhận những trách nhiệm sau:
- Scheduling — quyết định container nào chạy trên server nào, dựa trên tài nguyên còn trống (CPU, RAM).
- Self-healing — tự động khởi động lại container chết, thay thế container trên server hỏng.
- Scaling — tăng/giảm số bản sao theo tải.
- Service discovery & load balancing — giúp các service tìm và gọi nhau, phân bổ lưu lượng đều.
- Rollout & rollback — cập nhật phiên bản mới dần dần, quay lui khi có sự cố.
Tư duy khai báo (declarative) — trái tim của Kubernetes
Điểm khác biệt lớn nhất của Kubernetes so với việc quản lý container thủ công nằm ở triết lý declarative. Với cách làm cũ (imperative), bạn ra lệnh từng bước: "chạy container này", "dừng container kia", "khởi động lại cái nọ". Bạn mô tả quá trình.
Với Kubernetes, bạn không ra lệnh. Bạn mô tả trạng thái mong muốn (desired state): "Tôi muốn service checkout luôn có 3 bản sao đang chạy, dùng image phiên bản v2.3". Bạn ghi điều đó vào một file YAML và đưa cho Kubernetes. Từ giây phút đó, Kubernetes tự chịu trách nhiệm biến trạng thái thực tế khớp với trạng thái bạn mong muốn — và giữ nó như vậy mãi.
Cơ chế đứng sau là reconciliation loop (vòng lặp hòa giải): Kubernetes liên tục so sánh "trạng thái hiện tại" với "trạng thái mong muốn", phát hiện sai lệch, và hành động để sửa. Một replica chết? Trạng thái hiện tại còn 2, mong muốn là 3, Kubernetes lập tức tạo lại 1 cái mới. Đây là lý do người ta nói Kubernetes có khả năng self-healing: nó không cần bạn báo lỗi, nó tự phát hiện và tự sửa.
Kiến trúc tổng thể: Control Plane và Worker Nodes
Một cụm (cluster) Kubernetes gồm hai loại thành phần: Control Plane (bộ não ra quyết định) và các Worker Node (nơi container thực sự chạy).
Control Plane gồm các thành phần chính:
- kube-apiserver — cửa ngõ duy nhất của cụm. Mọi thao tác, dù từ
kubectl, từ dashboard hay từ chính các thành phần nội bộ, đều đi qua API Server. Nó xác thực, kiểm tra quyền, rồi ghi trạng thái vào etcd. - etcd — cơ sở dữ liệu key-value phân tán, lưu toàn bộ trạng thái của cụm. Đây là "nguồn sự thật duy nhất". Mất etcd là mất cụm, nên nó luôn được backup cẩn thận.
- kube-scheduler — quan sát các Pod mới chưa được gán server, rồi quyết định đặt chúng lên node nào dựa trên tài nguyên, ràng buộc, affinity.
- kube-controller-manager — tập hợp các controller chạy reconciliation loop: đảm bảo số replica đúng, thay node hỏng, dọn dẹp tài nguyên thừa.
- cloud-controller-manager — tích hợp với nhà cung cấp cloud (AWS, GCP, Azure) để tạo load balancer, disk, route mạng khi cần.
- kubelet — "quản đốc" trên mỗi node. Nó nhận lệnh từ API Server, đảm bảo các container được yêu cầu thực sự đang chạy và khỏe mạnh, báo cáo tình trạng ngược về Control Plane.
- container runtime — phần mềm thực sự chạy container, ngày nay thường là containerd hoặc CRI-O (Docker Engine đã bị Kubernetes ngừng hỗ trợ trực tiếp từ v1.24).
- kube-proxy — quản lý quy tắc mạng trên node, giúp lưu lượng đến đúng Pod và cân bằng tải giữa các bản sao.
Pod — đơn vị nhỏ nhất
Điều khiến nhiều người mới bất ngờ: Kubernetes không trực tiếp quản lý container. Đơn vị nhỏ nhất mà nó điều phối là Pod — một lớp bọc chứa một (hoặc vài) container luôn được đặt chung, chia sẻ mạng và storage. 90% trường hợp một Pod chỉ chứa đúng một container ứng dụng. Bạn không tạo Pod thủ công trong production; bạn để các đối tượng cấp cao hơn như Deployment quản lý vòng đời của chúng — chi tiết sẽ ở bài kế tiếp.
Tình huống thực tế
Ví dụ 1: Sàn thương mại điện tử và đêm sale 12.12
Một sàn TMĐT tại Đông Nam Á (quy mô tương tự Tiki hay Sendo) chạy backend gồm 40 microservices. Ngày thường, service search chạy 4 replica là đủ. Nhưng đêm 12.12, lượng truy vấn tìm kiếm tăng gấp 8 lần trong vòng 30 phút.
Trước khi dùng Kubernetes, đội vận hành phải trực đêm, thủ công SSH vào server, chạy thêm container, cập nhật cấu hình load balancer bằng tay. Có năm họ tính sai, search sập, kéo theo cả trang chủ trắng xóa trong 12 phút — thiệt hại doanh thu và uy tín.
Sau khi chuyển sang Kubernetes, họ khai báo trạng thái mong muốn cho search với dải từ 4 đến 40 replica, gắn với ngưỡng CPU. Đêm sale, khi CPU vượt ngưỡng, Kubernetes tự nhân bản; sale qua đi, tải giảm, nó tự thu về 4 replica để tiết kiệm chi phí. Đội vận hành ngủ yên.
Bài học: Giá trị của Kubernetes không nằm ở việc "chạy container" — Docker đã làm được. Nó nằm ở khả năng biến trạng thái mong muốn thành hiện thực một cách tự động, kể cả khi tải biến động dữ dội.
Ví dụ 2: Server chết lúc 2 giờ sáng ở một startup logistics
Một startup logistics ở Hà Nội có cụm 12 node trên cloud. Một đêm, nhà cung cấp cloud gặp sự cố phần cứng, 2 node đột ngột chết, cuốn theo khoảng 25 Pod đang chạy trên đó.
Không có ai thức. Nhưng Control Plane phát hiện 2 node ngừng gửi tín hiệu (heartbeat) qua kubelet. kube-controller-manager kết luận các node đó không còn khỏe, đánh dấu Pod trên đó là "mất". Reconciliation loop lập tức nhận ra trạng thái hiện tại lệch trạng thái mong muốn, và kube-scheduler xếp lại 25 Pod đó lên các node còn sống trong vòng chưa đầy 2 phút.
Sáng hôm sau, đội DevOps chỉ thấy một cảnh báo "2 node đã được thay thế tự động" và mọi dịch vụ vẫn xanh. Với hệ thống cũ, đây đã là một sự cố production nghiêm trọng phải gọi người dậy xử lý.
Bài học: Self-healing không phải phép màu — nó là hệ quả trực tiếp của kiến trúc reconciliation loop cộng với cơ chế heartbeat giữa kubelet và Control Plane.
Ví dụ 3: Khi Kubernetes là lựa chọn SAI
Một studio game indie ở Đà Nẵng gồm 4 người dựng một cụm Kubernetes để chạy đúng... một website landing page và một API nhỏ. Họ mất 3 tuần cấu hình, học YAML, xử lý lỗi mạng CNI, và tốn chi phí Control Plane hàng tháng. Trong khi đó, toàn bộ hệ thống của họ hoàn toàn có thể chạy gọn trên một máy ảo duy nhất với Docker Compose, hoặc một dịch vụ PaaS.
Sau nửa năm vật lộn, họ gỡ Kubernetes và quay về giải pháp đơn giản. Năng suất tăng vọt.
Bài học: Kubernetes giải quyết bài toán quy mô lớn và phức tạp. Nếu bạn không có bài toán đó — không nhiều service, không cần scale động, không cần độ sẵn sàng cao — thì độ phức tạp mà nó mang lại sẽ lớn hơn giá trị nó tạo ra. Chọn công cụ theo bài toán, đừng chọn theo trào lưu.
Hướng dẫn từng bước
Dưới đây là cách tư duy để "đọc hiểu" một cụm Kubernetes khi bạn lần đầu tiếp cận nó, đi từ khái niệm đến dòng chảy vận hành:
- Xác định ranh giới cụm. Hỏi: cụm này có bao nhiêu node? Node nào là Control Plane, node nào là Worker? Trong môi trường managed như GKE, EKS, AKS, Control Plane do nhà cung cấp cloud quản lý hộ, bạn chỉ thấy và trả tiền cho Worker Node.
- Hình dung dòng chảy của một lệnh deploy. Khi bạn gửi một file YAML mô tả 3 replica: file đi tới kube-apiserver → được ghi vào etcd → controller-manager thấy cần tạo 3 Pod → scheduler chọn node cho từng Pod → kubelet trên node đó nhận lệnh và bảo container runtime kéo image về, chạy container.
- Hiểu vai trò của etcd là nguồn sự thật. Mọi thứ bạn thấy khi truy vấn cụm đều phản ánh nội dung trong etcd. Khi bạn thay đổi trạng thái mong muốn, thực chất bạn đang thay đổi một bản ghi trong etcd, và phần còn lại của hệ thống sẽ hòa giải theo.
- Nắm cơ chế reconciliation. Hãy tự nhắc: Kubernetes luôn hỏi "hiện tại vs mong muốn có khớp không?". Nếu bạn xóa một Pod do Deployment quản lý bằng tay, đừng ngạc nhiên khi nó lập tức mọc lại — vì trạng thái mong muốn vẫn là "phải có Pod đó".
- Phân biệt managed và self-hosted. Nếu bạn mới học, hãy bắt đầu với một cụm cục bộ nhẹ như minikube hoặc kind trên máy, hoặc một cụm managed nhỏ trên cloud. Đừng tự dựng và tự vận hành Control Plane bằng tay — đó là công việc chuyên sâu, để dành khi bạn đã vững.
Lỗi thường gặp & mẹo
Nhầm Kubernetes là công cụ thay thế Docker. Không. Docker (và các runtime khác) chịu trách nhiệm đóng gói và chạy từng container. Kubernetes điều phối hàng loạt container đó trên nhiều máy. Hai lớp bổ trợ cho nhau, không loại trừ.
Nghĩ theo lối imperative trong một hệ thống declarative. Người mới hay muốn "SSH vào node sửa tay". Đừng. Mọi thay đổi phải đi qua trạng thái mong muốn (file YAML, API). Sửa tay trực tiếp trên node sẽ bị reconciliation loop ghi đè, gây ra hiện tượng "tôi sửa rồi mà nó cứ quay về cũ".
Coi thường etcd. Đây là điểm chết chí mạng của cụm. Trong môi trường tự vận hành, không backup etcd định kỳ là đùa với lửa. Mất etcd đồng nghĩa mất toàn bộ trạng thái cụm.
Dựng Kubernetes khi chưa cần. Như ví dụ 3, đừng dùng dao mổ trâu để giết gà. Hãy hỏi trung thực: mình có thực sự có bài toán quy mô mà Kubernetes giải quyết không?
Mẹo ghi nhớ kiến trúc: Coi Control Plane như "ban giám đốc ra quyết định" và Worker Node như "công nhân thực thi". API Server là lễ tân duy nhất tiếp nhận mọi yêu cầu, etcd là kho hồ sơ, scheduler là người phân công việc, kubelet là quản đốc tại từng phân xưởng. Hình dung này giúp bạn nhớ vai trò từng thành phần mà không cần học thuộc.
Mẹo học lộ trình: Đừng cố hiểu hết mọi thành phần cùng lúc. Ở bài này, chỉ cần nắm chắc hai ý: (1) vì sao cần orchestration, và (2) dòng chảy Control Plane → Worker. Các đối tượng cụ thể như Deployment, Service, ConfigMap sẽ được xử lý riêng ở những bài tiếp theo.
Bài tập thực hành
- Bài toán quy mô: Giả sử công ty bạn có 30 microservices, mỗi service cần 2 replica ở dev, 2 ở staging, 4 ở production. Tính tổng số container đang chạy. Sau đó viết ra 4 công việc vận hành mà bạn sẽ không thể làm thủ công ở quy mô đó, và giải thích Kubernetes gánh mỗi việc đó bằng thành phần nào.
- Vẽ sơ đồ kiến trúc: Bằng giấy hoặc công cụ vẽ, phác lại một cụm gồm 1 Control Plane và 3 Worker Node. Ghi tên đầy đủ các thành phần trong Control Plane và trong mỗi Worker. Vẽ mũi tên thể hiện dòng chảy khi một lệnh deploy đi từ
kubectlđến khi container thực sự chạy.
- Phân tích tình huống self-healing: Viết một đoạn ngắn (5–7 câu) mô tả điều gì xảy ra, từng bước, khi một Worker Node đột ngột chết. Chỉ rõ thành phần nào phát hiện sự cố, thành phần nào ra quyết định, và thành phần nào thực thi.
- Ra quyết định kiến trúc: Cho 3 dự án — (a) một blog cá nhân, (b) một app fintech 25 service cần uptime cao, (c) một API demo cho khách hàng dùng 1 tuần. Với mỗi dự án, quyết định có nên dùng Kubernetes không và giải thích lý do dựa trên bài toán thực sự, không dựa trên trào lưu.
Tóm tắt
Kubernetes ra đời để giải quyết một bài toán rất cụ thể: điều phối hàng trăm đến hàng nghìn container trên nhiều máy một cách tự động, thứ mà con người và script thủ công không kham nổi. Trái tim của nó là tư duy declarative — bạn mô tả trạng thái mong muốn, còn Kubernetes dùng reconciliation loop để liên tục kéo trạng thái thực tế khớp với mong muốn, mang lại khả năng self-healing.
Về kiến trúc, một cụm chia làm Control Plane (bộ não: API Server là cửa ngõ duy nhất, etcd là nguồn sự thật, scheduler phân công, controller-manager hòa giải) và các Worker Node (kubelet quản đốc, container runtime chạy container, kube-proxy lo mạng). Đơn vị nhỏ nhất được điều phối là Pod, không phải container.
Quan trọng không kém: Kubernetes mạnh nhưng phức tạp. Nó chỉ đáng dùng khi bạn thực sự có bài toán quy mô. Ba ví dụ hôm nay — đêm sale bùng nổ, node chết lúc 2 giờ sáng, và startup game dùng sai công cụ — cho thấy cả sức mạnh lẫn cái giá của nó. Hiểu vững "vì sao" và "kiến trúc" ở bài này, bạn đã sẵn sàng bước vào các đối tượng cụ thể của Kubernetes ở những bài tiếp theo.