Vì sao cần Kubernetes, cluster và control plane
Ok, học tiếp đi rồi qua Kubernetes nè.
Mình chốt Docker ở đây và sang Kubernetes (K8s). Đừng bắt đầu bằng YAML. Bắt đầu bằng bài toán. Cả chương dùng một hệ thống chạy xuyên suốt: order-service gọi payment-service, cả hai dùng postgres.
1. Bài toán
Bạn đã biết Docker, nên hoàn toàn có thể chạy tay:
docker run order-service:1.0
docker run payment-service:1.0
docker run payment-service:1.0
Nhưng production có nhiều server:
Server A Server B Server C
├── order ├── payment ├── payment
├── payment ├── order ├── order
└── postgres └── redis └── notification
Server B chết:
Server B ❌
├── payment ❌
├── order ❌
└── redis ❌
- Ai phát hiện?
- Ai start container khác? Start trên server nào?
- Traffic còn gửi vào container chết không?
Đó là bài toán container orchestration.
2. Vấn đề
docker runlà lệnh imperative: "làm hành động này". Nó chạy xong là hết, không ai canh.restart: alwayscủa Docker chỉ cứu container trên một máy. Máy chết thì không có gì chuyển container sang máy khác.- Bạn phải tự chọn server còn trống CPU/RAM, tự sửa danh sách IP cho load balancer, tự deploy version mới từng máy.
3. Giải pháp: Kubernetes quản lý desired state
Bạn không nói "chạy container này ở máy kia". Bạn nói:
Tôi muốn payment-service luôn có 3 replicas.
Developer
│
"Tôi muốn 3 instances
payment-service"
│
▼
Kubernetes
┌────────┼────────┐
▼ ▼ ▼
Node 1 Node 2 Node 3
│ │ │
payment payment payment
K8s liên tục so sánh:
Desired = 3, Actual = 3 → OK
Một instance chết:
Desired = 3, Actual = 2 → 3 != 2 → tạo instance mới → Actual = 3
Vòng lặp "quan sát → so sánh → sửa" này gọi là reconciliation loop / control loop. Bạn sẽ gặp tư duy này ở mọi thứ trong K8s.
Cây đầu tiên cần thuộc: Cluster → Node → Pod → Container
Kubernetes Cluster
├── Node 1 (VM / server, ví dụ EC2)
│ ├── Pod order-service-abc12
│ │ └── Container order-service:1.0
│ └── Pod payment-service-x7k2p
│ └── Container payment-service:1.0
├── Node 2
│ └── Pod payment-service-m9q4z
└── Node 3
└── Pod postgres-0
- Cluster: toàn bộ hệ thống K8s.
- Node: một máy (physical hoặc VM) tham gia cluster.
- Pod: đơn vị nhỏ nhất K8s deploy và quản lý. Không phải container. Thường 1 Pod = 1 app container, nên ban đầu có thể nghĩ gần đúng Docker container ≈ Pod.
Một Pod có thể chứa nhiều container (ví dụ app + sidecar). Các container trong cùng Pod share network namespace, nên gọi nhau qua localhost:
ONE POD
┌────────────────────────────────┐
│ shared network namespace │
│ Container A Container B │
│ :8080 :9000 │
│ └── localhost ───┘ │
└────────────────────────────────┘
Bên trong cluster: control plane và worker node
Ai làm cái việc "so sánh desired với actual"? Đó là control plane.
CONTROL PLANE
┌──────────────────────────────────────────────┐
│ │
│ kubectl ──► kube-apiserver ◄──► etcd │
│ ▲ ▲ │
│ │ │ │
│ scheduler controller-manager │
│ │
└──────────────────┬───────────────────────────┘
│ watch / update (HTTPS)
┌─────────────┼─────────────┐
▼ ▼ ▼
Worker 1 Worker 2 Worker 3
kubelet kubelet kubelet
kube-proxy kube-proxy kube-proxy
containerd containerd containerd
└─ Pods └─ Pods └─ Pods
| Thành phần | Nằm ở | Việc |
|---|---|---|
| kube-apiserver | Control plane | Cửa duy nhất. Mọi thứ (kubectl, kubelet, controller) đều nói chuyện qua API server. Xác thực, phân quyền, validate rồi ghi vào etcd. |
| etcd | Control plane | Key-value store phân tán (Raft). Lưu toàn bộ trạng thái cluster: Deployment, Pod, Secret... Mất etcd = mất cluster, nên phải backup. |
| kube-scheduler | Control plane | Thấy Pod chưa có node thì chọn node phù hợp (đủ CPU/RAM request, affinity, taint). Chỉ chọn, không chạy Pod. |
| kube-controller-manager | Control plane | Chạy nhiều controller: Deployment, ReplicaSet, Node, Job... Mỗi controller là một reconciliation loop. |
| kubelet | Mỗi node | Agent trên node: thấy Pod được gán cho node mình thì gọi container runtime để chạy, chạy probe, báo status về API server. |
| container runtime | Mỗi node | containerd / CRI-O, thực sự pull image và chạy container. |
| kube-proxy | Mỗi node | Cài rule iptables/IPVS để Service IP đi tới Pod (bài 17.4). |
Trên cloud (EKS, AKS, GKE), control plane do nhà cung cấp quản lý. Bạn chủ yếu thấy worker node và Pod.
4. Code từng bước
Bước 1: Image vẫn từ Docker. Node không có image trên laptop bạn, nên phải push lên registry:
docker build -t myregistry/payment-service:1.0 .
docker push myregistry/payment-service:1.0
Bước 2: Viết Pod đầu tiên (chỉ để học)
apiVersion: v1
kind: Pod
metadata:
name: payment-demo
labels:
app: payment-service
spec:
containers:
- name: payment
image: myregistry/payment-service:1.0
ports:
- containerPort: 8080
Bốn phần lặp lại ở mọi resource: apiVersion (API nào), kind (loại resource), metadata (tên, label), spec (desired state).
Bước 3: Apply bằng kubectl
kubectl apply -f pod.yaml # đưa desired state vào cluster
kubectl get pods # xem Pod
kubectl get pods -o wide # thêm IP, node
kubectl describe pod payment-demo # chi tiết + events
kubectl logs payment-demo # log app
kubectl delete pod payment-demo # xóa
Đọc lệnh: kubectl = CLI nói chuyện với API server; apply = "hãy đưa trạng thái này vào cluster"; -f = đọc từ file.
Bước 4: Kiểm tra cluster
kubectl get nodes
kubectl get pods -n kube-system # thành phần hệ thống
kubectl api-resources # các kind có trong cluster
kubectl explain deployment.spec # tra field ngay trong terminal
Thử xóa Pod payment-demo: nó mất luôn, không ai tạo lại. Pod tự viết tay không có controller canh. Đó là lý do production dùng Deployment (bài 17.3).
5. Cách hoạt động: chuyện gì xảy ra khi kubectl apply
kubectl apply -f deployment.yaml (replicas: 3)
│
▼
kube-apiserver ── authn/authz/validate ──► ghi vào etcd
│
▼
Deployment controller (watch) ──► tạo ReplicaSet
│
▼
ReplicaSet controller: desired 3, actual 0 ──► tạo 3 Pod (chưa có node)
│
▼
Scheduler (watch Pod chưa có node) ──► gán Pod vào Node 1, 2, 3
│
▼
kubelet trên mỗi node (watch Pod của mình)
──► containerd pull image, chạy container
──► báo status Running về API server
Không thành phần nào ra lệnh trực tiếp cho thành phần khác. Tất cả watch API server và tự phản ứng. Đó là lý do K8s chịu lỗi tốt: controller restart xong chỉ cần đọc lại state và tiếp tục reconcile.
6. So sánh: Docker và Kubernetes
| Docker | Kubernetes |
|---|---|
| Image | Image vẫn vậy |
| Container | Pod (bọc 1 hoặc nhiều container) |
docker run (imperative) | Deployment + replicas (declarative) |
Tên service trong Compose (db:5432) | Service + DNS (postgres:5432) |
restart: always trên 1 máy | Self-healing trên cả cluster |
environment: | ConfigMap / Secret |
| Volume | PV / PVC |
7. Khi nào nên dùng / không nên dùng
- Nên: nhiều service, nhiều instance, cần self-healing, rolling update, autoscale, nhiều môi trường.
- Không nên: 1–2 app nhỏ, team không có người vận hành. Docker Compose trên một VM hoặc PaaS (Cloud Run, App Service) rẻ và đơn giản hơn. K8s có chi phí học và vận hành thật.
Có Kubernetes rồi thì còn cần Docker không nhỉ?
Cần phân biệt hai thứ: container image và Docker engine.
- Image vẫn cần. Bạn vẫn viết Dockerfile,
docker build, push lên registry. K8s chỉ pull image về chạy. Image theo chuẩn OCI nên tool nào build cũng được (Docker, Buildah, Jib). - Docker engine trên node thì không cần. Từ K8s 1.24, kubelet nói chuyện với runtime qua CRI, thường là containerd. Image build bằng Docker vẫn chạy bình thường trên containerd.
Docker → build/package application (image)
Kubernetes → run/manage/scale application
Helm → package/template K8s manifests (bài 16.8)
Ba công cụ không thay thế nhau.
Vậy control plane chết thì các Pod đang chạy có chết theo không?
Không. Đây là điểm hay bị hiểu sai.
- Container đang chạy trên node vẫn chạy. kubelet và containerd vẫn sống, kube-proxy vẫn giữ rule cũ, nên traffic tới Pod vẫn đi.
- Thứ mất là khả năng thay đổi: không
kubectl applyđược, Pod chết không được tạo lại trên node khác, không scale, không deploy.
Control plane ❌
Data plane (Pods đang chạy) ✅ vẫn phục vụ
Self-healing / scale / deploy ❌ tạm dừng
Vì vậy production chạy control plane HA: 3 node etcd (Raft cần đa số, 3 node chịu được 1 node chết), nhiều API server sau load balancer. Managed K8s lo việc này giúp bạn.
Phỏng vấn hỏi Kubernetes hoạt động thế nào thì trả lời sao?
Q1. Kubernetes là gì, giải quyết vấn đề gì?
🇻🇳 Tiếng Việt:
Kubernetes là nền tảng orchestration cho container. Khi có nhiều service chạy trên nhiều máy, nó lo việc đặt container lên node nào, giữ đúng số replica, tự tạo lại khi container hoặc node chết, cung cấp networking ổn định, rolling update và autoscale. Mình khai báo desired state bằng YAML, Kubernetes liên tục đưa actual state về desired state.
🇬🇧 English:
Kubernetes is a container orchestration platform. When many services run across many machines, it decides which node runs each container, keeps the right number of replicas, recreates containers when they or their node fail, and provides stable networking, rolling updates and autoscaling. I declare the desired state in YAML and Kubernetes keeps reconciling the actual state towards it.
Q2. Kể các thành phần của control plane và vai trò của chúng.
🇻🇳 Tiếng Việt:
API server là cửa vào duy nhất, xác thực và validate request rồi lưu vào etcd. etcd là key-value store phân tán giữ toàn bộ trạng thái cluster. Scheduler chọn node cho Pod mới dựa trên resource request và ràng buộc. Controller manager chạy các controller như Deployment, ReplicaSet, mỗi cái là một vòng lặp reconcile. Trên mỗi worker node có kubelet chạy Pod qua container runtime, và kube-proxy lo routing cho Service.
🇬🇧 English:
The API server is the single entry point; it authenticates and validates requests and stores them in etcd. etcd is a distributed key-value store holding the whole cluster state. The scheduler assigns new Pods to nodes based on resource requests and constraints. The controller manager runs controllers such as Deployment and ReplicaSet, each one a reconciliation loop. On every worker node, the kubelet runs Pods through the container runtime and kube-proxy handles Service routing.
Q3. Điều gì xảy ra khi bạn chạy kubectl apply một Deployment?
🇻🇳 Tiếng Việt:
kubectl gửi manifest lên API server, API server kiểm tra quyền, validate và ghi vào etcd. Deployment controller thấy Deployment mới thì tạo ReplicaSet, ReplicaSet controller tạo các Pod. Scheduler thấy Pod chưa có node thì gán node. kubelet trên node đó thấy Pod của mình thì gọi containerd pull image và chạy container, rồi báo trạng thái về API server. Các thành phần không gọi nhau trực tiếp mà đều watch API server.
🇬🇧 English:
kubectl sends the manifest to the API server, which checks permissions, validates it and stores it in etcd. The Deployment controller sees the new Deployment and creates a ReplicaSet, and the ReplicaSet controller creates the Pods. The scheduler sees unscheduled Pods and binds them to nodes. The kubelet on each node sees its Pods, asks containerd to pull the image and start the containers, and reports status back. Components never call each other directly; they all watch the API server.
Q4. Declarative khác imperative thế nào trong Kubernetes?
🇻🇳 Tiếng Việt:
Imperative là ra lệnh từng bước, như
docker runhaykubectl scale. Declarative là mô tả trạng thái mong muốn, nhưreplicas: 3trong file YAML rồikubectl apply. Kubernetes tự tìm cách đạt trạng thái đó và giữ nó. Em ưu tiên declarative vì file nằm trong Git, review được, và cluster luôn khớp với file thay vì phụ thuộc lệnh ai đó gõ tay.
🇬🇧 English:
Imperative means issuing step-by-step commands such as
docker runorkubectl scale. Declarative means describing the desired state, likereplicas: 3in a YAML file, and runningkubectl apply. Kubernetes figures out how to reach that state and keeps it there. I prefer declarative because the files live in Git, can be reviewed, and the cluster matches them instead of depending on commands someone typed by hand.
Cách nhớ ngắn gọn
- K8s = desired state + reconciliation loop.
- Cluster → Node → Pod → Container. Pod là đơn vị nhỏ nhất.
- API server là cửa duy nhất; etcd giữ state; scheduler chọn node; controller reconcile; kubelet chạy Pod.
- Control plane chết: Pod vẫn chạy, nhưng không tự sửa, không deploy được.
Tự kiểm tra
Thành phần nào chọn node cho Pod? Thành phần nào thực sự chạy container?
Scheduler chọn node. kubelet trên node đó gọi container runtime (containerd) để chạy.
Toàn bộ trạng thái cluster lưu ở đâu?
etcd. Chỉ API server đọc/ghi trực tiếp etcd.
Xóa một Pod tạo bằng kind: Pod thì sao?
Mất luôn, vì không có controller nào giữ số lượng. Dùng Deployment để có self-healing.
Hai container trong cùng Pod gọi nhau bằng địa chỉ nào?
localhost, vì chúng share network namespace.
Image build bằng Docker có chạy được trên node dùng containerd không?
Được. Image theo chuẩn OCI.