Phần 3 — Workloads: Pod, ReplicaSet, Deployment, StatefulSet, DaemonSet, Job/CronJob
Ý kiến
0
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Phần cuối series K8s: Cluster API (CAPI) quản lý cluster bằng K8s API, FinOps với OpenCost + Karpenter + right-sizing, AI/ML workloads (GPU, Kubeflow, KServe, vLLM, Argo Workflows, gang scheduling), Edge K8s và WebAssembly.
Multi-tenancy K8s (soft: namespace+RBAC+Quota, HNC, Capsule, vCluster; hard: cluster riêng) và multi-cluster: management cluster, GitOps ApplicationSet, Cluster API, Karmada, Crossplane, observability liên cluster.
Service Mesh chuyên sâu cho K8s: Istio (sidecar + ambient), Linkerd (proxy Rust nhẹ), Cilium Service Mesh (eBPF sidecarless) — mTLS, VirtualService, AuthorizationPolicy, canary, mirror, multi-cluster, đo overhead, khi không nên dùng mesh.
Series Kubernetes Toàn Tập — 13 phần:
Phần 3 — Workloads ← bạn đang đọc
Workload là từ K8s dùng để chỉ application chạy trên cluster. Một workload có thể là 1 component đơn lẻ hoặc nhiều component phối hợp. Đằng sau, mọi workload đều quy về một tập Pod.
K8s cung cấp các workload resource để quản lý Pod thay bạn — bạn không bao giờ nên tạo Pod trực tiếp trong production. Mỗi loại workload resource phù hợp một use case:
| Resource | Use case |
|---|---|
Deployment / ReplicaSet | Stateless app — web server, API, frontend |
StatefulSet | Stateful app — database, Kafka, Elasticsearch |
DaemonSet | 1 pod / node — log agent, network agent, monitoring |
Job | Chạy đến khi xong — migration, batch processing |
CronJob | Job theo lịch — daily backup, weekly report |
ReplicationController | Cha của ReplicaSet, đã legacy, không dùng |
Một Pod đại diện cho một process chạy trên cluster, có thể gồm nhiều container tightly-coupled chia chung:
Network namespace — cùng IP, cùng port space, nói chuyện với nhau qua localhost.
IPC namespace — chia sẻ memory segment.
Volumes — mount cùng path.
Lifecycle — chết cùng nhau.
Best practice: 1 container chính / pod. Pod đa container chỉ dùng cho:
Sidecar — log shipper, proxy, mesh sidecar (Envoy).
Init container — chạy trước, đảm bảo điều kiện trước khi main container start.
Ambassador — proxy gọi service ngoài.
Adapter — chuẩn hoá output, metrics format.
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
Pod có các phase:
Pending — đã accept, đang chờ schedule hoặc pull image.
Running — đã bound node, ít nhất 1 container đang chạy.
Succeeded — mọi container exit 0, không restart.
Failed — ít nhất 1 container exit khác 0, restart đã dừng.
Unknown — không lấy được state (thường node mất liên lạc).
Ngoài phase, pod còn có conditions: PodScheduled, ContainersReady, Initialized, Ready — chính các condition này được Service và Endpoint dùng để quyết định traffic.
| Probe | Tác dụng | Khi fail |
|---|---|---|
livenessProbe | Container có còn sống không? | kubelet kill và restart container |
readinessProbe | Container có sẵn sàng nhận traffic? | Remove khỏi Service endpoints |
startupProbe | Container đã start xong chưa? | Liveness/readiness chưa chạy cho đến khi pass — dùng cho app boot lâu |
3 kiểu probe: httpGet, tcpSocket, exec (chạy command, exit 0 = OK), và mới: grpc.
apiVersion: v1
kind: Pod
metadata:
name: app-with-init
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ['sh', '-c', 'until nc -z postgres 5432; do sleep 2; done']
containers:
- name: app
image: myapp:1.0
Init container chạy tuần tự, mỗi cái phải exit 0 trước khi cái sau start. Mọi init container chạy xong, main container mới start.
ReplicaSet maintain stable set N pod đang chạy. Bạn hiếm khi tạo ReplicaSet trực tiếp — Deployment làm việc đó cho bạn. Nhưng hiểu ReplicaSet rất quan trọng vì Deployment điều khiển ReplicaSet.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-rs
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
Mỗi giây, ReplicaSet controller so sánh:
(số pod hiện có matching selector) vs replicas mong muốn
Thiếu thì tạo thêm, thừa thì xoá bớt.
Deployment là controller bậc cao hơn ReplicaSet, bổ sung:
Rolling update — thay version mới mà không downtime.
Rollback — quay lại version cũ.
Pause/resume — dừng rollout giữa chừng.
Revision history — giữ N ReplicaSet cũ để rollback.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 3
revisionHistoryLimit: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25% # tối đa 25% pod xuống cùng lúc
maxSurge: 25% # tối đa 25% pod thừa khi rollout
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
# Apply
kubectl apply -f nginx.yaml
# Xem rollout status
kubectl rollout status deployment/nginx
# Đổi image — trigger rollout mới
kubectl set image deployment/nginx nginx=nginx:1.28
# Xem lịch sử
kubectl rollout history deployment/nginx
# Rollback về version trước
kubectl rollout undo deployment/nginx
# Rollback về revision cụ thể
kubectl rollout undo deployment/nginx --to-revision=3
# Pause / resume
kubectl rollout pause deployment/nginx
kubectl rollout resume deployment/nginx
# Restart pod (giữ nguyên spec) — hữu ích khi đổi ConfigMap
kubectl rollout restart deployment/nginx
Deployment quản lý nhiều ReplicaSet:
Deployment nginx (replicas=3)
├── ReplicaSet nginx-abc (v1.27) — 0 pod (cũ)
└── ReplicaSet nginx-xyz (v1.28) — 3 pod (mới)
Khi rollout v1.28, K8s tạo ReplicaSet mới với 0 pod, rồi tăng dần pod RS mới, giảm dần pod RS cũ — tôn trọng maxUnavailable và maxSurge.
Nếu app không hỗ trợ chạy đồng thời 2 version (vd. migration schema phá vỡ backward compat), dùng:
spec:
strategy:
type: Recreate
K8s sẽ kill toàn bộ pod cũ trước, rồi mới tạo pod mới. Có downtime, nhưng an toàn schema.
StatefulSet giống Deployment nhưng pod có identity ổn định:
Tên pod cố định: web-0, web-1, web-2 (không random hash).
DNS ổn định: web-0.headless-svc.namespace.svc.cluster.local.
PVC riêng từng pod, gắn theo tên — pod chết và respawn vẫn mount lại volume cũ.
Ordered deploy/scale: pod-0 ready trước mới đến pod-1.
Đây là design cho database, message queue, KV store — nơi mỗi instance có data riêng và peer cần biết identity của nhau.
# Phải có headless Service (clusterIP: None)
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
clusterIP: None
selector:
app: postgres
ports:
- port: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: ssd
resources:
requests:
storage: 10Gi
Sau khi apply, bạn có:
postgres-0 (PVC data-postgres-0, 10Gi)
postgres-1 (PVC data-postgres-1, 10Gi)
postgres-2 (PVC data-postgres-2, 10Gi)
PVC không tự xoá khi delete StatefulSet — đây là feature (data quan trọng). Dùng persistentVolumeClaimRetentionPolicy (K8s 1.27+ stable) nếu muốn auto cleanup.
Update mặc định RollingUpdate theo thứ tự ngược (pod-N trước, pod-0 sau), tôn trọng partition cho canary.
Không dùng StatefulSet nếu app stateless — dùng Deployment đơn giản hơn nhiều.
DaemonSet đảm bảo mọi node (hoặc tập node match selector) có đúng 1 pod. Use case kinh điển:
Log shipper: Fluent Bit, Promtail, Alloy.
Network agent: Cilium agent, Calico-node.
Monitoring: node-exporter, Datadog agent.
Storage: Longhorn, OpenEBS engine.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
hostNetwork: true
hostPID: true
tolerations:
- operator: Exists # chạy cả trên node có taint
containers:
- name: node-exporter
image: prom/node-exporter:v1.8.0
args:
- --path.rootfs=/host
volumeMounts:
- name: root
mountPath: /host
readOnly: true
volumes:
- name: root
hostPath:
path: /
Khi thêm node mới vào cluster, K8s tự tạo pod DaemonSet trên đó.
spec:
template:
spec:
nodeSelector:
node-role.kubernetes.io/storage: "true"
Job tạo Pod chạy đến lúc complete, không restart vô hạn như Deployment.
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
spec:
backoffLimit: 4 # số lần retry trước khi đánh Failed
activeDeadlineSeconds: 600 # timeout toàn job 10 phút
ttlSecondsAfterFinished: 3600 # auto xoá sau 1h
template:
spec:
restartPolicy: OnFailure
containers:
- name: migrate
image: myapp:1.0
command: ['./migrate.sh']
spec:
parallelism: 5 # 5 pod song song
completions: 20 # job hoàn thành khi đã có 20 pod Succeeded
Mode khác: completionMode: Indexed — mỗi pod có index unique (qua env JOB_COMPLETION_INDEX), tốt cho phân vùng dữ liệu.
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 2 * * *" # 2h sáng mỗi ngày, cron syntax
timeZone: "Asia/Ho_Chi_Minh" # K8s 1.27+
concurrencyPolicy: Forbid # không cho run đè khi cái cũ chưa xong
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
startingDeadlineSeconds: 300
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: backup-tool:1.0
command: ['./backup.sh']
concurrencyPolicy:
Allow — mặc định, run đè.
Forbid — skip nếu cái trước chưa xong.
Replace — kill cái cũ, start cái mới.
| Loại | Pod restart | Identity | Storage | Order |
|---|---|---|---|---|
| Deployment | Replace bất kỳ | Random hash | Stateless | Không |
| StatefulSet | Same name | Stable (web-0…) | PVC riêng pod | Có (0→N) |
| DaemonSet | 1 pod / node | Theo node | HostPath / PVC | Không |
| Job | Chạy đến xong | Random | Stateless | Optional (Indexed) |
| CronJob | Schedule | Theo Job | Theo Job | — |
Luôn set resources.requests và limits. Không set = QoS BestEffort, dễ bị evict.
Luôn có readinessProbe. Không có thì Service tưởng pod sẵn sàng ngay khi container start → 502 cho user.
Cẩn thận với livenessProbe. Probe quá nhạy → restart loop. App có warm-up dài thì dùng startupProbe.
Không dùng latest tag. Luôn pin version cụ thể.
Không tạo Pod trực tiếp. Pod chết không có ai tạo lại.
StatefulSet: backup PVC. Delete pod không mất data, nhưng node hỏng + storage không HA thì mất.
PodDisruptionBudget cho workload quan trọng để giới hạn pod down đồng thời khi drain node:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-pdb
spec:
minAvailable: 2 # hoặc maxUnavailable: 1
selector:
matchLabels:
app: nginx
# Pod kẹt Pending? Xem event
kubectl describe pod <name>
# Pod liên tục CrashLoopBackOff? Xem log container trước đó
kubectl logs <pod> --previous
# Xem mọi event mới nhất
kubectl get events --sort-by='.lastTimestamp' -A
# Xem YAML pod cuối cùng K8s đang giữ
kubectl get pod <name> -o yaml
# Exec vào container
kubectl exec -it <pod> -c <container> -- /bin/sh
# Run debug pod cùng namespace
kubectl run debug --rm -it --image=nicolaka/netshoot --restart=Never
# Ephemeral debug container (K8s 1.25+) — attach vào pod đang chạy
kubectl debug -it <pod> --image=nicolaka/netshoot --target=<container>
Sau phần này bạn đã có thể chọn đúng workload resource:
Web/API stateless → Deployment
Database, queue → StatefulSet
Log/metrics agent → DaemonSet
Migration, batch one-off → Job
Backup, report định kỳ → CronJob
Trong Phần 4, ta xem làm sao các workload nói chuyện với nhau và với thế giới bên ngoài qua Service, Ingress, DNS, NetworkPolicy.
← Phần 2 | Phần 4: Networking — Service, Ingress, DNS, NetworkPolicy →