115

I'm looking for a way to tell (from within a script) when a Kubernetes Job has completed. I want to then get the logs out of the containers and perform cleanup.

What would be a good way to do this? Would the best way be to run kubectl describe job <job_name> and grep for 1 Succeeded or something of the sort?

codeforester
  • 39,467
  • 16
  • 112
  • 140
russt
  • 1,548
  • 2
  • 14
  • 16
  • To wait for pod to be running, use "condition=ready". Alternatively, you can wait for deployment rollout status. See: https://stackoverflow.com/a/60810347/658497 – Noam Manos Mar 23 '20 at 09:01
  • See also https://stackoverflow.com/questions/55073453/wait-for-kubernetes-job-to-complete-on-either-failure-success-using-command-line – user11153 Jul 07 '22 at 15:52

7 Answers7

187

Since version 1.11, you can do:

kubectl wait --for=condition=complete job/myjob

and you can also set a timeout:

kubectl wait --for=condition=complete --timeout=30s job/myjob
Felix Dombek
  • 13,664
  • 17
  • 79
  • 131
abagshaw
  • 6,162
  • 4
  • 38
  • 76
  • 2
    Is it possible to have an indefinite timeout (wait forever)? – João Matos Mar 14 '19 at 19:06
  • 4
    @JoãoMatos The default `--timeout` is 30 seconds. Specifying negative values for `--timeout` means "wait for a week" (which is, in practice, almost indefinite). Take a look at the [kubectl wait](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#wait) reference. – Eduardo Baitello Mar 14 '19 at 20:23
  • I search for something I can easily implement in my YAML config files and a small docker image from [groundnuty/k8s-wait-for](https://github.com/groundnuty/k8s-wait-for) met my expectations. – Egel Sep 18 '20 at 09:33
  • 5
    Is there anything wrong with just following the log of the job? If there's only one pod, doing `kubectl logs job/myjob --follow` will effectively wait for the job to finish, regardless of its success (`condition=complete` does trigger when the job fails) – Coo Dec 05 '20 at 01:50
  • If the pods for the job fail, this command would timeout since there is no event for the job. – Hong Mar 04 '21 at 01:29
  • @Coo That command will fail until the job has had a chance to start up, so there's a race condition there. – Noah Jul 08 '21 at 14:29
  • Actually the race condition is present with the `wait` command in the answer as well. – Noah Jul 08 '21 at 14:51
32

You can visually watch a job's status with this command:

kubectl get jobs myjob -w

The -w option watches for changes. You are looking for the SUCCESSFUL column to show 1.

For waiting in a shell script, I'd use this command:

until kubectl get jobs myjob -o jsonpath='{.status.conditions[? 
    (@.type=="Complete")].status}' | grep True ; do sleep 1 ; done
Alex Riley
  • 169,130
  • 45
  • 262
  • 238
Eric Tune
  • 7,819
  • 1
  • 15
  • 17
  • what do you think about waiting on `status.succeeded = 1` ? – Guido Feb 22 '18 at 17:30
  • @GuidoGarcía If you use status.succeeded = 1 then you've got the problem when a Job has multiple completions status.succeeded is set to 1 when the first one is finished, not all of them like with conditions.type = Complete – Meiko Watu May 09 '18 at 18:00
  • 7
    The shell script answer this will wait forever on a "failed" job, as a failed job will return nothing – bwawok Apr 12 '19 at 18:40
  • Isn't there a minor editing mistake with the space after `.status.conditions[?`? – Philippe Fanaro Aug 20 '19 at 14:03
9

You can use official Python kubernetes-client.

https://github.com/kubernetes-client/python

Create new Python virtualenv:

virtualenv -p python3 kubernetes_venv activate it with

source kubernetes_venv/bin/activate

and install kubernetes client with:

pip install kubernetes

Create new Python script and run:

from kubernetes import client, config

config.load_kube_config()

v1 = client.BatchV1Api()
ret = v1.list_namespaced_job(namespace='<YOUR-JOB-NAMESPACE>', watch=False)
for i in ret.items:
    print(i.status.succeeded)

Remember to set up your specific kubeconfig in ~/.kube/config and valid value for your job namespace -> '<YOUR-JOB-NAMESPACE>'

  • Not sure this is an exhaustive answer, because there is also `job.status.failed` field. I now observe that if the job succeeded, than the `job.status.failed` is `None`. I wonder if `job.status.failed` is 1, could the `job.status.succeeded` be `None`. – Anton Daneyko May 20 '21 at 12:50
2

I would use -w or --watch:

$ kubectl get jobs.batch --watch
NAME     COMPLETIONS   DURATION   AGE
python   0/1           3m4s       3m4s
aleb
  • 2,490
  • 1
  • 28
  • 46
1

Adding the best answer, from a comment by @Coo, If you add a -f or --follow option when getting logs, it'll keep tailing the log and terminate when the job completes or fails. The $# status code is even non-zero when the job fails.

kubectl logs -l job-name=myjob --follow

One downside of this approach, that I'm aware of, is that there's no timeout option.

Another downside is the logs call may fail while the pod is in Pending (while the containers are being started). You can fix this by waiting for the pod:

# Wait for pod to be available; logs will fail if the pod is "Pending"
while [[ "$(kubectl get pod -l job-name=myjob -o json | jq -rc '.items | .[].status.phase')" == 'Pending' ]]; do
    # Avoid flooding k8s with polls (seconds)
    sleep 0.25
done

# Tail logs
kubectl logs -l job-name=myjob --tail=400 -f
kelloti
  • 8,705
  • 5
  • 46
  • 82
  • Thanks. You can send the output of `kubectl logs` to the background first with `&`, like: `kubectl logs -l job-name=myjob --follow &`. Then you can run `kubectl wait` immediately after as in @abagshaw's answer (for example in a CI script). This will stream your logs to the output while waiting for the job to finish. – inostia Jun 01 '22 at 01:15
1

Although kubectl wait --for=condition=complete job/myjob and kubectl wait --for=condition=complete job/myjob allow us to check whether the job completed but there is no way to check if the job just finished executing (irrespective of success or failure). If this is what you are looking for, a simple bash while loop with kubectl status check did the trick for me.

#!/bin/bash
while true; do
status=$(kubectl get job jobname -o jsonpath='{.status.conditions[0].type}')
echo "$status" | grep -qi 'Complete' && echo "0" && exit 0
echo "$status" | grep -qi 'Failed' && echo "1" && exit 1
done
Koshur
  • 378
  • 1
  • 6
  • 20
0

It either one of these queries with kubectl

kubectl get job test-job -o jsonpath='{.status.succeeded}'

or

kubectl get job test-job -o jsonpath='{.status.conditions[?(@.type=="Complete")].status}'
Most Wanted
  • 6,254
  • 5
  • 53
  • 70