Jobs#
Everything we’ve deployed so far has been a long-running service. A deployment promises “keep N replicas running at all times”. But a lot of real work is the opposite: run once, finish, exit. Think database backups, schema migrations, or batch processing.
A Job creates one or more pods and keeps retrying them until the specified number of pods successfully terminate.
| Deployment | Job | |
|---|---|---|
| Amaç | Servis sürekli çalışsın | İş tamamlanana kadar çalışsın |
| Pod durumu | Running (olması beklenen) | Completed (başarı sinyali) |
| Çalışma biçimi | Sonsuz döngü | Bitince exit code ile biter |
| Örnek | web, api, crawler | migration, backup, batch |
A minimal job looks like this:
apiVersion: batch/v1
kind: Job
metadata:
name: example-job
spec:
template:
spec:
containers:
- name: example
image: busybox
command: ["echo", "hello from the job"]
restartPolicy: NeverA few important spec fields:
completions: How many pods need to complete successfully (default1)parallelism: How many pods may run concurrently (default1)backoffLimit: How many failed attempts before the Job is markedFailed(default6)restartPolicy: Must beNeverorOnFailure.Alwaysis not allowed for Jobs - a Job pod that would never exit would defeat the point.
Apply one and watch its lifecycle:
kubectl apply -f example-job.yaml
kubectl get jobs --watch
kubectl get podsNotice that the pod will show STATUS: Completed, not Running. That’s the whole point!
To see the output:
kubectl logs job/example-jobCronJobs#
A CronJob creates Jobs on a repeating schedule. If you’ve ever written a crontab, this will feel like home:
apiVersion: batch/v1
kind: CronJob
metadata:
name: example-cronjob
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: example
image: busybox
command: ["echo", "tick"]
restartPolicy: OnFailureCron Formatı Hatırlatması#
* * * * *
│ │ │ │ │
│ │ │ │ └── haftanın günü/günleri (0-6, 0=Pazar)
│ │ │ └──── ay (1-12)
│ │ └────── ayın günü (1-31)
│ └──────── saat (0-23)
└────────── dakika (0-59)*/5 * * * *= her 5 dakikada bir0 */6 * * *= her 6 saatte bir (0. dakikada)0 3 * * *= her gün saat 03:00’te
Two more useful spec fields:
concurrencyPolicy:Allow(default),Forbid(skip if the previous job is still running) orReplace(kill the old one)startingDeadlineSeconds: Max allowed lateness, otherwise the run is skipped
Assignment#
Remember the db.json file that our api writes to its persistent volume (see storage and persistent volumes)? Let’s build a backup CronJob for it. Create api-backup-cronjob.yaml:
apiVersion: batch/v1
kind: CronJob
metadata:
name: api-db-backup
spec:
schedule: "0 */6 * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: busybox:1.36
command:
- sh
- -c
- cp /persist/db.json /backup/db-$(date +%Y%m%d-%H%M%S).json && ls -la /backup && cat /persist/db.json
volumeMounts:
- name: api-data
mountPath: /persist
- name: backup
mountPath: /backup
volumes:
- name: api-data
persistentVolumeClaim:
claimName: synergychat-api-pvc
- name: backup
emptyDir: {}A few things to notice:
- We mount the same PVC the api pod uses. It’s
ReadWriteOnce, and since Minikube is a single-node cluster, multiple pods on that node can mount it. - The backup target is an
emptyDirvolume, which dies with the pod. For this exercise we only verify the backup via logs; in real life you’d push the file to S3/object storage or a dedicated backup PVC. restartPolicy: OnFailuremeans a failed backup attempt will retry.
Apply it:
kubectl apply -f api-backup-cronjob.yaml
kubectl get cronjobsWe don’t want to wait up to 6 hours to see if it works. Manually trigger one job right now:
kubectl create job --from=cronjob/api-db-backup manual-backupThen check the results:
kubectl get jobs
kubectl logs job/manual-backupYou should see the timestamped db-YYYYMMDD-HHMMSS.json file in the listing, and the contents of the messages database in the logs. Congratulations - you now have a scheduleable, retryable backup pipeline in a single YAML file.
for more services