Lesson  in  Kubernetes 101

Jobs and CronJobs

Not everything runs forever: run finite tasks with Jobs and schedule them periodically with CronJobs, understanding completions, backoffLimit and restart policies.

Everything you have deployed so far shares one premise: the process must run forever, and if it exits, something is wrong. But there are tasks whose definition of success is exactly the opposite: finishing. Generating a report, copying a database, processing a batch. For those there are Jobs, and for the ones that also repeat on a schedule, CronJobs.

Work from the dev-machine tab.

Step 1: A Job that finishes well

Create the file job.yaml:

cat << 'EOF' > job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: backups
spec:
  completions: 1
  backoffLimit: 3
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: backups
        image: ghcr.io/iximiuz/labs/nginx:alpine
        command: ["sh", "-c", "echo copiando la db; sleep 5; echo copia completada"]
EOF

The command prints its messages in Spanish, as in the book: "copying the db" and, five seconds later, "copy complete".

The YAML, explained in questions and answers

Why apiVersion: batch/v1?

Jobs and CronJobs live in the batch API group, the one devoted to finite workloads. The third variant you know, after v1 (Pods) and apps/v1 (Deployments).

What does completions count?

How many successful completions the Job needs to consider itself done. With completions: 1, one Pod that finishes well is enough. Higher values are for processing batches in several rounds (and combined with parallelism, in parallel).

What does backoffLimit limit?

The number of retries on failure before marking the Job as failed. Without it, a broken command would retry (with growing waits) up to the default limit of 6.

Why restartPolicy: Never, if in Pods we had never touched it?

Long-running Pods use the default value Always: if the process exits, the kubelet restarts it. That is incompatible with a Job, where exiting is the goal. Jobs only accept Never (each retry is a new Pod) or OnFailure (it retries in the same Pod). Choosing one is mandatory.

And the command that overrides the image's?

Same as you saw in the challenges: it replaces the image's default process. Here it turns a web server into a five-second script, enough to simulate a batch job.

Apply it and watch its whole lifecycle:

kubectl apply -f job.yaml
kubectl get jobs,pods
kubectl logs -l job-name=backups

Notice an important detail: when the Pod finishes, it moves to the Completed state but does not disappear. The Job keeps its Pods so you can read their logs.

Step 2: A CronJob that repeats

A CronJob is a Job factory with a schedule. Create cronjob.yaml:

cat << 'EOF' > cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: reports
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: reports
            image: ghcr.io/iximiuz/labs/nginx:alpine
            command: ["sh", "-c", "date; echo informe de ventas generado"]
EOF

(Its message, "informe de ventas generado", means "sales report generated".)

The YAML, explained in questions and answers

What format does schedule use?

The classic five-field cron format: minute, hour, day of the month, month and day of the week. * * * * * means every minute, ideal for not waiting in a lab; in the real world you will see things like 0 3 * * * (at 3:00 every day).

What is jobTemplate?

A complete embedded Job, just as a Deployment's template embedded a Pod. The resulting chain has three links: the CronJob creates Jobs, and each Job creates Pods.

What happens if a run is still going when the next one is due?

The concurrencyPolicy field, which we do not declare here, decides: its default value Allow permits simultaneous runs. The alternatives are Forbid (skips the new one) and Replace (kills the old one). It is worth knowing before a slow Job teaches it to you the hard way.

Apply it:

kubectl apply -f cronjob.yaml

Now it is time to wait for the next minute. Watch how the Jobs get created:

kubectl get jobs --watch

When the first one appears (named reports-<timestamp>: each Job inherits the name of its CronJob), exit with Ctrl+C and read its log:

kubectl logs -l job-name=<nombre-del-job>
Note

💡 By default, a CronJob keeps the last 3 successful Jobs and the last failed one (successfulJobsHistoryLimit and failedJobsHistoryLimit). Without those limits, an every-minute CronJob would pile up thousands of objects in a few days.

Summary

  • A Job chases successful completions (completions) with a budget of retries (backoffLimit).
  • restartPolicy stops being a detail: in Jobs it is mandatory to choose Never or OnFailure.
  • A CronJob is a template of Jobs with a cron schedule, and concurrencyPolicy governs the overlaps.
  • Completed Pods are not garbage: they are the logs of your tasks.

Before moving on, clean up the CronJob so it does not keep manufacturing Jobs for the rest of the course: kubectl delete cronjob reports.