Chương 06 · Containers

Kubernetes — Container Orchestration

K8s đã trở thành standard cloud-native infrastructure. Học từ architecture (control plane + nodes), core resources (Pod/Deployment/Service/Ingress), networking, storage, ConfigMap/Secret, Helm, autoscaling, troubleshooting kubectl. Đầy đủ cho mức "tự deploy app production".

1. Vì sao Kubernetes?

Container chạy 1 process tốt rồi. Chạy 100 container trên 10 server, có vấn đề:

  • Container nào chạy trên server nào? Cân bằng tải thế nào?
  • Server fail thì container đó chuyển đi đâu?
  • Update version không downtime thế nào?
  • Lưu state (DB) ở đâu?
  • Service A gọi service B — IP gì? Service B move thì sao?
  • Khi traffic tăng, tự thêm container? Bao nhiêu?

Kubernetes (K8s)orchestrator giải quyết tất cả. Định nghĩa state mong muốn (declarative), K8s liên tục đảm bảo thực tế khớp.

1.1. Lịch sử ngắn

  • 2003-2014: Google develop Borg nội bộ — chạy mọi service Google trên cluster.
  • 2014: Google open source Kubernetes (Greek "helmsman") — re-design Borg cho công chúng.
  • 2015: CNCF nhận K8s làm project flagship.
  • 2018+: K8s thắng container war (vs Docker Swarm, Mesos). Mọi cloud provider managed K8s.

1.2. K8s mang lại gì?

Self-healing
Auto
Container fail → restart; node die → reschedule
Auto-scaling
HPA/VPA/CA
Theo CPU/RAM/custom metric
Service Discovery
DNS
my-svc.namespace.svc.cluster.local
Rolling Update
Zero downtime
Built-in deployment strategy

1.3. Khi nào KHÔNG cần K8s?

  • 1-3 service nhỏ → Docker Compose / 1 VM đủ.
  • Stateless web đơn giản → Vercel / Cloud Run / App Engine.
  • Team chưa có kỹ năng vận hành K8s → managed alternative.

K8s rất mạnh nhưng complexity tax cao. "K8s pour les vrais besoins" — chỉ dùng khi thực sự cần.

2. Kubernetes Architecture

┌─────────────────────────────── Control Plane (Master) ─────────────────────────────┐ │ │ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐│ │ │ API Server │←──│ Scheduler │ │ Controller │ │ etcd ││ │ │ (kube-apiserver) │ │ Manager │ │ (key-value DB)││ │ │ │ │ │ │ │ │ ││ │ └────────┬───────┘ └────────────────┘ └────────────────┘ └────────────────┘│ │ ↑ │ └───────────┼────────────────────────────────────────────────────────────────────────┘ │ kubectl, REST API │ ┌───────────┴────────────── Worker Node 1 ──────────────────┐ ┌── Worker Node 2 ──┐ │ │ │ │ │ ┌────────────┐ ┌────────────┐ ┌─────────────────────┐ │ │ (similar) │ │ │ kubelet │ │ kube-proxy │ │ Container Runtime │ │ │ │ │ │ │ │ │ │ (containerd) │ │ │ │ │ └─────┬──────┘ └────────────┘ └──────────┬──────────┘ │ │ │ │ │ │ │ │ │ │ ↓ ↓ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ │ │ Pods │ │ │ │ │ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │ │ │ │ │ Pod │ │ Pod │ │ Pod │ ... │ │ │ │ │ │ │ Cont │ │ Cont │ │ Cont │ │ │ │ │ │ │ └────────┘ └────────┘ └────────┘ │ │ │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────┘ └───────────────────┘

2.1. Control Plane components

ComponentVai trò
kube-apiserverREST API entry point — mọi tương tác qua đây
etcdKey-value DB lưu toàn bộ cluster state (single source of truth)
kube-schedulerQuyết định Pod chạy trên Node nào (filter + score)
kube-controller-managerChạy controller: ReplicaSet, Deployment, Node, Endpoint, ...
cloud-controller-managerTích hợp cloud (LoadBalancer, Volume, Route)

2.2. Worker Node components

ComponentVai trò
kubeletAgent trên mỗi node, nhận lệnh từ API server, quản lý Pod local
kube-proxyNetwork proxy — implement Service abstraction (iptables/IPVS rules)
Container Runtimecontainerd / CRI-O — chạy container thật

2.3. Reconciliation loop

Tâm điểm K8s: desired state vs actual state.

  1. User: kubectl apply -f deployment.yaml với replicas: 3.
  2. API server lưu desired state vào etcd.
  3. Deployment controller thấy desired = 3, actual = 0 → tạo ReplicaSet.
  4. ReplicaSet controller thấy desired = 3 Pod, actual = 0 → tạo 3 Pod.
  5. Scheduler thấy 3 Pod chưa có node → assign nodes.
  6. Kubelet trên node thấy Pod assigned → start container qua containerd.
  7. Liên tục: nếu Pod fail → controller tạo Pod mới.

Đây là declarative: bạn nói "tôi muốn 3 Pod chạy", không nói "start Pod 1, start Pod 2...". K8s tự duy trì.

2.4. Local cluster cho lab

  • minikube — single-node K8s trong VM/container.
  • kind (Kubernetes IN Docker) — multi-node K8s trong Docker container.
  • k3d — k3s (lightweight K8s) trong Docker.
  • Docker Desktop — built-in K8s tab.
# Setup kind
kind create cluster --name dev
kubectl cluster-info
kubectl get nodes

# Setup minikube
minikube start --cpus=4 --memory=8g
minikube dashboard

3. kubectl — CLI duy nhất bạn cần biết

# Cluster info
kubectl cluster-info
kubectl get nodes
kubectl version

# Get resources
kubectl get pods                        # default namespace
kubectl get pods -A                     # all namespaces
kubectl get pods -n kube-system
kubectl get pods -o wide                # show node, IP
kubectl get pods -o yaml                # full YAML
kubectl get pods -l app=web             # filter label

# Apply / Create / Delete
kubectl apply -f deployment.yaml
kubectl apply -f .                      # all yaml in dir
kubectl delete -f deployment.yaml
kubectl delete pod my-pod

# Describe — full detail + events
kubectl describe pod my-pod

# Logs
kubectl logs my-pod                     # log container
kubectl logs my-pod -c sidecar          # specific container
kubectl logs -f my-pod                  # follow
kubectl logs --tail=100 my-pod
kubectl logs --previous my-pod          # log của Pod trước (sau crash)

# Exec
kubectl exec -it my-pod -- sh
kubectl exec my-pod -- env              # 1 lệnh

# Port-forward
kubectl port-forward pod/my-pod 8080:80
kubectl port-forward svc/my-svc 8080:80

# Edit
kubectl edit deployment myapp           # mở editor, save → apply

# Rollout
kubectl rollout status deployment/myapp
kubectl rollout history deployment/myapp
kubectl rollout undo deployment/myapp
kubectl rollout restart deployment/myapp

# Context (multi-cluster)
kubectl config get-contexts
kubectl config use-context prod
kubectl config set-context --current --namespace=staging

# Aliases (dev productivity)
alias k=kubectl
alias kgp='kubectl get pods'
alias kdp='kubectl describe pod'
alias kaf='kubectl apply -f'
Phải cài (1) kubectx + kubens — switch context/namespace nhanh. (2) k9s — TUI quản lý cluster. (3) stern — multi-pod log streaming.

4. Pod — đơn vị nhỏ nhất

Pod = nhóm 1 hoặc nhiều container chia sẻ network (cùng IP), storage. Đơn vị nhỏ nhất K8s deploy/scale.

4.1. Pod đơn giản

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: web
spec:
  containers:
    - name: nginx
      image: nginx:1.25-alpine
      ports:
        - containerPort: 80
      resources:
        requests:               # đảm bảo có
          cpu: 100m              # 100 millicore = 0.1 CPU
          memory: 128Mi
        limits:                  # max
          cpu: 500m
          memory: 256Mi
      readinessProbe:
        httpGet:
          path: /health
          port: 80
        initialDelaySeconds: 5
        periodSeconds: 10
      livenessProbe:
        httpGet:
          path: /alive
          port: 80
        initialDelaySeconds: 30
        periodSeconds: 30
kubectl apply -f pod.yaml
kubectl get pod nginx-pod
kubectl describe pod nginx-pod
kubectl port-forward pod/nginx-pod 8080:80

4.2. Lifecycle phase

PhaseNghĩa
PendingPod accepted, container chưa chạy (image pulling, scheduling)
RunningPod bound to node, ít nhất 1 container running
SucceededTất cả container exit 0 (Job/CronJob)
FailedÍt nhất 1 container exit khác 0
UnknownMất liên lạc với kubelet

4.3. Probes

ProbeVai tròFail action
readinessProbeContainer có sẵn sàng nhận traffic?Remove khỏi Service endpoint
livenessProbeContainer có còn sống?Restart container
startupProbeContainer đã startup?Hoãn liveness/readiness, kill nếu timeout

Probe types: httpGet, tcpSocket, exec, grpc.

4.4. Multi-container patterns

  • Sidecar — container phụ (log shipper, mesh proxy).
  • Init container — chạy trước main, setup môi trường.
  • Ambassador — proxy outbound traffic.
  • Adapter — chuẩn hóa output cho monitoring.
apiVersion: v1
kind: Pod
metadata:
  name: with-init-and-sidecar
spec:
  initContainers:                # chạy tuần tự trước main
    - name: wait-for-db
      image: busybox
      command: ['sh', '-c', 'until nc -z db 5432; do sleep 2; done']
  containers:
    - name: app
      image: myapp:1.0
    - name: log-shipper            # sidecar
      image: fluentbit:2.0
      volumeMounts:
        - name: logs
          mountPath: /logs
  volumes:
    - name: logs
      emptyDir: {}

4.5. Pod KHÔNG phải đơn vị bạn nên tạo trực tiếp

Pod không có self-healing — nếu Pod chết, không tự tạo lại. Dùng Deployment (mục 5) để được self-healing + scaling + rolling update.

5. Workload Resources

5.1. Deployment — cho stateless app

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1                  # tối đa 1 Pod thêm tạm
      maxUnavailable: 0             # tối thiểu 3 Pod available
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: ghcr.io/me/myapp:1.5.0
          ports:
            - containerPort: 3000
          env:
            - name: NODE_ENV
              value: production
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: db-url
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 1000m
              memory: 512Mi
          readinessProbe:
            httpGet: { path: /health, port: 3000 }
            periodSeconds: 5
          livenessProbe:
            httpGet: { path: /alive, port: 3000 }
            periodSeconds: 30

Quan hệ: Deployment → ReplicaSet → Pod.

  • Deployment quản lý ReplicaSet (định nghĩa template).
  • ReplicaSet đảm bảo N Pod chạy.
  • Update template → Deployment tạo ReplicaSet mới, scale up từ từ, scale down ReplicaSet cũ (rolling).

5.2. StatefulSet — cho stateful (DB, queue)

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres-headless
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:15
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:           # mỗi Pod có PVC riêng
    - metadata:
        name: data
      spec:
        accessModes: [ReadWriteOnce]
        resources:
          requests:
            storage: 10Gi

Khác Deployment:

  • Pod có tên cố định: postgres-0, postgres-1, postgres-2.
  • Mỗi Pod có PVC riêng, persistent qua restart.
  • Start tuần tự (postgres-0 → postgres-1 → postgres-2).
  • Stable network identity (DNS).

Use case: Postgres replica, Kafka, ElasticSearch, Cassandra.

5.3. DaemonSet — 1 Pod mỗi node

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: log-collector
spec:
  selector:
    matchLabels:
      app: fluent-bit
  template:
    metadata:
      labels:
        app: fluent-bit
    spec:
      containers:
        - name: fluent-bit
          image: fluent/fluent-bit:2.0
          volumeMounts:
            - name: varlog
              mountPath: /var/log
              readOnly: true
      volumes:
        - name: varlog
          hostPath:
            path: /var/log

Use case: log shipper, monitoring agent, network proxy (Calico, Cilium).

5.4. Job & CronJob

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
spec:
  backoffLimit: 3
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: migrate
          image: myapp:1.5.0
          command: ["npm", "run", "db:migrate"]

---
apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
spec:
  schedule: "0 2 * * *"           # 2 AM daily
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: backup-tool:1.0
              command: ["./backup.sh"]

6. Service & Networking

Pod IP ephemeral (đổi khi restart). Cần abstraction stable → Service.

6.1. Service types

TypeExposeUse case
ClusterIP (default)Internal cluster onlyService-to-service
NodePortMở port (30000-32767) trên mỗi nodeDev, on-prem
LoadBalancerCloud LB (ELB, GLB) → external IPPublic app
ExternalNameDNS CNAMEExternal service alias

6.2. Service example

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: ClusterIP
  selector:
    app: web                          # match Pod label
  ports:
    - port: 80                        # service port
      targetPort: 3000                # container port
      protocol: TCP

DNS resolution: web.default.svc.cluster.local — hay ngắn gọn web nếu cùng namespace.

6.3. Endpoint & Endpoints Slice

kubectl get endpoints web
# NAME   ENDPOINTS                              AGE
# web    10.244.0.5:3000,10.244.1.6:3000,10.244.2.7:3000   5m

Endpoints = list IP của Pods match Service selector và pass readiness probe. Service load balance round-robin (kube-proxy implementation).

6.4. Headless Service (clusterIP: None)

apiVersion: v1
kind: Service
metadata:
  name: postgres-headless
spec:
  clusterIP: None                       # headless
  selector:
    app: postgres
  ports:
    - port: 5432

Headless: không có cluster IP, DNS query trả về tất cả Pod IP. StatefulSet dùng để truy cập trực tiếp postgres-0, postgres-1.

6.5. Network Policy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-allow-only-app
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: web              # chỉ web pods
      ports:
        - protocol: TCP
          port: 5432

Mặc định K8s allow all. NetworkPolicy phải có CNI plugin support (Calico, Cilium). Cần thiết cho zero-trust networking.

7. Ingress & Gateway API

Service expose 1 LB cho mỗi service → tốn IP, đắt. Ingress = single LB route nhiều service theo host/path (L7).

7.1. Ingress example

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-service
                port:
                  number: 80

Cần Ingress Controller implement spec. Phổ biến: nginx-ingress, Traefik, HAProxy, AWS ALB Controller, GCE Ingress.

7.2. Gateway API — successor

Gateway API (GA 2023) là successor của Ingress với role-oriented design:

  • GatewayClass — admin define type LB.
  • Gateway — admin tạo LB instance.
  • HTTPRoute, TCPRoute, GRPCRoute — app team route traffic.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
spec:
  parentRefs:
    - name: prod-gateway
  hostnames:
    - app.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: api-service
          port: 80

Gateway API support traffic split (canary), header-based routing, mTLS — mạnh hơn Ingress.

8. ConfigMap & Secret

8.1. ConfigMap — config phi-secret

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: info
  FEATURE_NEW_UI: "true"
  database.yml: |
    host: db.production.svc.cluster.local
    pool_size: 10
# Sử dụng trong Deployment
spec:
  containers:
    - name: app
      image: myapp:1.0
      envFrom:
        - configMapRef:
            name: app-config
      # Hoặc mount as files:
      volumeMounts:
        - name: config
          mountPath: /etc/app
  volumes:
    - name: config
      configMap:
        name: app-config

8.2. Secret — sensitive data

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  db-password: c2VjcmV0MTIz          # base64 encoded
  api-key: YWJjZGVmMTIzNDU2
# Tạo từ CLI
kubectl create secret generic app-secrets \
  --from-literal=db-password=secret123 \
  --from-literal=api-key=abcdef123456

# Tạo từ file
kubectl create secret generic tls-cert \
  --from-file=tls.crt=cert.pem \
  --from-file=tls.key=key.pem
Secret không secure mặc định! K8s Secret chỉ base64-encoded, không encrypted. Anyone có RBAC read namespace có thể decode. Để secure thật:
  • Enable etcd encryption-at-rest (KMS).
  • RBAC chặt: chỉ ServiceAccount của app đọc được Secret.
  • External Secret Operator (sync từ Vault/AWS Secrets Manager).
  • Sealed Secrets (Bitnami) — encrypt secret trong git.

8.3. External Secrets Operator

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secrets
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: app-secrets                  # K8s Secret tạo ra
  data:
    - secretKey: db-password
      remoteRef:
        key: prod/db
        property: password

Source of truth là AWS Secrets Manager / Vault / GCP Secret Manager. Operator sync vào K8s Secret.

9. Volumes & Persistent Storage

9.1. Volume types

  • emptyDir — temp, sống cùng Pod.
  • hostPath — mount path từ node (avoid trừ DaemonSet).
  • configMap / secret — mount config files.
  • PersistentVolumeClaim (PVC) — chuẩn cho persistent.
  • Cloud-specific: awsElasticBlockStore, gcePersistentDisk, azureDisk (legacy, dùng PVC qua CSI thay).

9.2. PV / PVC / StorageClass

PVC (Pod request) ─→ StorageClass ─→ Provisioner ─→ PV (real disk) "tôi muốn 10GiB SSD" "gp3, ext4" "EBS CSI driver" "vol-abc123"
# StorageClass (admin tạo, thường có sẵn từ cloud)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: ebs.csi.aws.com         # AWS EBS CSI
parameters:
  type: gp3
  iops: "3000"
  throughput: "125"
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer

---
# PVC (app request)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 10Gi

9.3. Access modes

ModeNghĩa
ReadWriteOnce (RWO)Mount read-write 1 node
ReadOnlyMany (ROX)Read-only nhiều node
ReadWriteMany (RWX)Read-write nhiều node (cần NFS, EFS, FSx)
ReadWriteOncePod (RWOP)Chỉ 1 Pod (K8s 1.27+)

Block storage (EBS) chỉ RWO. File storage (EFS, NFS) hỗ trợ RWX.

10. Namespace & RBAC

10.1. Namespace — virtual cluster

kubectl create namespace production
kubectl create namespace staging

kubectl get pods -n production
kubectl config set-context --current --namespace=production

# Default namespaces:
# default, kube-system, kube-public, kube-node-lease

Tách team, env, app. Resource cùng tên có thể tồn tại ở nhiều namespace.

10.2. ResourceQuota & LimitRange

# Giới hạn tổng resource trong namespace
apiVersion: v1
kind: ResourceQuota
metadata:
  name: prod-quota
  namespace: production
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    persistentvolumeclaims: "10"
    pods: "50"

---
# Default request/limit cho Pod thiếu khai báo
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:
        cpu: 500m
        memory: 512Mi
      defaultRequest:
        cpu: 100m
        memory: 128Mi

10.3. RBAC — Role-Based Access Control

4 components:

  • Role / ClusterRole — set quyền (verb + resource).
  • RoleBinding / ClusterRoleBinding — gán Role cho subject.
  • ServiceAccount — identity cho Pod (dùng RBAC).
  • User / Group — identity người (qua OIDC, cert).
# Role chỉ trong namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: pod-reader
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: production
subjects:
  - kind: User
    name: alice@example.com
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRole/ClusterRoleBinding hoạt động cluster-wide. Dùng khi cần grant cho cluster-level resource (Node, PV).

Built-in ClusterRoles: cluster-admin, admin, edit, view.

11. Autoscaling — HPA, VPA, Cluster Autoscaler

11.1. HPA — Horizontal Pod Autoscaler

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 3
  maxReplicas: 50
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70       # scale up khi avg CPU > 70%
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
        - type: Percent
          value: 100
          periodSeconds: 30            # tối đa double mỗi 30s
    scaleDown:
      stabilizationWindowSeconds: 300  # 5 phút cool-down
      policies:
        - type: Pods
          value: 1
          periodSeconds: 60

HPA cần metrics-server install trong cluster. Custom metric (RPS, queue length) cần Prometheus Adapter.

11.2. VPA — Vertical Pod Autoscaler

VPA điều chỉnh CPU/RAM request của Pod (không phải số replica). 3 mode:

  • Off — chỉ recommend, không apply.
  • Initial — set khi tạo Pod.
  • Auto — restart Pod với resource mới.

VPA và HPA không xài chung trên cùng metric (CPU). Có thể HPA on CPU + VPA recommend mode.

11.3. Cluster Autoscaler (CA)

CA scale node (VM) — khi Pod không schedule được do thiếu node, thêm node. Khi node idle, remove.

  • AWS: AWS Karpenter (modern, tốt hơn CA cũ).
  • GCP: GKE Autopilot (managed) hoặc node pool autoscaler.
  • Azure: AKS cluster autoscaler.

11.4. KEDA — Kubernetes Event-Driven Autoscaler

KEDA scale dựa trên event source: Kafka lag, RabbitMQ queue, Cron, Prometheus metric, AWS SQS, ... Bao gồm scale-to-zero (HPA chỉ scale down min replica = 1).

12. Helm — Package Manager cho K8s

Vấn đề: deploy 1 app cần 5-10 YAML files (Deployment, Service, Ingress, ConfigMap, Secret, HPA, ...). Multiply by env (dev/staging/prod) → trùng lặp + drift.

Helm = template engine + package manager. Output: "Chart" (template + values).

12.1. Cài và dùng chart có sẵn

brew install helm                    # macOS

# Add repo
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# Search
helm search repo nginx

# Install
helm install my-nginx bitnami/nginx \
  --namespace web --create-namespace \
  --set service.type=LoadBalancer

# List
helm list -A

# Upgrade
helm upgrade my-nginx bitnami/nginx --reuse-values --set image.tag=1.26

# Rollback
helm history my-nginx
helm rollback my-nginx 2

# Uninstall
helm uninstall my-nginx -n web

12.2. Tạo chart

helm create myapp
# myapp/
# ├── Chart.yaml         ← metadata
# ├── values.yaml        ← default values
# ├── templates/         ← K8s YAML với Go template
# │   ├── deployment.yaml
# │   ├── service.yaml
# │   ├── ingress.yaml
# │   ├── _helpers.tpl
# │   └── ...
# └── charts/            ← sub-chart dependency
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
  labels:
    {{- include "myapp.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "myapp.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.targetPort }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

# values.yaml
replicaCount: 3
image:
  repository: ghcr.io/me/myapp
  tag: "1.5.0"
service:
  type: ClusterIP
  port: 80
  targetPort: 3000
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi
# Test render
helm template myapp ./myapp --values prod-values.yaml | kubectl apply --dry-run=client -f -

# Lint
helm lint ./myapp

# Package
helm package ./myapp                  # myapp-0.1.0.tgz

# Install với values custom
helm install web ./myapp -f prod-values.yaml

12.3. Helm vs Kustomize

ToolApproachProsCons
Helm Template (Go) Package distribution, rollback, hooks YAML có {{ }} khó đọc, complexity cao
Kustomize Patch (overlay) Pure YAML, built-in kubectl, đơn giản Hạn chế logic, không packaging

Phổ biến: Helm cho external chart (ingress, monitoring stack); Kustomize cho own app.

13. Troubleshooting — Workflow chuẩn

13.1. Pod không Run

# Step 1: status
kubectl get pod my-pod
# CrashLoopBackOff / ImagePullBackOff / Pending / Error

# Step 2: describe — events tell story
kubectl describe pod my-pod
# Events:
#   Warning  Failed   Failed to pull image "myapp:99.0": not found
#   Warning  Failed   Error: ImagePullBackOff

# Step 3: logs
kubectl logs my-pod
kubectl logs my-pod --previous       # log Pod cũ trước crash

# Step 4: shell vào (nếu Pod up)
kubectl exec -it my-pod -- sh

13.2. Common errors

StatusNghĩaHành động
PendingChưa schedule được Poddescribe → events: insufficient cpu/memory? PVC pending?
ImagePullBackOffPull image failTag đúng? Registry credentials?
CrashLoopBackOffContainer crash, K8s exponential backoff restartlogs --previous, fix bug
OOMKilledVượt memory limit, kernel killTăng memory limit, fix leak
ErrorContainer exit non-zerologs
EvictedNode OOM, K8s evict PodTăng resource request, capacity

13.3. Service không route

# Service tồn tại?
kubectl get svc

# Endpoints có Pod IP?
kubectl get endpoints my-svc
# Nếu trống → label mismatch

# Pod có label đúng?
kubectl get pods --show-labels

# Pod ready?
kubectl get pod my-pod
# Nếu READY 0/1 → readinessProbe fail

# Network từ Pod khác
kubectl run -it --rm debug --image=nicolaka/netshoot -- bash
# Trong netshoot:
nslookup my-svc
curl my-svc:80

13.4. Debugging tools

  • k9s — TUI explore cluster.
  • stern — multi-pod log streaming: stern app=web.
  • kubectl-debug / ephemeral container — attach debug container vào Pod đang chạy.
  • nicolaka/netshoot — image full network tools (dig, curl, tcpdump, ...).
  • kubectl-trace — bpftrace trên node.

14. Bài tập

  1. Setup local K8s: cài kind/minikube. Verify kubectl get nodes.
  2. First Pod: deploy nginx Pod với resource limit, readiness/liveness probe. Verify probe fail → Pod restart.
  3. Deployment + rolling update: deploy 3 replicas. Update image tag. Watch rolling update với kubectl rollout status. Rollback.
  4. Service + DNS: tạo Deployment + ClusterIP Service. Spawn debug Pod, verify DNS my-svc.default.svc.cluster.local, curl được.
  5. Ingress: cài nginx-ingress (helm). Tạo Ingress route 2 service (api + web) qua 1 host. Truy cập từ browser.
  6. ConfigMap + Secret: ConfigMap chứa app config; Secret chứa DB password. Mount vào Deployment qua envFrom + volumeMount.
  7. StatefulSet Postgres: deploy 3-replica Postgres với StatefulSet + PVC. Restart Pod, verify data persist.
  8. HPA: setup HPA cho web deployment, target CPU 70%. Stress test bằng kubectl run -it --rm --image=busybox -- /bin/sh -c "while true; do wget -q -O- http://web; done". Watch replica scale up.
  9. Helm chart: tạo chart cho app của bạn. values.yaml có dev / staging / prod. helm install lên cluster với từng values.
  10. RBAC: tạo ServiceAccount "cicd" với Role chỉ deploy được trong namespace "production". Test bằng kubectl auth can-i.
  11. Network policy: enforce: pods app=db chỉ accept ingress từ pods app=api. Verify từ pods khác không kết nối được.
  12. Troubleshooting drill:
    • Tạo Pod với image "nginx:9.99" (không tồn tại). Diagnose ImagePullBackOff.
    • Tạo Pod với memory limit 50Mi, app dùng 200Mi. Verify OOMKilled.
    • Tạo Service với label selector sai. Tìm root cause "no endpoints".
  13. Compare: cài cùng app bằng (a) raw YAML, (b) Helm, (c) Kustomize. Đánh giá ergonomics.

15. Quiz

Quiz cuối Chương 6

K8s đơn vị nhỏ nhất bạn deploy là:

  • Container
  • Image
  • Pod (1 hoặc nhiều container chia sẻ network/storage)
  • Service
Pod là smallest deployable unit. 1 Pod thường = 1 container chính (+ optional sidecar). Pod chia sẻ network namespace (cùng IP, localhost), volumes. K8s không deploy container đơn lẻ — luôn đóng gói vào Pod.

Ngôn ngữ K8s là declarative, có nghĩa:

  • Bạn khai báo "tôi muốn 3 Pod chạy", controller tự duy trì (reconciliation loop)
  • Phải viết bằng Go
  • Mọi command imperative
  • Chỉ dùng được YAML
Declarative: define desired state, K8s đảm bảo actual state khớp. Controller liên tục reconcile (compare desired vs actual, take action). Nếu Pod chết, controller tạo mới. Nếu node fail, scheduler chuyển Pod sang node khác. Đối lập với imperative: bạn chạy command từng bước.

readinessProbe vs livenessProbe khác nhau:

  • Cả 2 đều restart container
  • readiness fail → remove khỏi Service endpoint (no traffic); liveness fail → restart container
  • readiness check ngoại tuyến
  • Giống nhau
readiness: "sẵn sàng nhận traffic chưa?" — fail thì kube-proxy remove Pod IP khỏi Service endpoint, traffic không đi tới. Liveness: "có sống không?" — fail thì kubelet kill container và restart. Use case: app warmup chậm dùng readiness; app deadlock dùng liveness; app khởi động lâu dùng startupProbe.

Service type ClusterIP nghĩa là:

  • Mở port public
  • External LoadBalancer
  • Virtual IP chỉ trong cluster, dùng cho service-to-service
  • DNS alias
ClusterIP (default): IP chỉ reachable từ trong cluster, dùng cho microservice nội bộ. NodePort: mở port trên mỗi node (dev/on-prem). LoadBalancer: cloud LB tạo external IP. ExternalName: DNS CNAME alias service ngoài cluster.

Deployment vs StatefulSet khác:

  • StatefulSet nhanh hơn
  • Deployment chỉ cho dev
  • StatefulSet không scale được
  • StatefulSet có Pod tên cố định (postgres-0, postgres-1), PVC riêng mỗi Pod, start/stop tuần tự — cho stateful workload (DB, queue)
Deployment: Pod có hash random tên (web-7dc-abc1, web-7dc-def2), interchangeable. StatefulSet: stable identity, persistent storage, ordered deployment. Use cases StatefulSet: Postgres, Cassandra, ElasticSearch, Kafka. Modern alternative: vẫn ưu tiên Deployment + cloud-managed DB nếu có thể.

K8s Secret KHÔNG secure mặc định vì:

  • Chỉ base64-encode (không encrypt). Anyone có RBAC read namespace decode được. Cần enable etcd encryption-at-rest + RBAC chặt + dùng external secret manager
  • Bị plaintext trong YAML
  • Không có security
  • Mặc định public
Base64 không phải encryption. kubectl get secret -o yaml + base64 decode = plaintext. Defense: (1) etcd encryption-at-rest với KMS key; (2) RBAC: chỉ ServiceAccount của Pod đọc được; (3) External Secrets Operator sync từ Vault/AWS SM (source of truth không ở K8s); (4) Sealed Secrets cho commit secret encrypted vào Git (GitOps).

HPA scale dựa trên metric mặc định nào?

  • Disk usage
  • CPU + memory utilization từ metrics-server (cần install)
  • Số request
  • Network traffic
HPA mặc định scale theo CPU/memory utilization (target Utilization avg). Cần metrics-server trong cluster để collect metric. Custom metric (RPS, queue length, p99 latency) cần Prometheus Adapter hoặc external metrics. KEDA mở rộng cho event-driven scale (Kafka lag, SQS, ...).

Helm chart phù hợp với:

  • Replace Kubernetes
  • Chỉ deploy 1 Pod
  • Package manager — distribute, version, install/upgrade/rollback application với template + values
  • Service mesh
Helm: template + values → render YAML → apply. Lợi: package distribution (helm repo), version + history (helm history), rollback (helm rollback). Use cases: install third-party (nginx-ingress, prometheus-stack); package own app cho multi-env. Alternative: Kustomize (pure YAML overlay) — đơn giản nhưng ít features.

Pod ở status "CrashLoopBackOff" có nghĩa:

  • Image pull failed
  • Pending schedule
  • Healthy
  • Container start xong rồi crash; K8s restart, lặp đi lặp lại với exponential backoff (10s, 20s, 40s, ... max 5 phút)
CrashLoopBackOff = restart loop. Cause: app exit non-zero (bug, missing config, can't connect DB). Debug: kubectl logs --previous xem log Pod crash trước đó. Common: missing env var (DB_URL không có); permission denied (file user khác); OOM (limit thấp).

Khi nào KHÔNG nên dùng Kubernetes?

  • Big enterprise
  • App nhỏ 1-3 service, team chưa quen K8s — Docker Compose / 1 VM / serverless (Cloud Run) đủ; K8s complexity tax không xứng
  • Microservice
  • Multi-cloud
K8s mạnh nhưng đắt: setup, vận hành, learning curve. Ngưỡng "đáng đầu tư": ~10+ services hoặc cần features (autoscale, self-heal, rolling). Dưới ngưỡng: managed PaaS (Cloud Run, App Runner, Fly.io) hoặc Compose. Câu nói: "K8s là Linux của data center" — powerful nhưng không phải lúc nào cũng cần.

Hoàn thành Chương 6. Tiếp theo: Chương 7 — Infrastructure as Code →