Overview #
In this tutorial I’m using the following Kubernetes cluster, deployed with Kubeadm:
NAME      STATUS   ROLES           AGE    VERSION    INTERNAL-IP     EXTERNAL-IP   OS-IMAGE           KERNEL-VERSION     CONTAINER-RUNTIME
ubuntu1   Ready    control-plane   104d   v1.28.11   192.168.30.10   <none>        Ubuntu 24.04 LTS   6.8.0-36-generic   containerd://1.7.18
ubuntu2   Ready    worker          104d   v1.28.11   192.168.30.11   <none>        Ubuntu 24.04 LTS   6.8.0-36-generic   containerd://1.7.18
ubuntu3   Ready    worker          104d   v1.28.11   192.168.30.12   <none>        Ubuntu 24.04 LTS   6.8.0-36-generic   containerd://1.7.1
Kubernetes Jobs Explanation #
Overview #
- 
A Job creates pods that run short-term, one-off tasks that should run once, until completion, or a limited number of times.
 - 
When the task is done, the Job finishes.
 - 
Jobs are used to process data, run a script, or clean up temporary files.
 
Job Types #
Non-Parallel Jobs:
- 
Are designed to run a single task to completion.
 - 
Are used for tasks that can’t be divided among multiple pods.
 - 
For example a script that needs to be run once a day.
 
Parallel Jobs with a Fixed Completion Count:
- 
Used to divide the a among several pods, each processing a portion of the data.
 - 
The workload can be spread across multiple nodes.
 - 
The job is considered complete when a specified number of Pods successfully complete their tasks.
 
CronJob #
- A CronJob allows to define a job that will run automatically at specified times or intervals.
 
 
Basic Example Jobs #
Create Namespace #
# Create a new namespace
kubectl create ns example-jobs
Non-parallel Job #
Create Job #
# Create a manifest for the job
vi exjob-non-parallel.yaml
Without automatic cleanup:
apiVersion: batch/v1
kind: Job
metadata:
  name: exjob-non-parallel
  namespace: example-jobs
spec:
  template:
    metadata:
      name: job-pod
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["echo", "print some text"]
      restartPolicy: OnFailure
  completions: 3
Add automatic cleanup:
apiVersion: batch/v1
kind: Job
metadata:
  name: exjob-non-parallel
  namespace: example-jobs
spec:
  template:
    metadata:
      name: job-pod
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["echo", "print some text"]
      restartPolicy: OnFailure
  ttlSecondsAfterFinished: 10 # Job and pods will be deleted
  completions: 3
# Deploy the manifest
kubectl apply -f exjob-non-parallel.yaml
The automatic cleanup deletes the job and its pods a certain amount of time after the job completes.
Verify Job #
Verify job without automatic cleanup:
# List pods and jobs in "example-jobs" namespace
kubectl get pod,job -n example-jobs
# Shell output:
NAME                           READY   STATUS      RESTARTS   AGE
pod/exjob-non-parallel-68ds7   0/1     Completed   0          6s
pod/exjob-non-parallel-6n8jn   0/1     Completed   0          14s
pod/exjob-non-parallel-kcvwv   0/1     Completed   0          10s
NAME                           STATUS     COMPLETIONS   DURATION   AGE
job.batch/exjob-non-parallel   Complete   3/3           12s        14s
Verify job with automatic cleanup:
# List pods and jobs in "example-jobs" namespace
kubectl get pod,job -n example-jobs
# Shell output:
NAME                           READY   STATUS      RESTARTS   AGE
pod/exjob-non-parallel-2hffr   0/1     Completed   0          8s
pod/exjob-non-parallel-tgtrj   0/1     Completed   0          12s
pod/exjob-non-parallel-v5kt6   0/1     Completed   0          16s
NAME                           STATUS     COMPLETIONS   DURATION   AGE
job.batch/exjob-non-parallel   Complete   3/3           12s        16s
# Shell output: (Wait 10 seconds)
No resources found in example-jobs namespace.
Delete Job #
If the job was deployed without automatic cleanup, manually delete the job:
# Delete Job after it's finished
kubectl delete job exjob-non-parallel -n example-jobs
 
Parallel Job #
Create Job #
# Create a manifest for the job
vi exjob-parallel.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: exjob-parallel
  namespace: example-jobs
spec:
  template:
    metadata:
      name: job-pod
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["echo", "print some text"]
      restartPolicy: OnFailure
  ttlSecondsAfterFinished: 15 # Job and pods will be deleted
  completions: 12
  parallelism: 3
# Deploy the manifest
kubectl apply -f exjob-parallel.yaml
- 
completions: 12Specifies the total number of successful completions required for the Job to be considered finished. - 
parallelism: 3Defines how many pods can run in parallel at the same time. Kubernetes will start 3 pods in parallel to work on the Job. 
Verify Job #
Verify job without automatic cleanup:
# List pods and jobs in "example-jobs" namespace
kubectl get pod,job -n example-jobs
# Shell output:
NAME                       READY   STATUS      RESTARTS   AGE
pod/exjob-parallel-2v7bd   0/1     Completed   0          4s
pod/exjob-parallel-67hwz   0/1     Completed   0          8s
pod/exjob-parallel-jlw4p   0/1     Completed   0          8s
pod/exjob-parallel-mfqnk   0/1     Completed   0          3s
pod/exjob-parallel-q4q64   0/1     Completed   0          3s
pod/exjob-parallel-xd9w2   0/1     Completed   0          8s
NAME                       STATUS    COMPLETIONS   DURATION   AGE
job.batch/exjob-parallel   Running   3/12          8s         8s
# Shell output:
NAME                       READY   STATUS      RESTARTS   AGE
pod/exjob-parallel-2v7bd   0/1     Completed   0          13s
pod/exjob-parallel-5xgj9   0/1     Completed   0          5s
pod/exjob-parallel-67hwz   0/1     Completed   0          17s
pod/exjob-parallel-ffdcc   0/1     Completed   0          9s
pod/exjob-parallel-jlw4p   0/1     Completed   0          17s
pod/exjob-parallel-ks7vv   0/1     Completed   0          9s
pod/exjob-parallel-mfqnk   0/1     Completed   0          12s
pod/exjob-parallel-n8hvd   0/1     Completed   0          5s
pod/exjob-parallel-q4q64   0/1     Completed   0          12s
pod/exjob-parallel-qjphj   0/1     Completed   0          5s
pod/exjob-parallel-xd9w2   0/1     Completed   0          17s
pod/exjob-parallel-zqn5b   0/1     Completed   0          9s
NAME                       STATUS     COMPLETIONS   DURATION   AGE
job.batch/exjob-parallel   Complete   12/12         16s        17s
# Shell output:
No resources found in example-jobs namespace
 
CronJob: Restart a Deployment #
Create Namespace #
# Create a new namespace
kubectl create ns example-namespace
Example Deployment #
# Create deployment manifest
vi nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment  # Should match CronJob
  namespace: example-namespace
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
# Apply the manifest
kubectl apply -f nginx-deployment.yaml
ServiceAccount, Role & RoleBinding #
# Create a manifest for the job permissions
vi permissions.yaml
# ServiceAccount
kind: ServiceAccount
apiVersion: v1
metadata:
  name: restart-deployment
  namespace: example-namespace # Define namespace
---
# Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: restart-deployment
  namespace: example-namespace # Define namespace
rules:
  - apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    resourceNames: ["nginx-deployment"] # Deployment name
    verbs: ["get", "patch", "list", "watch"] # Permissions required for restarting the deployment
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: restart-deployment
  namespace: example-namespace # Define namespace
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: restart-deployment
subjects:
  - kind: ServiceAccount
    name: restart-deployment
    namespace: example-namespace # Define namespace
# Apply the manifest
kubectl apply -f permissions.yaml
CronJob #
The following CronJob runs every two minutes:
# Create a manifest for the CronJob
vi restart-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: restart-deployment
  namespace: example-namespace # Define namespace
spec:
  successfulJobsHistoryLimit: 1
  failedJobsHistoryLimit: 2
  concurrencyPolicy: Forbid
  schedule: '*/2 * * * *'
  jobTemplate:
    spec:
      backoffLimit: 2
      activeDeadlineSeconds: 600
      template:
        spec:
          serviceAccountName: restart-deployment
          restartPolicy: Never
          containers:
            - name: kubectl
              image: bitnami/kubectl
              command:
                - 'kubectl'
                - 'rollout'
                - 'restart'
                - 'deployment/nginx-deployment' # Deployment name
# Apply the manifest
kubectl apply -f restart-cronjob.yaml
- 
successfulJobsHistoryLimit: 1Specifies the number of successful jobs that Kubernetes will keep in the history after their completion. Older successful job records will be deleted. - 
failedJobsHistoryLimit: 2Specifies the number of failed jobs that Kubernetes will retain in the history. - 
concurrencyPolicy: ForbidDefines how Kubernetes handles the execution of jobs if a new job is scheduled to run before the previous one finishes.ForbidEnsures that only one job runs at a time. If a job is still running when a new one is scheduled, the new job will be skipped. Other options areAllowandReplace. - 
backoffLimit: 2Specifies how many times Kubernetes will retry a failed job before it is marked as failed and not retried anymore. - 
activeDeadlineSeconds: 600mMximum duration (in seconds) that a job is allowed to run before Kubernetes terminates it. 
Verify Cronjob & Job #
# List cronjobs in "example-namespace" namespace
kubectl get cronjob -n example-namespace
# Shell output:
NAME                 SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
restart-deployment   */2 * * * *   False     0        <none>          7s
# List cronjob details
kubectl describe cronjob restart-deployment -n example-namespace
# List jobs created by your CronJob
kubectl get jobs -n example-namespace
# Shell output:
NAME                          COMPLETIONS   DURATION   AGE
restart-deployment-28821114   1/1           3s         4s
Verify the Deployment Restart #
# List pods in "example-namespace" namespace
kubectl get pods -n example-namespace --watch
# Shell output:
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-dd4dc744c-9fp7p   1/1     Running   0          27s
restart-deployment-28821114-bnft5   0/1     Pending   0          0s
restart-deployment-28821114-bnft5   0/1     Pending   0          0s
restart-deployment-28821114-bnft5   0/1     ContainerCreating   0          0s
nginx-deployment-8fd995c7b-tw7d6    0/1     Pending             0          0s
nginx-deployment-8fd995c7b-tw7d6    0/1     Pending             0          0s
nginx-deployment-8fd995c7b-tw7d6    0/1     ContainerCreating   0          0s
restart-deployment-28821114-bnft5   0/1     Completed           0          1s
restart-deployment-28821114-bnft5   0/1     Completed           0          2s
restart-deployment-28821114-bnft5   0/1     Completed           0          3s
nginx-deployment-8fd995c7b-tw7d6    1/1     Running             0          2s
nginx-deployment-dd4dc744c-9fp7p    1/1     Terminating         0          62s
restart-deployment-28821114-bnft5   0/1     Completed           0          3s
nginx-deployment-dd4dc744c-9fp7p    0/1     Terminating         0          63s
nginx-deployment-dd4dc744c-9fp7p    0/1     Terminating         0          63s
nginx-deployment-dd4dc744c-9fp7p    0/1     Terminating         0          63s
nginx-deployment-dd4dc744c-9fp7p    0/1     Terminating         0          63s
restart-deployment-28821116-vt9r4   0/1     Pending             0          0s
restart-deployment-28821116-vt9r4   0/1     Pending             0          0s
restart-deployment-28821116-vt9r4   0/1     ContainerCreating   0          0s
nginx-deployment-95949fd86-mpf6j    0/1     Pending             0          0s
nginx-deployment-95949fd86-mpf6j    0/1     Pending             0          0s
nginx-deployment-95949fd86-mpf6j    0/1     ContainerCreating   0          0s
restart-deployment-28821116-vt9r4   0/1     Completed           0          2s
restart-deployment-28821116-vt9r4   0/1     Completed           0          3s
restart-deployment-28821116-vt9r4   0/1     Completed           0          4s
nginx-deployment-95949fd86-mpf6j    1/1     Running             0          3s
nginx-deployment-8fd995c7b-tw7d6    1/1     Terminating         0          2m3s
restart-deployment-28821116-vt9r4   0/1     Completed           0          4s
restart-deployment-28821114-bnft5   0/1     Terminating         0          2m4s
restart-deployment-28821114-bnft5   0/1     Terminating         0          2m4s
nginx-deployment-8fd995c7b-tw7d6    0/1     Terminating         0          2m3s
nginx-deployment-8fd995c7b-tw7d6    0/1     Terminating         0          2m4s
nginx-deployment-8fd995c7b-tw7d6    0/1     Terminating         0          2m4s
nginx-deployment-8fd995c7b-tw7d6    0/1     Terminating         0          2m4s
# List deployment details
kubectl describe deployment nginx-deployment -n example-namespa
# Shell output:
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  6m31s  deployment-controller  Scaled up replica set nginx-deployment-7c79c4bf97 to 1
  Normal  ScalingReplicaSet  5m51s  deployment-controller  Scaled up replica set nginx-deployment-86d6c9f578 to 1
  Normal  ScalingReplicaSet  5m49s  deployment-controller  Scaled down replica set nginx-deployment-7c79c4bf97 to 0 from 1
  Normal  ScalingReplicaSet  4m51s  deployment-controller  Scaled up replica set nginx-deployment-dd4dc744c to 1
  Normal  ScalingReplicaSet  4m49s  deployment-controller  Scaled down replica set nginx-deployment-86d6c9f578 to 0 from 1
  Normal  ScalingReplicaSet  3m51s  deployment-controller  Scaled up replica set nginx-deployment-8fd995c7b to 1
  Normal  ScalingReplicaSet  3m49s  deployment-controller  Scaled down replica set nginx-deployment-dd4dc744c to 0 from 1
  Normal  ScalingReplicaSet  111s   deployment-controller  Scaled up replica set nginx-deployment-95949fd86 to 1
  Normal  ScalingReplicaSet  108s   deployment-controller  Scaled down replica set nginx-deployment-8fd995c7b to 0 from 
Delete Resources #
# Delete the Cronjob
kubectl delete cronjob restart-deployment -n example-namespace
# Delete the example deployment
kubectl delete deployment nginx-deployment -n example-namespace
# Delete the RoleBinding
kubectl delete rolebinding restart-deployment -n example-namespace
# Delete the Role
kubectl delete role restart-deployment -n example-namespace
# Delete the ServiceAccount
kubectl delete serviceaccount restart-deployment -n example-namespace