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) là 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ì?
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
2.1. Control Plane components
| Component | Vai trò |
|---|---|
| kube-apiserver | REST API entry point — mọi tương tác qua đây |
| etcd | Key-value DB lưu toàn bộ cluster state (single source of truth) |
| kube-scheduler | Quyết định Pod chạy trên Node nào (filter + score) |
| kube-controller-manager | Chạy controller: ReplicaSet, Deployment, Node, Endpoint, ... |
| cloud-controller-manager | Tích hợp cloud (LoadBalancer, Volume, Route) |
2.2. Worker Node components
| Component | Vai trò |
|---|---|
| kubelet | Agent trên mỗi node, nhận lệnh từ API server, quản lý Pod local |
| kube-proxy | Network proxy — implement Service abstraction (iptables/IPVS rules) |
| Container Runtime | containerd / CRI-O — chạy container thật |
2.3. Reconciliation loop
Tâm điểm K8s: desired state vs actual state.
- User:
kubectl apply -f deployment.yamlvới replicas: 3. - API server lưu desired state vào etcd.
- Deployment controller thấy desired = 3, actual = 0 → tạo ReplicaSet.
- ReplicaSet controller thấy desired = 3 Pod, actual = 0 → tạo 3 Pod.
- Scheduler thấy 3 Pod chưa có node → assign nodes.
- Kubelet trên node thấy Pod assigned → start container qua containerd.
- 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'
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
| Phase | Nghĩa |
|---|---|
| Pending | Pod accepted, container chưa chạy (image pulling, scheduling) |
| Running | Pod bound to node, ít nhất 1 container running |
| Succeeded | Tất cả container exit 0 (Job/CronJob) |
| Failed | Ít nhất 1 container exit khác 0 |
| Unknown | Mất liên lạc với kubelet |
4.3. Probes
| Probe | Vai trò | Fail action |
|---|---|---|
| readinessProbe | Container có sẵn sàng nhận traffic? | Remove khỏi Service endpoint |
| livenessProbe | Container có còn sống? | Restart container |
| startupProbe | Container đã 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
| Type | Expose | Use case |
|---|---|---|
| ClusterIP (default) | Internal cluster only | Service-to-service |
| NodePort | Mở port (30000-32767) trên mỗi node | Dev, on-prem |
| LoadBalancer | Cloud LB (ELB, GLB) → external IP | Public app |
| ExternalName | DNS CNAME | External 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
- 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
# 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
| Mode | Nghĩ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
| Tool | Approach | Pros | Cons |
|---|---|---|---|
| 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
| Status | Nghĩa | Hành động |
|---|---|---|
| Pending | Chưa schedule được Pod | describe → events: insufficient cpu/memory? PVC pending? |
| ImagePullBackOff | Pull image fail | Tag đúng? Registry credentials? |
| CrashLoopBackOff | Container crash, K8s exponential backoff restart | logs --previous, fix bug |
| OOMKilled | Vượt memory limit, kernel kill | Tăng memory limit, fix leak |
| Error | Container exit non-zero | logs |
| Evicted | Node OOM, K8s evict Pod | Tă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
- Setup local K8s: cài kind/minikube. Verify
kubectl get nodes. - First Pod: deploy nginx Pod với resource limit, readiness/liveness probe. Verify probe fail → Pod restart.
- Deployment + rolling update: deploy 3 replicas. Update image tag. Watch rolling update với
kubectl rollout status. Rollback. - Service + DNS: tạo Deployment + ClusterIP Service. Spawn debug Pod, verify DNS
my-svc.default.svc.cluster.local, curl được. - Ingress: cài nginx-ingress (helm). Tạo Ingress route 2 service (api + web) qua 1 host. Truy cập từ browser.
- ConfigMap + Secret: ConfigMap chứa app config; Secret chứa DB password. Mount vào Deployment qua envFrom + volumeMount.
- StatefulSet Postgres: deploy 3-replica Postgres với StatefulSet + PVC. Restart Pod, verify data persist.
- 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. - Helm chart: tạo chart cho app của bạn. values.yaml có dev / staging / prod.
helm installlên cluster với từng values. - RBAC: tạo ServiceAccount "cicd" với Role chỉ deploy được trong namespace "production". Test bằng
kubectl auth can-i. - 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.
- 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".
- 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à:
Ngôn ngữ K8s là declarative, có nghĩa:
readinessProbe vs livenessProbe khác nhau:
Service type ClusterIP nghĩa là:
Deployment vs StatefulSet khác:
K8s Secret KHÔNG secure mặc định vì:
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?
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:
Pod ở status "CrashLoopBackOff" có nghĩa:
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?
Hoàn thành Chương 6. Tiếp theo: Chương 7 — Infrastructure as Code →