Job & CronJob #
Deployment, StatefulSet, dan DaemonSet semuanya dirancang untuk workload yang berjalan terus-menerus. Tapi tidak semua pekerjaan perlu berjalan selamanya — ada task yang perlu dijalankan sekali, selesai, lalu berhenti: migrasi data, generate report, kirim email batch, proses antrian. Untuk kebutuhan ini, Kubernetes menyediakan Job dan CronJob.
Job #
Job memastikan satu atau lebih Pod berhasil menyelesaikan tugasnya. Berbeda dari Deployment yang me-restart container yang keluar, Job menganggap exit code 0 sebagai keberhasilan dan berhenti membuat Pod baru setelah berhasil.
Struktur Manifest Job #
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
namespace: production
spec:
completions: 1 # jumlah Pod yang harus berhasil selesai
parallelism: 1 # berapa Pod yang berjalan bersamaan
backoffLimit: 3 # maksimum retry jika Pod gagal
activeDeadlineSeconds: 300 # maksimum durasi Job (dalam detik)
template:
spec:
restartPolicy: Never # Never atau OnFailure — BUKAN Always
containers:
- name: migrator
image: my-app:v2
command:
- /app/migrate
- up
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secret
key: DATABASE_URL
resources:
requests:
cpu: "500m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
restartPolicy di Job harus Never atau OnFailure — tidak boleh Always. Dengan Never, jika container gagal Kubernetes membuat Pod baru (sampai backoffLimit tercapai). Dengan OnFailure, Kubernetes me-restart container yang sama dalam Pod yang sama.
Completions dan Parallelism #
Kombinasi dua field ini menentukan pola eksekusi Job:
completions: 1, parallelism: 1 (default)
→ Jalankan 1 Pod, selesai saat Pod berhasil
→ Pola: task tunggal
completions: 5, parallelism: 1
→ Jalankan 1 Pod dalam satu waktu
→ Total 5 Pod harus berhasil (satu per satu)
→ Pola: sequential batch
completions: 10, parallelism: 3
→ Jalankan 3 Pod secara bersamaan
→ Total 10 Pod harus berhasil
→ Pola: parallel batch
completions: tidak diset, parallelism: 3
→ Jalankan 3 Pod bersamaan, Job selesai ketika semua Pod selesai
→ Pola: work queue (setiap Pod mengambil task dari queue)
Contoh: Parallel Batch Processing #
spec:
completions: 100 # proses 100 item
parallelism: 10 # 10 worker bersamaan
backoffLimit: 5
template:
spec:
restartPolicy: Never
containers:
- name: processor
image: my-processor:v1
env:
- name: JOB_COMPLETION_INDEX # Kubernetes inject indeks otomatis (1.21+)
valueFrom:
fieldRef:
fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
# Processor menggunakan JOB_COMPLETION_INDEX untuk menentukan
# item mana yang perlu diproses (item 0-9, 10-19, dst)
Failure Handling #
Jika Pod gagal (exit code != 0):
backoffLimit: 3
→ Retry pertama: langsung
→ Retry kedua: tunggu 10 detik
→ Retry ketiga: tunggu 20 detik (exponential backoff)
→ Setelah 3 kali gagal: Job ditandai Failed
activeDeadlineSeconds: 300
→ Jika Job belum selesai dalam 300 detik: hentikan semua Pod
→ Job ditandai Failed dengan reason: DeadlineExceeded
→ Berguna untuk menghindari Job yang hang selamanya
# Lihat status Job
kubectl get jobs
# Output:
# NAME COMPLETIONS DURATION AGE
# db-migration 1/1 45s 2m ← sukses
# data-cleanup 0/1 3m 3m ← masih berjalan atau gagal
# Detail Job termasuk events
kubectl describe job db-migration
# Lihat log Pod dari Job yang sudah selesai
kubectl logs job/db-migration
Pembersihan Job Otomatis #
Job yang sudah selesai tidak otomatis dihapus — ia tetap ada beserta Pod-nya (dalam status Completed). Ini berguna untuk melihat log, tapi bisa mengotori cluster jika Job sering dijalankan.
spec:
ttlSecondsAfterFinished: 3600 # hapus Job (dan Pod-nya) 1 jam setelah selesai
# ttlSecondsAfterFinished: 0 # hapus langsung setelah selesai
CronJob #
CronJob menjalankan Job secara terjadwal, menggunakan sintaks cron yang sama dengan cron di Linux.
Struktur Manifest CronJob #
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-report
namespace: production
spec:
schedule: "0 2 * * *" # setiap hari jam 02:00
timezone: "Asia/Jakarta" # timezone (Kubernetes 1.25+)
concurrencyPolicy: Forbid # Allow | Forbid | Replace
successfulJobsHistoryLimit: 3 # simpan log 3 Job terakhir yang sukses
failedJobsHistoryLimit: 1 # simpan log 1 Job terakhir yang gagal
jobTemplate: # template Job yang akan dibuat
spec:
activeDeadlineSeconds: 600
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: reporter
image: my-reporter:v1
command:
- /app/generate-report
- --date=$(date +%Y-%m-%d)
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
Sintaks Schedule Cron #
┌──────────── menit (0-59)
│ ┌────────── jam (0-23)
│ │ ┌──────── hari dalam bulan (1-31)
│ │ │ ┌────── bulan (1-12)
│ │ │ │ ┌──── hari dalam minggu (0-7, 0 dan 7 = Minggu)
│ │ │ │ │
* * * * *
Contoh jadwal umum:
"*/5 * * * *" → setiap 5 menit
"0 * * * *" → setiap jam tepat
"0 2 * * *" → setiap hari jam 02:00
"0 2 * * 1" → setiap Senin jam 02:00
"0 2 1 * *" → setiap tanggal 1, jam 02:00
"0 2 * * 1-5" → setiap hari kerja jam 02:00
Concurrency Policy #
CronJob punya field concurrencyPolicy untuk menangani situasi di mana jadwal baru tiba sebelum Job sebelumnya selesai:
Allow (default):
→ Boleh ada beberapa Job berjalan bersamaan
→ Gunakan untuk task yang benar-benar independen
Forbid:
→ Jika Job sebelumnya masih berjalan, jadwal baru di-skip
→ Gunakan untuk task yang tidak boleh berjalan bersamaan
(misalnya: cleanup yang modifikasi database)
Replace:
→ Jika Job sebelumnya masih berjalan, hentikan dan ganti dengan yang baru
→ Gunakan untuk task di mana hanya yang terbaru yang relevan
(misalnya: sync data yang selalu fresh dari awal)
Menjalankan CronJob Secara Manual #
Berguna untuk testing atau menjalankan task di luar jadwal:
# Trigger CronJob manual
kubectl create job --from=cronjob/daily-report manual-run-$(date +%Y%m%d)
# Pantau Job yang berjalan
kubectl get jobs
kubectl logs job/manual-run-20240115
Anti-Pattern: Job untuk Long-Running Service #
ANTI-PATTERN: gunakan Job untuk service yang seharusnya terus berjalan
spec:
template:
spec:
restartPolicy: Never
containers:
- name: api-server # ← SALAH: api server bukan task yang selesai
image: my-api:v1
Masalah:
→ Job selesai ketika container exit → service mati dan tidak dijalankan ulang
→ Jika container crash, Job retry sampai backoffLimit lalu berhenti
→ Tidak ada self-healing seperti Deployment
BENAR: gunakan Deployment untuk long-running service
Ringkasan #
- Job untuk task yang selesai, bukan service — exit code 0 berarti sukses; Kubernetes tidak me-restart Pod yang berhasil selesai.
restartPolicy: NevervsOnFailure—Nevermembuat Pod baru saat gagal (cocok untuk task yang tidak boleh diulang di Pod yang sama),OnFailureme-restart container dalam Pod yang sama.completionsdanparallelism— kombinasi keduanya menentukan pola eksekusi: sequential, parallel, atau work queue.activeDeadlineSecondswajib untuk task produksi — mencegah Job yang hang selamanya karena bug atau resource starvation.ttlSecondsAfterFinisheduntuk pembersihan otomatis — Job yang selesai tidak otomatis dihapus; tetapkan TTL agar cluster tidak penuh dengan Pod Completed.concurrencyPolicy: Forbiduntuk task yang tidak boleh overlap — penting untuk task yang memodifikasi data bersama seperti database cleanup atau report generation.