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àoDùng
API server, web app, microserviceDeployment
Database, message queue (cần stable network ID + ordered deploy)StatefulSet (bài 12)
Log agent, monitoring agent, kube-proxyDaemonSet
Migration, ETL, report generationJob
Nightly backup, hourly cleanupCronJob

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
completionsSố pod cần thành công (mặc định 1)
parallelismSố 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:

  1. kubelet restart container (nếu restartPolicy: OnFailure).
  2. Nếu pod fail hoàn toàn (Never) hoặc exceed restart → Job controller tạo pod mới.
  3. 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

PolicyHành vi
Allow (mặc định)Cho phép nhiều Job chạy cùng lúc
ForbidSkip lần trigger mới nếu Job trước chưa xong
ReplaceKill 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

DaemonSetDeployment
Số pod1 per node (tự động)Bạn chỉ định replicas
ScaleTheo số nodereplicas hoặc HPA
Node selectorHỗ trợHỗ trợ
Rolling updateCó (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

DeploymentStatefulSetDaemonSetJobCronJob
Mục đíchLong-running statelessLong-running statefulPer-node agentRun-to-completionScheduled batch
ReplicasChỉ địnhChỉ địnhAuto per nodecompletionsTừ Job template
Stable identityKhôngCó (ordinal, DNS)KhôngKhôngKhông
ScaleHPA/manualManualTheo nodeparallelismN/A
RestartAlwaysAlwaysAlwaysOnFailure/NeverOnFailure/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 + activeDeadlineSeconds là 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.