Chuyển đến nội dung chính

BÀI 18: JOBS VÀ CRONJOBS

Batch processing với Jobs (single, parallel, indexed, work queue), CronJobs với timezone support (GA K8s 1.27). JobSet (CNCF project) cho nhóm Jobs phụ thuộc nhau — lý tưởng cho AI/ML training pipelines.

🔒 DevSecOps — Bài 18 BÀI 18: JOBS VÀ CRONJOBS

KUBERNETES: TỪ CƠ BẢN ĐẾN NÂNG CAO

Module 5: Workload Management

xdev.asia

Jobs và CronJobs trong Kubernetes

Trong Kubernetes, Deployments và StatefulSets được thiết kế cho các workloads chạy liên tục — chúng luôn cố gắng duy trì một số lượng Pod nhất định. Nhưng nhiều tác vụ trong thực tế không cần chạy mãi mãi: xử lý một batch dữ liệu, chạy database migration, train một model ML, hoặc gửi email hàng loạt. Đây là lúc Jobs và CronJobs phát huy tác dụng.

1. Jobs là gì? Batch Workloads và Run-to-Completion

Một Job trong Kubernetes tạo ra một hoặc nhiều Pods với mục tiêu hoàn thành một tác vụ cụ thể. Khác với Deployment, Job theo dõi số lượng completions thành công — khi đủ số Pod hoàn thành, Job được coi là done.

Đặc điểm quan trọng của Jobs:

  • Run-to-completion: Pod chạy xong và exit với code 0 nghĩa là thành công
  • Retry tự động: Nếu Pod fail, Job tự tạo Pod mới theo backoffLimit
  • Tracking completions: Job biết đã hoàn thành bao nhiêu trong tổng số cần thiết
  • Parallelism: Nhiều Pod có thể chạy song song để tăng throughput

Ví dụ Job đơn giản nhất — tính số Pi:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-calculator
  namespace: default
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
        resources:
          requests:
            cpu: "250m"
            memory: "64Mi"
          limits:
            cpu: "500m"
            memory: "128Mi"
      restartPolicy: Never
  backoffLimit: 4

Lưu ý restartPolicy: Never — với Jobs, bạn chỉ được dùng Never hoặc OnFailure, không được dùng Always.

2. Job Completion Modes

Kubernetes hỗ trợ ba completion modes cho Jobs, phù hợp với các use case khác nhau.

2.1 NonIndexed (Default)

Job hoàn thành khi đủ số completions thành công. Các Pods không có thứ tự — chúng đều làm cùng một công việc và Job cần đủ completions Pod thành công.

apiVersion: batch/v1
kind: Job
metadata:
  name: nonindexed-parallel-job
spec:
  completions: 5        # Cần 5 Pod hoàn thành thành công
  parallelism: 2        # Chạy tối đa 2 Pod cùng lúc
  completionMode: NonIndexed  # Đây là default, có thể bỏ qua
  template:
    spec:
      containers:
      - name: worker
        image: busybox:1.35
        command: ["sh", "-c", "echo Processing task; sleep 10; echo Done"]
      restartPolicy: Never
  backoffLimit: 3

2.2 Indexed Jobs

Indexed Jobs là tính năng rất mạnh — mỗi Pod nhận được một index duy nhất từ 0 đến completions-1 thông qua biến môi trường JOB_COMPLETION_INDEX. Điều này lý tưởng cho data partitioning: mỗi Pod xử lý một phần dữ liệu xác định.

apiVersion: batch/v1
kind: Job
metadata:
  name: indexed-data-processor
spec:
  completions: 10       # 10 partitions
  parallelism: 3        # Xử lý 3 partitions cùng lúc
  completionMode: Indexed
  template:
    spec:
      containers:
      - name: data-processor
        image: python:3.11-slim
        command:
        - python3
        - -c
        - |
          import os
          partition_id = int(os.environ['JOB_COMPLETION_INDEX'])
          total_partitions = 10
          # Xử lý dữ liệu từ partition partition_id
          start = partition_id * 1000
          end = start + 1000
          print(f"Processing records {start} to {end}")
          # ... thực tế sẽ query database hoặc đọc file
        env:
        - name: JOB_COMPLETION_INDEX
          valueFrom:
            fieldRef:
              fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
      restartPolicy: Never
  backoffLimit: 6

Kubernetes tự động inject biến JOB_COMPLETION_INDEX vào mỗi Pod. Pod 0 xử lý partition 0, Pod 1 xử lý partition 1, v.v. — không bao giờ trùng lặp dù có Pod restart.

2.3 Work Queue

Với work queue pattern, nhiều Pod cùng lấy task từ một hàng đợi (Redis, RabbitMQ, SQS). Job hoàn thành khi queue rỗng và không còn Pod nào đang xử lý.

apiVersion: batch/v1
kind: Job
metadata:
  name: queue-worker
spec:
  parallelism: 4    # 4 workers đồng thời
  # completions không set = work queue mode (hoàn thành khi 1 Pod exit 0)
  template:
    spec:
      containers:
      - name: worker
        image: my-queue-worker:v1.2
        env:
        - name: QUEUE_URL
          value: "redis://redis-service:6379/queue:tasks"
        - name: MAX_TASKS
          value: "100"
        resources:
          requests:
            cpu: "500m"
            memory: "256Mi"
      restartPolicy: OnFailure

3. Job Parameters Chi Tiết

Hiểu rõ các parameters của Job giúp bạn tối ưu cho từng use case:

  • completions: Tổng số Pod cần hoàn thành thành công. Mặc định là 1.
  • parallelism: Số Pod tối đa chạy đồng thời. Mặc định là 1.
  • backoffLimit: Số lần retry trước khi Job bị đánh dấu failed. Mặc định là 6.
  • activeDeadlineSeconds: Thời gian tối đa (giây) Job được phép chạy. Vượt quá → Job bị terminate.
  • ttlSecondsAfterFinished: Xóa Job (và Pods) sau N giây kể từ khi hoàn thành.
apiVersion: batch/v1
kind: Job
metadata:
  name: time-limited-job
spec:
  completions: 3
  parallelism: 3
  backoffLimit: 2
  activeDeadlineSeconds: 600    # Job phải xong trong 10 phút
  ttlSecondsAfterFinished: 3600 # Xóa sau 1 giờ
  template:
    spec:
      containers:
      - name: worker
        image: busybox:1.35
        command: ["sh", "-c", "sleep 30 && echo completed"]
      restartPolicy: Never

4. Pod Failure Policies (K8s 1.31+)

Từ Kubernetes 1.31, Pod Failure Policy cho phép bạn định nghĩa hành vi chi tiết khi Pod fail — không phải lúc nào cũng nên retry.

apiVersion: batch/v1
kind: Job
metadata:
  name: job-with-failure-policy
spec:
  completions: 5
  parallelism: 2
  backoffLimit: 6
  podFailurePolicy:
    rules:
    # Nếu Pod exit với code 42 (business error), đừng retry — fail ngay
    - action: FailJob
      onExitCodes:
        containerName: main
        operator: In
        values: [42]
    # Nếu node bị preempt (OOM, spot interruption), ignore và retry
    - action: Ignore
      onPodConditions:
      - type: DisruptionTarget
    # Các lỗi khác: retry như bình thường
    - action: Count
      onExitCodes:
        operator: NotIn
        values: [0, 42]
  template:
    spec:
      containers:
      - name: main
        image: my-batch-processor:v2
        command: ["./process"]
      restartPolicy: Never

Các action có thể dùng:

  • FailJob: Dừng toàn bộ Job ngay lập tức, đánh dấu failed
  • Ignore: Không tính vào backoffLimit, tạo Pod mới
  • Count: Tính vào backoffLimit như bình thường (default behavior)

5. Job TTL — Dọn Dẹp Tự Động

Jobs và Pods của chúng sẽ tồn tại mãi sau khi hoàn thành nếu không có cơ chế dọn dẹp. Dùng ttlSecondsAfterFinished để tự động xóa:

apiVersion: batch/v1
kind: Job
metadata:
  name: cleanup-demo
spec:
  ttlSecondsAfterFinished: 300  # Xóa 5 phút sau khi xong (kể cả failed)
  template:
    spec:
      containers:
      - name: task
        image: busybox:1.35
        command: ["echo", "Hello from Job"]
      restartPolicy: Never

Bạn cũng có thể patch Jobs hiện có: kubectl patch job old-job -p '{"spec":{"ttlSecondsAfterFinished":0}}' — điều này xóa Job ngay lập tức.

6. CronJobs — Lên Lịch Tác Vụ

CronJob tự động tạo Jobs theo lịch định kỳ, sử dụng cú pháp cron quen thuộc.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-report
  namespace: production
spec:
  schedule: "0 2 * * *"    # 2 giờ sáng mỗi ngày
  timeZone: "Asia/Ho_Chi_Minh"   # GA từ K8s 1.27
  concurrencyPolicy: Forbid        # Không chạy job mới nếu job cũ đang chạy
  startingDeadlineSeconds: 300     # Nếu trễ quá 5 phút, bỏ qua
  successfulJobsHistoryLimit: 3    # Giữ 3 successful jobs gần nhất
  failedJobsHistoryLimit: 1        # Giữ 1 failed job gần nhất
  jobTemplate:
    spec:
      ttlSecondsAfterFinished: 86400  # Xóa sau 24 giờ
      template:
        spec:
          containers:
          - name: report-generator
            image: my-report-app:v1.5
            command: ["python", "generate_report.py", "--date", "yesterday"]
            env:
            - name: DB_HOST
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: host
          restartPolicy: OnFailure

6.1 CronJob Timezone Support (GA K8s 1.27)

Trước K8s 1.27, tất cả CronJobs đều dùng UTC của controller. Từ K8s 1.27, timeZone field là GA — bạn có thể chỉ định timezone bất kỳ theo IANA timezone database:

spec:
  schedule: "0 9 * * 1-5"          # 9 giờ sáng thứ 2-6
  timeZone: "Asia/Ho_Chi_Minh"     # Vietnam timezone (UTC+7)

Các timezone phổ biến:

  • Asia/Ho_Chi_Minh — Việt Nam (UTC+7)
  • Asia/Singapore — Singapore (UTC+8)
  • America/New_York — Eastern US
  • Europe/London — UK
  • UTC — Coordinated Universal Time

6.2 ConcurrencyPolicy

Điều quan trọng cần quyết định: phải làm gì nếu một Job cũ chưa xong khi đến lịch chạy Job mới?

  • Allow (default): Tạo Job mới kể cả khi Job cũ đang chạy — cẩn thận với race conditions
  • Forbid: Bỏ qua Job mới, Job cũ vẫn tiếp tục
  • Replace: Xóa Job cũ, tạo Job mới thay thế

7. JobSet — CNCF Project cho Distributed Jobs

JobSet là một CNCF project (hiện đang ở giai đoạn Sandbox) được thiết kế để điều phối nhiều Jobs có quan hệ phụ thuộc nhau. Đây là công cụ lý tưởng cho distributed ML training pipelines.

Cài đặt JobSet:

kubectl apply --server-side -f \
  https://github.com/kubernetes-sigs/jobset/releases/download/v0.7.0/manifests.yaml

7.1 JobSet cho Distributed ML Training

Scenario: Train một model với kiến trúc Parameter Server — một nhóm pods làm parameter server (lưu trữ gradients), một nhóm khác làm workers (tính toán).

apiVersion: jobset.x-k8s.io/v1alpha2
kind: JobSet
metadata:
  name: ml-training-pytorch
  namespace: ml-training
  annotations:
    jobset.sigs.k8s.io/exclusive-topology: kubernetes.io/hostname
spec:
  failurePolicy:
    maxRestarts: 3        # Restart toàn bộ JobSet nếu có failure
  replicatedJobs:
  # Parameter Server: lưu model state, nhận gradients từ workers
  - name: parameter-server
    replicas: 1
    template:
      spec:
        completions: 2
        parallelism: 2
        completionMode: Indexed
        template:
          spec:
            containers:
            - name: ps
              image: pytorch/pytorch:2.2-cuda12.1-cudnn8-runtime
              command: ["python", "train.py", "--role", "ps"]
              env:
              - name: ROLE
                value: "parameter-server"
              - name: JOB_INDEX
                valueFrom:
                  fieldRef:
                    fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
              resources:
                requests:
                  cpu: "4"
                  memory: "16Gi"
            restartPolicy: Never

  # Workers: tính toán gradients, gửi lên PS
  - name: worker
    replicas: 1
    template:
      spec:
        completions: 8      # 8 workers
        parallelism: 8
        completionMode: Indexed
        template:
          spec:
            containers:
            - name: worker
              image: pytorch/pytorch:2.2-cuda12.1-cudnn8-runtime
              command: ["python", "train.py", "--role", "worker"]
              env:
              - name: ROLE
                value: "worker"
              - name: PS_HOSTS
                value: "ml-training-pytorch-parameter-server-0-0.ml-training-pytorch:8080,ml-training-pytorch-parameter-server-0-1.ml-training-pytorch:8080"
              resources:
                requests:
                  cpu: "4"
                  memory: "16Gi"
                  nvidia.com/gpu: "1"
                limits:
                  nvidia.com/gpu: "1"
            restartPolicy: Never

7.2 Tính Năng Nổi Bật của JobSet

  • Failure policy propagation: Nếu một Job trong set fail, toàn bộ JobSet có thể restart hoặc fail cùng — không để Jobs "orphan"
  • DNS-based communication: Các Jobs trong JobSet tự động có DNS records để giao tiếp với nhau ({jobset-name}-{job-name}-{job-index}-{pod-index}.{jobset-name})
  • Exclusive topology: Đảm bảo các Pods của cùng Job được schedule trên cùng rack/node (giảm network latency)
  • Startup sequencing: Chỉ start worker sau khi PS đã sẵn sàng

7.3 Theo Dõi JobSet

# Xem trạng thái JobSet
kubectl get jobset -n ml-training

# Xem chi tiết
kubectl describe jobset ml-training-pytorch -n ml-training

# Xem logs của parameter server
kubectl logs -l jobset.sigs.k8s.io/job-name=parameter-server -n ml-training

# Xem logs của tất cả workers
kubectl logs -l jobset.sigs.k8s.io/job-name=worker -n ml-training --prefix

8. Tổng Kết: Khi Nào Dùng Gì?

  • Job đơn giản: Một tác vụ, chạy một lần — dùng basic Job
  • Parallel processing không cần order: NonIndexed Job với completions + parallelism
  • Data partitioning: Indexed Job — mỗi Pod xử lý partition xác định
  • Queue-based processing: Work queue Job + Redis/RabbitMQ
  • Scheduled tasks: CronJob với timezone support
  • Distributed training/HPC: JobSet cho multi-job coordination

Jobs và CronJobs là nền tảng của mọi batch processing system trên Kubernetes. Hiểu rõ các completion modes và failure policies giúp bạn xây dựng pipelines đáng tin cậy, đặc biệt với AI/ML workloads ngày càng phổ biến.