Sau bài này bạn làm được: vẽ được sơ đồ tối thiểu của một cluster (control plane + node); giải thích Pod là đơn vị scheduling chứ không phải container; tra cứu component nào chịu trách nhiệm khi pod không được lên lịch.


Kubernetes giải quyết vấn đề gì

Docker giúp bạn đóng gói app vào container. Nhưng khi có 50 container trên 10 máy:

  • Container crash → ai restart?
  • Deploy phiên bản mới → rolling update thế nào?
  • Service A gọi Service B → tìm IP bằng cách nào?
  • Node hết RAM → pod chuyển đi đâu?

Kubernetes là API + trạng thái hợp nhất trả lời tất cả câu hỏi trên. Bạn khai báo trạng thái mong muốn (desired state) bằng YAML, Kubernetes lo đưa thực tế về khớp (reconciliation loop).


Kiến trúc tổng quan

┌─────────────────────────────────────────────────────────┐
│                    CONTROL PLANE                         │
│                                                          │
│  ┌──────────────┐  ┌────────┐  ┌───────────────────┐    │
│  │ kube-apiserver│  │  etcd  │  │ kube-scheduler    │    │
│  └──────┬───────┘  └────────┘  └───────────────────┘    │
│         │                       ┌───────────────────┐    │
│         │                       │controller-manager │    │
│         │                       └───────────────────┘    │
└─────────┼───────────────────────────────────────────────┘
          │  API calls
┌─────────┼──────────────┐   ┌──────────────────────────┐
│  NODE 1 │              │   │  NODE 2                   │
│  ┌──────▼─────┐        │   │  ┌───────────┐           │
│  │  kubelet   │        │   │  │  kubelet   │           │
│  └──────┬─────┘        │   │  └─────┬─────┘           │
│  ┌──────▼─────┐        │   │  ┌─────▼─────┐           │
│  │ kube-proxy │        │   │  │ kube-proxy │           │
│  └────────────┘        │   │  └───────────┘           │
│  ┌─────┐ ┌─────┐      │   │  ┌─────┐ ┌─────┐        │
│  │Pod A│ │Pod B│      │   │  │Pod C│ │Pod D│        │
│  └─────┘ └─────┘      │   │  └─────┘ └─────┘        │
└────────────────────────┘   └──────────────────────────┘

Control plane — bộ não

kube-apiserver

  • Cổng vào duy nhất của cluster. Mọi thứ (kubectl, kubelet, controller) đều nói chuyện qua API server.
  • Xác thực (authentication), phân quyền (authorization), admission control, rồi ghi vào etcd.
  • Stateless — có thể chạy nhiều replica phía sau load balancer.

etcd

  • Key-value store lưu toàn bộ trạng thái cluster: pod, service, configmap, secret…
  • Nguồn sự thật duy nhất (single source of truth). Mất etcd = mất cluster.
  • Chỉ API server nói chuyện trực tiếp với etcd.

kube-scheduler

  • Theo dõi pod mới chưa có node (.spec.nodeName trống).
  • Chọn node phù hợp dựa trên: resource requests, affinity/anti-affinity, taint/toleration, topology.
  • Chỉ gán node — không tạo container. Việc tạo container là của kubelet.

kube-controller-manager

  • Chạy nhiều controller trong một process: Deployment controller, ReplicaSet controller, Node controller, Job controller…
  • Mỗi controller watch một loại resource và reconcile trạng thái thực về trạng thái mong muốn.
  • Ví dụ: Deployment controller thấy replicas: 3 nhưng chỉ có 2 pod → tạo thêm 1.

Node — nơi workload chạy

kubelet

  • Agent chạy trên mỗi node. Nhận pod spec từ API server, gọi container runtime (containerd, CRI-O) để tạo/dừng container.
  • Báo cáo trạng thái node và pod về API server.
  • Chạy probe (liveness, readiness, startup) để kiểm tra container health.

kube-proxy

  • Duy trì network rules trên node để Service hoạt động.
  • Mode phổ biến: iptables (mặc định), IPVS (scale lớn), hoặc eBPF (Cilium thay thế hoàn toàn kube-proxy).
  • Không proxy traffic trực tiếp — chỉ cấu hình kernel networking rules.

Container runtime

  • Kubernetes không chạy container trực tiếp — giao cho runtime qua CRI (Container Runtime Interface).
  • Phổ biến: containerd (mặc định từ K8s 1.24+, Docker shim đã bị loại bỏ), CRI-O.
  • Runtime lo pull image, tạo cgroup, mount filesystem, start process.

Pod — đơn vị scheduling

Pod không phải container. Pod là:

  • Một hoặc nhiều container cùng chia sẻ network namespace (cùng IP, cùng port space) và volume.
  • Đơn vị scheduling — scheduler đặt cả pod lên một node, không tách container ra các node khác nhau.
  • Có IP riêng trong cluster (do CNI cấp).
apiVersion: v1
kind: Pod
metadata:
  name: my-app
  namespace: default
spec:
  containers:
  - name: app
    image: my-app:1.2.0
    ports:
    - containerPort: 8080
    resources:
      requests:
        cpu: "100m"
        memory: "128Mi"

Thực tế: bạn hiếm khi tạo Pod trực tiếp. Bạn tạo Deployment (bài 04) — nó tạo ReplicaSet → ReplicaSet tạo Pod.


Request flow: từ kubectl apply đến container chạy

1. kubectl apply -f deploy.yaml
2. kube-apiserver
   - Authenticate (kubeconfig token/cert)
   - Authorize (RBAC)
   - Admission (mutating → validating webhooks)
   - Persist Deployment object → etcd
3. Deployment controller (trong controller-manager)
   - Thấy Deployment mới → tạo ReplicaSet
   - ReplicaSet controller → tạo Pod objects (chưa có nodeName)
4. kube-scheduler
   - Thấy Pod chưa có node → filter + score → gán nodeName
   - Update Pod object trong etcd qua API server
5. kubelet trên node được gán
   - Watch thấy Pod mới cho node mình
   - Gọi containerd: pull image, tạo container
   - Báo cáo trạng thái: Running / Failed / Pending
6. kube-proxy (hoặc CNI)
   - Cập nhật network rules cho Service nếu Pod match selector

Điểm quan trọng: mỗi bước là asynchronous. Không có “transaction” từ đầu đến cuối. Controller A tạo object, Controller B watch và phản ứng — đây là mô hình level-triggered reconciliation.


Namespace — phân vùng logic

Namespace không phải cluster riêng. Nó là:

  • Phân vùng tên: hai Service cùng tên api có thể tồn tại ở namespace khác nhau.
  • Ranh giới cho RBAC, ResourceQuota, NetworkPolicy.
  • Không cách ly network mặc định — pod ở namespace A vẫn reach được pod ở namespace B (trừ khi có NetworkPolicy — bài 10).
# Namespace mặc định của cluster
kubectl get ns
# NAME              STATUS   AGE
# default           Active   30d
# kube-system       Active   30d    ← control plane components
# kube-public       Active   30d    ← readable by all
# kube-node-lease   Active   30d    ← node heartbeat

# Tạo namespace
kubectl create ns staging

# Làm việc trong namespace
kubectl get pods -n staging
kubectl config set-context --current --namespace=staging

Object API — mọi thứ là resource

Kubernetes quản lý mọi thứ qua resource objects với cấu trúc chung:

apiVersion: apps/v1          # API group + version
kind: Deployment              # Loại resource
metadata:
  name: my-app                # Tên (unique trong namespace)
  namespace: default
  labels:                     # Key-value để filter/select
    app: my-app
    version: v1
spec:                         # Trạng thái mong muốn (bạn khai báo)
  replicas: 3
  # ...
status:                       # Trạng thái thực (K8s cập nhật)
  availableReplicas: 3
  # ...

spec là cái bạn viết. status là cái K8s báo lại. Controller reconcile statusspec.


Labels và selectors

Labels là cơ chế kết nối giữa các objects:

  • Deployment chọn Pod qua label selector.
  • Service chọn Pod qua label selector.
  • NetworkPolicy chọn Pod qua label selector.
# Deployment tạo Pod với label app=my-api
spec:
  selector:
    matchLabels:
      app: my-api
  template:
    metadata:
      labels:
        app: my-api

# Service route traffic tới Pod có label app=my-api
spec:
  selector:
    app: my-api

Sai label = traffic không tới, scaling không hoạt động. Đây là nguồn lỗi phổ biến nhất cho người mới.


Lab nhanh: cluster local với kind

# Cài kind (Kubernetes IN Docker)
# macOS: brew install kind
# Linux: go install sigs.k8s.io/kind@latest

# Tạo cluster
kind create cluster --name dev

# Kiểm tra
kubectl cluster-info
kubectl get nodes
kubectl get pods -A    # -A = all namespaces

# Xem components control plane
kubectl get pods -n kube-system

# Dọn dẹp
kind delete cluster --name dev

Tóm tắt

  • Kubernetes = desired state + reconciliation loops. Bạn khai báo, controller lo hiện thực.
  • Control plane (apiserver, etcd, scheduler, controller-manager) là bộ não; node (kubelet, kube-proxy, runtime) là tay chân.
  • Pod là đơn vị scheduling, không phải container. Một pod có thể chứa nhiều container cùng network/volume.
  • Mọi thứ là API object với spec (mong muốn) và status (thực tế).
  • Labels + selectors kết nối objects với nhau — sai label là nguồn lỗi số 1.

Câu hỏi hay gặp

etcd mất dữ liệu thì cluster có chạy được không?

Trả lời: Pod đang chạy vẫn chạy (kubelet giữ container), nhưng bạn không thể tạo/sửa/xoá bất kỳ object nào, không thể schedule pod mới, Service không cập nhật. Thực tế cluster “đứng hình”. Backup etcd định kỳ là bắt buộc.

Có thể chạy control plane trên một node duy nhất không?

Trả lời: Có — đó là cách minikube, kind, k3s hoạt động. Production thường chạy 3+ control plane node cho HA: etcd cần quorum (2/3 hoặc 3/5), apiserver stateless nên scale ngang dễ dàng.

Docker đã bị loại khỏi Kubernetes?

Trả lời: Dockershim bị loại từ K8s 1.24 (2022). Container runtime giờ là containerd hoặc CRI-O. Image Docker (OCI format) vẫn hoạt động bình thường — chỉ runtime shim thay đổi, không ảnh hưởng Dockerfile hay image registry.


Bài tiếp theo (Giai đoạn I): kubectl, ngữ cảnh và namespace — pattern thao tác hằng ngày trên cluster.