Job用途
容器按照持续运行的时间可分为两类:服务类容器和工作类容器
服务类容器通常持续提供服务,需要一直运行,比如HTTPServer、Daemon等。工作类容器则是一次性任务,比如批处理程序,完成后容器就退出
Kubernetes的Deployment、ReplicaSet和DaemonSet都用于管理服务类容器;对于工作类容器,我们使用Job
先看一个简单的Job配置文件myjob.yml
[k8s@server1 ~]$ cat myjob.yml
apiVersion: batch/v1 (1)
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: busybox
command: ["echo","hello k8s job !"]
restartPolicy: Never
batch/v1 | 当前Job的apiVersion |
kind | 指明当前资源的类型为Job |
restartPolicy | 指定什么情况下需要重启容器。对于Job,只能设置为Never(启动容器失败了,会一直重新启动新的pod)或者OnFailure(启动容器失败,不会重新启动新的pod,节省资源)。对于其他controller(比如Deployment), |
可以设置为Always
启动Job
[k8s@server1 ~]$ kubectl apply -f myjob.yml
job.batch/myjob created
通过kubectl get job查看Job的状态
[k8s@server1 ~]$ kubectl get job
NAME COMPLETIONS DURATION AGE
myjob 1/1 12s 14s
通过kubectl logs可以查看Pod的标准输出
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-c87rp 0/1 Completed 0 27s
[k8s@server1 ~]$ kubectl logs myjob-c87rp
hello k8s job !
以上是Pod成功执行的情况,如果Pod失败了会怎么样呢?
修改myjob.yml,故意引入一个错误
[k8s@server1 ~]$ cat myjob.yml
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: busybox
command: ["invalid_command","hello k8s job !"]
restartPolicy: Never
先删除之前的Job
[k8s@server1 ~]$ kubectl delete -f myjob.yml
job.batch "myjob" deleted
运行新的Job并查看状态
[k8s@server1 ~]$ kubectl apply -f myjob.yml
job.batch/myjob created
可以看到有多个Pod,状态均不正常
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-n5t26 0/1 ContainerCreating 0 9s
myjob-kvb67 0/1 ContainerCannotRun 0 38s
myjob-n5t26 0/1 ContainerCannotRun 0 59s
通过kubectl describe podname查看某个Pod的启动日志
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/myjob-n5t26 to server3
Normal Pulling 24s kubelet, server3 Pulling image "busybox"
Normal Pulled 5s kubelet, server3 Successfully pulled image "busybox"
Normal Created 5s kubelet, server3 Created container hello
Warning Failed 5s kubelet, server3 Error: failed to start container "hello": Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"invalid_command\": executable file not found in $PATH": unknown
# 日志显示没有可执行程序,符合我们的预期
下面解释一个现象:为什么kubectl get pod会看到这么多个失败
的Pod?
原因是:当第一个Pod启动时,容器失败退出,根据restartPolicy:
Never,此失败容器不会被重启,但Job DESIRED的Pod是1,目前SUCCESSFUL为0,不满足,所以Job controller会启动新的Pod,直到SUCCESSFUL为1。对于我们这个例子,SUCCESSFUL永远也到不了1,所以Job controller会一直创建新的Pod。为了终止这个行为,只能删除Job
如果将restartPolicy设置为OnFailure会怎么样?下面我们实践一下,修改myjob.yml后重新启动
[k8s@server1 ~]$ cat myjob.yml
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: busybox
command: ["invalid_command","hello k8s job !"]
restartPolicy: OnFailure
[k8s@server1 ~]$ kubectl apply -f myjob.yml
job.batch/myjob created
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-b28g7 0/1 ContainerCreating 0 4s
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-b28g7 0/1 ContainerCreating 0 6s
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-b28g7 0/1 ContainerCreating 0 7s
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-b28g7 0/1 RunContainerError 1 38s
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-b28g7 0/1 RunContainerError 1 38s
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-b28g7 0/1 RunContainerError 2 39s
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-b28g7 0/1 RunContainerError 2 86s
# RESTARTS为2,而且不断增加,说明OnFailure生效,容器失败后会自动重启,不会创建新的pod
[k8s@server1 ~]$ kubectl delete -f myjob.yml
job.batch "myjob" deleted
Job的并行性
有时我们希望能同时运行多个Pod,提高Job的执行效率。这个可以通过parallelism设置
[k8s@server1 ~]$ cat myjob.yml
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
parallelism: 2 ##同时运行两个pod
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: busybox
command: ["echo","hello k8s job !"]
restartPolicy: OnFailure
#这里我们将并行的Pod数量设置为2,实践一下,Job一共启动了两个Pod,而且AGE相同,可见是并行运行的
[k8s@server1 ~]$ kubectl apply -f myjob.yml
job.batch/myjob created
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-plh9q 0/1 RunContainerError 0 19s
myjob-x4f4v 0/1 RunContainerError 0 19s
[k8s@server1 ~]$ kubectl get jobs.batch
NAME COMPLETIONS DURATION AGE
myjob 0/1 of 2 7s 7s
[k8s@server1 ~]$ kubectl get job
NAME COMPLETIONS DURATION AGE
myjob 0/1 of 2 11s 11s
我们还可以通过completions设置Job成功完成Pod的总数
[k8s@server1 ~]$ cat myjob.yml
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
completions: 6
parallelism: 2
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: busybox
command: ["echo","hello k8s job !"]
restartPolicy: OnFailure
# 上面配置的含义是:每次运行两个Pod,直到总共有6个Pod成功完成
[k8s@server1 ~]$ kubectl apply -f myjob.yml
job.batch/myjob created
[k8s@server1 ~]$ kubectl get job
NAME COMPLETIONS DURATION AGE
myjob 6/6 57s 2m6s
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myjob-5n7sz 0/1 Completed 0 74s
myjob-rp2sx 0/1 Completed 0 41s
myjob-v5thn 0/1 Completed 0 55s
myjob-vkww5 0/1 Completed 0 74s
myjob-w9cvx 0/1 Completed 0 33s
myjob-wlh4j 0/1 Completed 0 54s
上面的例子只是为了演示Job的并行特性,实际用途不大。不过现实中确实存在很多需要并行处理的场景。比如批处理程序,每个副本(Pod)都会从任务池中读取任务并执行,副本越多,执行时间就越短,效率就越高。这种类似的场景都可以用Job来实现
定时Job
Linux中有cron程序定时执行任务,Kubernetes的CronJob提供了,类似的功能,可以定时执行Job
[k8s@server1 ~]$ cat cron.yml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
command: ["echo","hello k8s job !"]
restartPolicy: OnFailure
(1) batch/v1beta1 | 当前CronJob的apiVersion |
kind | 指明当前资源的类型为CronJob |
schedule | 指定什么时候运行Job,其格式与Linux cron一致。这里*/1 * * * *的含义是每一分钟启动一次 |
[k8s@server1 ~]$ kubectl api-versions
admissionregistration.k8s.io/v1
admissionregistration.k8s.io/v1beta1
apiextensions.k8s.io/v1
apiextensions.k8s.io/v1beta1
apiregistration.k8s.io/v1
apiregistration.k8s.io/v1beta1
apps/v1
authentication.k8s.io/v1
authentication.k8s.io/v1beta1
authorization.k8s.io/v1
authorization.k8s.io/v1beta1
autoscaling/v1
autoscaling/v2beta1
autoscaling/v2beta2
batch/v1
batch/v1beta1
certificates.k8s.io/v1beta1
coordination.k8s.io/v1
coordination.k8s.io/v1beta1
events.k8s.io/v1beta1
extensions/v1beta1
networking.k8s.io/v1
networking.k8s.io/v1beta1
node.k8s.io/v1beta1
policy/v1beta1
rbac.authorization.k8s.io/v1
rbac.authorization.k8s.io/v1beta1
scheduling.k8s.io/v1
scheduling.k8s.io/v1beta1
storage.k8s.io/v1
storage.k8s.io/v1beta1
v1
通过kubectl apply创建CronJob
[k8s@server1 ~]$ kubectl apply -f cron.yml
cronjob.batch/hello created
通过kubectl get cronjob查看CronJob的状态
[k8s@server1 ~]$ kubectl get cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 0 <none> 20s
等待几分钟,然后通过kubectl get jobs查看Job的执行情况
[k8s@server1 ~]$ kubectl get jobs
NAME COMPLETIONS DURATION AGE
hello-1573011120 0/1 3m41s 3m41s
hello-1573011180 0/1 2m41s 2m41s
hello-1573011240 0/1 101s 101s
hello-1573011300 0/1 41s 41s
可以看到每隔一分钟就会启动一个Job。执行kubectl logs可查看某个Job的运行日志
[k8s@server1 ~]$ kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-1573011120-68kp9 0/1 ImagePullBackOff 0 4m21s
hello-1573011180-lmwvv 0/1 ImagePullBackOff 0 3m21s
hello-1573011240-v4h77 0/1 ImagePullBackOff 0 2m21s
hello-1573011300-l4v9v 0/1 ImagePullBackOff 0 81s
hello-1573011360-tk64h 0/1 ContainerCreating 0 21s
myjob-5n7sz 0/1 Completed 0 19h
myjob-rp2sx 0/1 Completed 0 19h
myjob-v5thn 0/1 Completed 0 19h
myjob-vkww5 0/1 Completed 0 19h
myjob-w9cvx 0/1 Completed 0 19h
myjob-wlh4j 0/1 Completed 0 19h
[k8s@server1 ~]$ kubectl logs hello-1573011120-68kp9
hello k8s job !