Lesson: One-Shot and Scheduled Work
What you'll learn
- The difference between work that runs forever and work that runs once and finishes.
- How a Job runs a task to completion and retries on failure.
- How
completionsandparallelismlet a Job run many times. - How a CronJob schedules Jobs on a timetable.
Skill gained: you can run batch and scheduled tasks on the cluster instead of long-running services.
The lesson
Deployments and StatefulSets assume your container should run forever — if it exits, that is a failure to fix. But lots of real work is the opposite: a database backup, a data import, a report generation. It runs, finishes, and should stop. For that, Kubernetes gives you Jobs and CronJobs.
1. Run-forever vs run-to-completion
This is the key mental split.
Deployment / StatefulSet Job / CronJob
------------------------- -------------------------
container exits = problem container exits 0 = success
restart it forever don't restart on success
"a service" "a task"
If you ran a backup script as a Deployment, Kubernetes would "helpfully" restart it the instant it finished, looping forever. A Job understands that finishing is the goal.
2. The Job
A Job runs one or more Pods until a set number of them complete successfully, then stops. If a Pod fails (exits non-zero or crashes), the Job retries it up to backoffLimit times.
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
backoffLimit: 4 # retry a failing Pod up to 4 times
template:
spec:
restartPolicy: Never # required for Jobs: Never or OnFailure
containers:
- name: pi
image: perl:5.40
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(200)"]
Note restartPolicy: Never — a Job's Pods may not use the default Always, because "always restart" contradicts "run once."
kubectl apply -f pi.yaml
kubectl get jobs # COMPLETIONS goes 0/1 -> 1/1
kubectl logs job/pi # see the result
kubectl get pods # the Pod stays in Completed state
kubectl delete job pi # cleanup (also removes its Pods)
A completed Pod sticks around (in Completed status) so you can read its logs. It is not running and uses no CPU.
3. completions and parallelism
By default a Job runs one Pod to success. Two fields let you scale that:
completions— how many successful Pods you need in total.parallelism— how many run at the same time.
apiVersion: batch/v1
kind: Job
metadata:
name: import
spec:
completions: 6 # need 6 successful runs total
parallelism: 2 # but only 2 at a time
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: busybox:1.36
command: ["sh", "-c", "echo processing chunk; sleep 5"]
completions: 6, parallelism: 2
time -->
[ pod1 ][ pod3 ][ pod5 ]
[ pod2 ][ pod4 ][ pod6 ]
^ Job complete (6/6)
This is the classic "work queue / batch" pattern: chop a big job into N pieces and process P at a time.
4. The CronJob
A CronJob creates a Job on a repeating schedule, using the same cron syntax you may know from Linux. Use it for backups, nightly reports, periodic cleanup.
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 2 * * *" # every day at 02:00
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: busybox:1.36
command: ["sh", "-c", "echo backing up at $(date)"]
The five schedule fields are: minute, hour, day-of-month, month, day-of-week. A few you will use often:
"*/5 * * * *" every 5 minutes
"0 * * * *" top of every hour
"0 2 * * *" daily at 02:00
"0 3 * * 0" Sundays at 03:00
kubectl apply -f backup.yaml
kubectl get cronjob # see SCHEDULE and LAST SCHEDULE
kubectl get jobs # each run creates a Job named backup-<timestamp>
kubectl create job --from=cronjob/nightly-backup test-now # trigger one immediately to test
Two useful CronJob fields:
successfulJobsHistoryLimit/failedJobsHistoryLimit— how many old Jobs to keep (so they do not pile up).concurrencyPolicy: Forbid— skip a new run if the previous one is still going (good for backups that must not overlap).
5. When to use which
One-off task, run now -> Job
Same task on a schedule -> CronJob (which creates Jobs)
Process N items, P at a time -> Job with completions + parallelism
Long-running service / API -> Deployment (NOT a Job)
A simple test: ask "should this container ever exit on its own?" If yes, it is a Job/CronJob. If no, it should run until I stop it, it is a Deployment.
6. Watching and cleaning up
kubectl get jobs -w # -w watches live as COMPLETIONS changes
kubectl describe job import # events, completion counts, failures
kubectl logs job/import # logs (picks one of the Pods)
kubectl delete cronjob nightly-backup # stops scheduling and removes its Jobs
CronJobs accumulate completed Jobs over time. Set the history limits, or you will see dozens of old Completed Pods cluttering kubectl get pods. That is harmless but messy — keeping the history small is good hygiene.
Dig deeper
Search terms
kubernetes job vs deploymentkubernetes job completions parallelismkubernetes cronjob schedule syntaxkubectl create job from cronjobkubernetes cronjob concurrencyPolicy forbid
Check yourself
- Why would running a backup script as a Deployment cause an endless loop?
- What
restartPolicyvalues are allowed for a Job's Pods, and why notAlways? - If
completions: 6andparallelism: 2, how many Pods run at once and how many must succeed total? - Write a
schedulestring that runs every day at 2 a.m. - How do you trigger a CronJob immediately to test it without waiting for the schedule?
No comments to display
No comments to display