Jobs and Cron Jobs in Kubernetes

14 minute read     Updated:

Mercy Bassey %
Mercy Bassey

We’re Earthly.dev. We make building software simpler and therefore faster using containerization. This article covers jobs and cron jobs in Kubernetes. If you want to know more about building in containers then check us out.

Modern software applications require efficient and reliable management of jobs and scheduled tasks. As applications become more complex and Kubernetes clusters increase in size, automating repetitive tasks like backups, batch processing, and data analysis becomes essential.

Kubernetes provides two critical resources to manage these tasks: Jobs and CronJobs. Jobs are ideal for running one-off or batch tasks, while a CronJob is perfect for scheduling recurring tasks. By using these resources, developers, DevOps engineers, and cluster administrators can focus on more critical tasks and ensure the reliability and efficiency of Kubernetes deployments.

Whether you’re a seasoned developer or a Kubernetes novice, understanding Jobs and CronJobs is essential for efficient and reliable application management. In this tutorial, we’ll explore how to run and schedule tasks with Kubernetes Jobs and CronJobs. You’ll learn how to create (define the desired state), monitor (watch their completion status), and customize these powerful resources in Kubernetes!

Prerequisites

To follow along in this tutorial, you’ll need to have:

  • A Kubernetes cluster up and running (A single node cluster will work fine).
  • This tutorial uses a Linux machine with the Ubuntu 22.04 LTS distro (Any other OS will work fine as long as you have Kubernetes set up).

Overview of Jobs and CronJobs in Kubernetes

Kubernetes Jobs and CronJobs play a critical role in managing and automating tasks in a Kubernetes cluster. They are an essential tool for ensuring the reliability and efficiency of any modern software application.

A Kubernetes Job is a resource that defines a task and ensures that it runs to completion. It creates one or more pods and manages their lifecycle to ensure that the desired number of successful completions is achieved. A CronJob resource creates Job objects on a predefined schedule (daily, weekly, or monthly). Each Job object created by the CronJob runs a specific task or a set of tasks as a Pod. So you simply specify the desired state for a Job or CronJob, and Kubernetes will ensure the desired state is achieved.

In summary, Jobs, and CronJobs are an essential part of Kubernetes, and understanding how to use them can help improve the reliability and efficiency of your Kubernetes deployments.

Creating and Managing Jobs in Kubernetes

As mentioned, Jobs are Kubernetes objects that run a container or pod(s) to completion, either once or as part of a batch process. In this section, we’ll explore how to create and manage jobs in Kubernetes.

Creating a Job Manifest File

Before you can run a job in Kubernetes, you need to create a job manifest file that defines the specifications of the job. This file should be created in YAML format and should include information such as the name of the job, the container image to be used, and the command to run.

To create your first job, create a yaml file, open it up with your preferred code editor, and paste in the following code snippets; this tutorial uses the nano text editor and calls this file job.yaml:

# job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: hello-world
spec:
  template:
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["echo",  "Hello World!"]
      restartPolicy: Never

The code snippet above does the following:

  • Creates a Kubernetes job named hello-world using the batch/v1 API version.
  • Runs a container named busybox with the image busybox.
  • Executes the command echo Hello World! and then exits.

💡 The restartPolicy: Never specifies that the container should not be restarted if it fails or exits.

Understanding the Lifecycle of a Job

When a job is created, Kubernetes creates a pod to run the container image specified in the job manifest file. The pod will continue to run until the container completes successfully or fails. If the container fails, Kubernetes will restart the container based on the job’s specifications until it reaches the completion state.

Now create and view this job with the following kubectl commands:

kubectl apply -f job.yaml
kubectl get jobs
Creating and viewing the hello-world job

Run the following command to view the pod and its logs created by the job:

kubectl get pods
kubectl logs <name of the pod>

From the image below, you can see that the job created a pod that echoes Hello World!.

Viewing pods and logs

💡 For a more detailed description of a job, you can execute the command kubectl describe job <job-name> | less. This will output what namespace the job was created in, the start time of the job and when it was completed, the duration of the job and other vital information.

Since the job has run to its completion, you’ll need to delete the job manually with the following command kubectl delete job hello-world as it will not delete itself automatically when it runs to completion:

Deleting the hello-world job

Monitoring Job Status and Completion

To ensure that your jobs are running as expected, it’s important to monitor their status and completion. Kubernetes provides several ways to monitor jobs, including using kubectl commands or the Kubernetes dashboard.

Divide your terminal into two segments (you can use tmux), and run the following command watch kubectl get all on one segment and execute the kubectl apply -f <your-job-specification-manifest-file> on the other segment, like the clip below; this process aims to see how a job gets created:

Monitoring a job to completion

Additionally, when you create a job in Kubernetes, this job runs to completion even if you delete the pod it creates. Meaning if you delete the pod that the job creates, the pod will be recreated automatically, as long as it hasn’t run to completion.

To see this in action, delete the job you created above kubectl delete job hello-world and edit the job.yaml file to look like the following:

# job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: hello-world
spec:
  template:
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["sleep",  "45"]
      restartPolicy: Never

The code above creates a job configuration that is designed to run a short-lived task that pauses for 45 seconds before exiting.

Divide your terminal into two segments, and run the following command watch kubectl get all on one segment and execute the kubectl apply -f <your-job-specification-manifest-file> on the other segment, like the clip below:

Verifying auto-recreation for jobs

You can see from the clip above that the pod created by the hello-world job gets recreated after deletion because it hasn’t run to completion.

Exploring Job Specifications

Jobs in Kubernetes has several specifications that can be customized to meet the specific requirements of your workload. These include completions, parallelism, backofflimit, and activeDeadlineSeconds.

Completions

Completions is a job specification that specifies the desired number of successfully completed pods in a job sequentially (one after the other). When a job is created, the job controller creates a completions number of pods and manages them until they complete successfully.

If the completions field is not specified, the default value is 1. The job is considered complete when the specified number of completions is reached, and any remaining pods are terminated. If a pod fails, Kubernetes will automatically create a new pod to replace it until the specified number of completions is reached.

💡 The completions field can be useful when you need to ensure that a specific number of pods complete successfully before proceeding to the next step in a workflow. For example, you might use the completions field to ensure that a certain number of database backups have been completed before proceeding to the next stage of a backup and recovery process.

Delete the current job and edit the job.yaml file to look like the following:

# job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: hello-world
spec:
  completions: 2
  template:
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["echo",  "Hello World!"]
      restartPolicy: Never

The code above creates a Kubernetes Job with a completions value of 2. The completions field specifies the desired number of completed pods that should exist when the job is considered complete.

Running job pods sequentially

Parallelism

Parallelism specifies the maximum number of pods that can run in parallel to complete the job. This is useful for workloads that require multiple instances of a task to run concurrently.

Delete the current job and edit the job.yaml file to look like the following:

#job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: hello-world
spec:
  completions: 2
  parallelism: 2
  template:
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["echo",  "Hello World!"]
      restartPolicy: Never

The parallelism field specifies the maximum number of pods that should be created simultaneously to run the job’s tasks. Also, the parallelism field is set to 2, which means that at most 2 pods will be created to run the tasks of the job at any given time. This can help increase the efficiency and speed of the job by allowing multiple tasks to be processed in parallel.

Running job pods in parallel

Backofflimit

Backofflimit specifies the number of times Kubernetes should retry a failed container before giving up. This can be useful for workloads prone to failures or requiring several attempts to complete successfully.

Delete the current job and edit the job.yaml file to look like the following:

#job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: hello-world
spec:
  template:
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["cat",  "job.yaml"]
      restartPolicy: Never

The code above will create a job that runs the busybox container which uses the busybox image to execute the cat command with the argument job.yaml. Now, this job is expected to fail, as such command cat job.yaml doesn’t exist in the busybox image.

Once you create this job, the behavior below is expected:

Running failed jobs without configuring backoffLimits

From the clip above, you can see that the job continues to create pods until it succeeds. But, certainly in this case the job won’t run to completion. So to resolve this, you’ll need to add the backoffLimit specification in the job configuration file.

Delete the job and edit the job.yaml file to look like the following:

#job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: hello-world
spec:
  backoffLimit: 2
  template:
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["cat",  "job.yaml"]
      restartPolicy: Never

Once you create this job, you should have the following output; which illustrates that the maximum number of retries the job controller should attempt before considering the Job as failed is 2.

💡 However, keep in mind that there’s also an initial attempt to run the pod, so if it fails on the first try, there will still be two retries for a total of three attempts.

Verifying backOffLimit for hello-world job

ActiveDeadlineSeconds

ActiveDeadlineSeconds specifies the maximum amount of time a job can run before it is terminated. This can be useful for workloads with strict time constraints or prevent a job from running indefinitely.

Delete the current job and edit the job.yaml file to look like the following:

#job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: hello-world
spec:
  activeDeadlineSeconds: 15
  template:
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["sleep",  "45"]
      restartPolicy: Never

In the code above, the activeDeadlineSeconds field is set to 15, which specifies that the job should be automatically terminated if it has been running for more than 15 seconds.

Verifying activeDeadlineSeconds for hello-world job

Creating and Managing CronJobs in Kubernetes

Creating and managing CronJobs in Kubernetes involves using the Kubernetes CronJob API to develop, monitor, and manage time-based scheduled jobs. As we know, a CronJob is a Kubernetes object that creates a job at a scheduled time interval using the Cron syntax. A CronJob definition includes the schedule of the job and the job template that specifies the container images, commands, and arguments to be executed.

Creating a CronJob Manifest File

A CronJob manifest file in Kubernetes looks similar to a job manifest file but with additional and specific properties. Creating a CronJob manifest file involves defining the CronJob’s schedule and job specifications using a YAML file. The YAML file should contain details such as the image, command, and other relevant specifications.

Create a yaml file and paste in the following configuration settings, this tutorial calls this file, cron-job.yaml:

# cron-job.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello-kubernetes
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: busybox
            image: busybox
            command: ["echo", "Hello-Kubernetes!!!"]
          restartPolicy: Never

The configuration settings above will create a CronJob resource named hello-kubernetes. This CronJob will run a job every minute as specified by the schedule field which is set to * * * * *.

The job to be run is defined in the jobTemplate section of the CronJob spec. This job runs a container named busybox with the echo command, which will output the text Hello-Kubernetes!!! as the standard output.

Execute the following kubectl command to create this CronJob:

kubectl apply -f cron-job.yaml

The below behavior is expected:

Creating a CrobJob that outputs (Hello-Kubernetes!!!) every minute

Understanding CronJob Properties and Options

Just like jobs, CronJobs also have several specifications that can be customized depending on the requirement of your workload. These specifications include successfulJobHistoryLimit, which specifies the number of completed jobs to keep in the history, and failedJobsHistoryLimit, which specifies the number of failed jobs to keep in the history. Other options include specifying concurrencyPolicy, which can be set to Allow, Forbid or Replace, and Idempotency, which ensures that a job is only run once.

To illustrate the successfulJobHistoryLimit and failedJobsHistoryLimit specifications, delete the CronJob kubectl delete cronjob hello-kubernetes and edit the cron-job.yaml file to look like the following:

# cron-job.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello-kubernetes
spec:
  schedule: "* * * * *"
  successfulJobsHistoryLimit: 0
  failedJobsHistoryLimit: 0
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: busybox
            image: busybox
            command: ["echo", "Hello-Kubernetes!!!"]
          restartPolicy: Never

The code above specifies the maximum number of successful and failed jobs to keep in the job history to 0.

💡 If the successfulJobsHistoryLimit or failedJobsHistoryLimit are not specified in the CronJob specification, Kubernetes sets their default values to 3, which means that the CronJob controller will keep the history of the last three successful and failed jobs, respectively.

Verifying successfulJobHistoryLimit and failedJobsHistoryLimit for a CronJob

concurrencyPolicy (Allow, Forbid & Replace)

ConcurrencyPolicy specifies how the CronJob handles concurrent job execution. Allow which is the default, allows concurrent execution of the same job, Forbid prevents concurrent execution of the same job (where jobs are run sequentially), and Replace cancels the currently running job and replaces it with a new one.

To manage concurrency policies, the CronJob manifest file is expected to look like the following:

# cron-job.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello-kubernetes
spec:
  schedule: "* * * * *"
  concurrencyPolicy: #Either Allow, Forbid, or Replace
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: busybox
            image: busybox
            command: ["echo", "Hello-Kubernetes!!!"]
          restartPolicy: Never

Idempotency

In the context of CronJobs in Kubernetes, idempotency means that if a CronJob is applied or executed multiple times, the result should be the same as if it had been executed only once. This is important for preventing unintended or duplicate actions, and ensuring that the system remains consistent and predictable.

💡 When using a CronJob in Kubernetes, there may be cases where a job object is not created or two job objects are created. This can result in problems if the jobs are not idempotent, meaning they can be run multiple times without causing side effects.

To mitigate this, it is important to ensure that the jobs are designed to be idempotent. That is, the job should produce the same result regardless of how many times it is executed.

To ensure idempotency, you should avoid relying on external states or resources that could be affected by multiple executions of the job, and instead use techniques like caching, deduplication, and transactional processing. You should also monitor the job and handle any errors or exceptions that may occur to ensure that the job runs smoothly and reliably.

Suspending a CronJob

Suspending a CronJob can be done using kubectl apply or patch commands. This can be useful when you need to temporarily stop the execution of a CronJob without deleting it.

To suspend this CronJob, edit the cron-job.yaml file to look like the following:

# cron-job.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello-kubernetes
spec:
  schedule: "* * * * *"
  suspend: true
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: busybox
            image: busybox
            command: ["echo", "Hello-Kubernetes!!!"]
          restartPolicy: Never

From the CronJob manifest file above, The suspend field is added to suspend the execution of a CronJob. If it is set to true, no new CronJob runs will be created based on the defined schedule.

Suspending a CronJob

Resuming a CronJob

When you are ready to resume the CronJob, you can simply set suspend to false. Additionally, you can use the patch command directly without editing the cron-job.yaml file. The following command kubectl patch cronjob <name-of-the-cronjob> -p '{"spec":{"suspend":false}}' will set the suspend spec to false.

Resuming a CronJob using the kubectl patch command

Conclusion

In this tutorial, you’ve learnt how to manage and monitor Jobs and CronJobs in Kubernetes. For reliable and efficient execution, always keep a check on their performance, control concurrent executions using a cluster-wide policy, configure restart policies for resilience, and assign appropriate resource requests and limits.

Equipped with these skills and tools, you can now schedule tasks in your Kubernetes cluster more efficiently.

As you continue to work with Kubernetes, you might find yourself looking for ways to improve your container build process. If that’s the case, you might want to check out Earthly. It’s a tool that can significantly enhance your build process, making it more efficient and reliable.

Earthly makes CI/CD super simple
Fast, repeatable CI/CD with an instantly familiar syntax – like Dockerfile and Makefile had a baby.

Learn More

Mercy Bassey %
Mercy Bassey

Mercy Bassey is a JavaScript programmer with a passion for technical writing. Her area of expertise is Full-stack web development and DevOps/IT.

Writers at Earthly work closely with our talented editors to help them create high quality tutorials. This article was edited by:
Bala Priya C

Bala is a technical writer who enjoys creating long-form content. Her areas of interest include math and programming. She shares her learning with the developer community by authoring tutorials, how-to guides, and more.

Published:

Get notified about new articles!
We won't send you spam. Unsubscribe at any time.