Sau bài này bạn làm được: chọn đúng workload type (Deployment, Job, CronJob, DaemonSet, StatefulSet) cho từng use case; cấu hình CronJob không bị duplicate; debug Job failed/stuck.
Bản đồ workload types
┌──────────────┐
│ Deployment │ ← Long-running, stateless, scale horizontal
└──────────────┘
┌──────────────┐
│ StatefulSet │ ← Long-running, stateful, ordered identity
└──────────────┘
┌──────────────┐
│ DaemonSet │ ← Một pod mỗi node (hoặc subset node)
└──────────────┘
┌──────────────┐
│ Job │ ← Chạy đến hoàn thành, rồi dừng
└──────────────┘
┌──────────────┐
│ CronJob │ ← Job theo lịch (cron)
└──────────────┘
| Khi nào | Dùng |
|---|---|
| API server, web app, microservice | Deployment |
| Database, message queue (cần stable network ID + ordered deploy) | StatefulSet (bài 12) |
| Log agent, monitoring agent, kube-proxy | DaemonSet |
| Migration, ETL, report generation | Job |
| Nightly backup, hourly cleanup | CronJob |
Job
Cơ bản
Job tạo pod và đảm bảo pod chạy đến thành công (exit code 0).
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
spec:
template:
spec:
containers:
- name: migrate
image: my-app:1.2.0
command: ["./migrate", "up"]
restartPolicy: OnFailure # Hoặc Never
backoffLimit: 4 # Tối đa 4 lần retry
activeDeadlineSeconds: 300 # Timeout 5 phút cho toàn Job
Completion modes
| Field | Ý nghĩa |
|---|---|
completions | Số pod cần thành công (mặc định 1) |
parallelism | Số pod chạy đồng thời (mặc định 1) |
# Parallel job: 10 task, 3 chạy cùng lúc
spec:
completions: 10
parallelism: 3
Indexed job (K8s 1.24+ GA): mỗi pod nhận JOB_COMPLETION_INDEX (0, 1, 2, …) — dùng để chia partition:
spec:
completionMode: Indexed
completions: 5
parallelism: 5
backoffLimit và retry
Khi pod fail:
- kubelet restart container (nếu
restartPolicy: OnFailure). - Nếu pod fail hoàn toàn (Never) hoặc exceed restart → Job controller tạo pod mới.
- Tổng số lần fail đạt
backoffLimit→ Job báo Failed.
Retry backoff: 10s, 20s, 40s, … (exponential, cap 6 phút).
activeDeadlineSeconds
Timeout cho toàn bộ Job (kể cả retry). Job quá hạn → tất cả pod bị kill, Job status = Failed.
TTL sau hoàn thành
spec:
ttlSecondsAfterFinished: 3600 # Xoá Job + pod sau 1 giờ
Không đặt → Job và pod tồn tại mãi (dù Completed). Sẽ tích dồn trong namespace.
CronJob
CronJob = Job factory chạy theo lịch.
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 2 * * *" # 2:00 AM mỗi ngày
timeZone: "Asia/Ho_Chi_Minh" # K8s 1.27+ GA
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:3.0
command: ["./backup.sh"]
restartPolicy: OnFailure
backoffLimit: 2
ttlSecondsAfterFinished: 7200
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
startingDeadlineSeconds: 200
Schedule syntax
# ┌───────────── minute (0–59)
# │ ┌───────────── hour (0–23)
# │ │ ┌───────────── day of month (1–31)
# │ │ │ ┌───────────── month (1–12)
# │ │ │ │ ┌───────────── day of week (0–6, Sun=0)
# │ │ │ │ │
# * * * * *
"*/5 * * * *" # Mỗi 5 phút
"0 */6 * * *" # Mỗi 6 giờ
"0 2 * * 1" # 2:00 AM thứ Hai
concurrencyPolicy
| Policy | Hành vi |
|---|---|
Allow (mặc định) | Cho phép nhiều Job chạy cùng lúc |
Forbid | Skip lần trigger mới nếu Job trước chưa xong |
Replace | Kill Job cũ, tạo Job mới |
Cảnh báo: Allow + Job chạy lâu → tích dồn Job → tốn resource. Dùng Forbid cho hầu hết trường hợp.
startingDeadlineSeconds
Nếu CronJob bị miss (controller tắt, cluster maintenance), K8s retry nếu chưa quá deadline. Nếu miss > startingDeadlineSeconds → bỏ qua lần đó.
Nếu 100+ lần bị miss liên tiếp → CronJob tự dừng. Kiểm tra Events.
DaemonSet
DaemonSet đảm bảo mỗi node (hoặc subset) chạy đúng một pod.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.0
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
tolerations: # Chạy cả trên control plane node
- operator: Exists
Use case phổ biến
- Log collector: Fluent Bit, Fluentd, Vector.
- Monitoring agent: node-exporter, Datadog agent.
- Network plugin: Calico, Cilium (thường do addon cài).
- Storage driver: CSI node plugin.
DaemonSet vs Deployment
| DaemonSet | Deployment | |
|---|---|---|
| Số pod | 1 per node (tự động) | Bạn chỉ định replicas |
| Scale | Theo số node | replicas hoặc HPA |
| Node selector | Hỗ trợ | Hỗ trợ |
| Rolling update | Có (per node) | Có (per pod) |
Giới hạn chạy trên subset node
spec:
template:
spec:
nodeSelector:
node-type: gpu # Chỉ chạy trên node có label này
# Hoặc dùng affinity cho logic phức tạp hơn
Update strategy
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # Update 1 node tại một thời điểm
# type: OnDelete # Chỉ update khi pod bị xoá thủ công
So sánh tổng hợp
| Deployment | StatefulSet | DaemonSet | Job | CronJob | |
|---|---|---|---|---|---|
| Mục đích | Long-running stateless | Long-running stateful | Per-node agent | Run-to-completion | Scheduled batch |
| Replicas | Chỉ định | Chỉ định | Auto per node | completions | Từ Job template |
| Stable identity | Không | Có (ordinal, DNS) | Không | Không | Không |
| Scale | HPA/manual | Manual | Theo node | parallelism | N/A |
| Restart | Always | Always | Always | OnFailure/Never | OnFailure/Never |
Debug Job/CronJob
# Xem Job status
kubectl get jobs
kubectl describe job db-migrate
# Xem pod của Job
kubectl get pods -l job-name=db-migrate
# Log pod
kubectl logs <job-pod-name>
# CronJob: xem lần chạy
kubectl get cronjobs
kubectl describe cronjob nightly-backup
# Xem Job được CronJob tạo
kubectl get jobs -l app=nightly-backup
# CronJob không chạy? Kiểm tra:
# 1. Events (startingDeadlineSeconds quá, > 100 missed)
# 2. suspend: true?
# 3. Schedule syntax sai?
Tóm tắt
- Job: chạy đến completion.
backoffLimit+activeDeadlineSecondslà safety net. - CronJob: Job theo lịch. Luôn đặt
concurrencyPolicy: Forbid,ttlSecondsAfterFinished, vàstartingDeadlineSeconds. - DaemonSet: 1 pod per node — cho agent cần chạy trên mọi node.
- Chọn workload type theo bản chất: long-running → Deployment/StatefulSet, batch → Job/CronJob, per-node → DaemonSet.
Câu hỏi hay gặp
Job pod Completed nhưng không bị xoá — có vấn đề gì không?
Trả lời: Bình thường — pod Completed giữ lại để bạn xem log. Đặt ttlSecondsAfterFinished để tự dọn. Không đặt → tích dồn hàng nghìn pod Completed trong namespace, kubectl get pods chậm.
CronJob chạy đúp (2 Job cùng lúc) — cách phòng?
Trả lời: Đặt concurrencyPolicy: Forbid. Nếu vẫn thấy đúp: kiểm tra startingDeadlineSeconds (miss nhiều → retry) hoặc CronJob controller bug ở K8s version cũ. App nên idempotent bất kể policy.
DaemonSet có bị HPA scale không?
Trả lời: Không. DaemonSet scale theo số node, không theo metric. HPA chỉ hoạt động với Deployment/StatefulSet.
Bài tiếp theo (Giai đoạn III): Service: ClusterIP, NodePort, LoadBalancer — điểm vào ổn định cho pod.