从零吃透 K8s Job:场景、失败策略与定时任务 CronJob 实战
前言在 Kubernetes 中容器应用大体可以分为两类服务类容器和工作类容器。服务类容器需要长时间持续运行对外提供服务如 Nginx、MySQL而工作类容器通常执行一次性任务任务完成后容器便退出如批处理程序、数据计算、测试脚本等。对于服务类容器Kubernetes 提供了 Deployment、ReplicaSet、DaemonSet 等控制器来管理对于工作类容器则需要使用Job这种原生控制器。Job 能够确保指定数量的 Pod 成功完成任务并退出非常适合运行一次性作业。本文将从 Job 的使用场景入手通过实际 YAML 配置、失败重试策略分析以及 CronJob 定时任务等方面系统讲解 Kubernetes Job 的用法与原理帮助读者在生产环境中正确使用 Job 来完成批处理任务。一、Job使用场景容器按照持续运行的时间可分为两类类型描述示例对应K8s控制器服务类容器持续提供服务需要一直运行http server, mysql dbDeployment, ReplicaSet, DaemonSet工作类容器一次性任务完成后容器就退出批处理程序、数据导入、计算任务JobKubernetes 中的 Deployment、ReplicaSet 和 DaemonSet 都用于管理服务类容器而对于工作类容器Job是最佳选择。二、Job应用介绍1. 一个简单的 Job 配置文件下面是一个基于busybox执行简单 echo 任务的 Job YAML 示例apiVersion: batch/v1 kind: Job metadata: name: myjob spec: template: spec: containers: - name: job image: busybox command: [echo, hello k8s] restartPolicy: Never2. 关键字段解释字段说明apiVersionJob 的 API 版本当前稳定版为batch/v1kind资源类型这里为Jobmetadata.nameJob 的名称spec.templatePod 模板定义要运行的 Podspec.template.spec.containers容器定义image和command指定要执行的命令restartPolicy重要Pod 内容器的重启策略。对于 Job只能设置为Never或OnFailure不能设为AlwaysrestartPolicy 的含义Never无论容器退出状态如何kubelet 都不重启该容器。OnFailure当容器终止运行且退出码不为 0 时重启容器。Always容器失效时自动重启仅用于服务类控制器。3. 创建并查看 Job[rootmaster k8s]# kubectl apply -f myjob.yml job.batch/myjob created [rootmaster k8s]# kubectl get job NAME COMPLETIONS DURATION AGE myjob 1/1 2s 10s [rootmaster k8s]# kubectl get pod NAME READY STATUS RESTARTS AGE myjob-xxxxx 0/1 Completed 0 15s [rootmaster k8s]# kubectl logs myjob-xxxxx hello k8s成功执行后Pod 的状态变为CompletedJob 的COMPLETIONS显示为1/1表示成功完成 1 个 Pod。三、Job失败策略分析上面介绍的是 Job 成功执行的情况。如果任务失败会怎样下面通过一个故意出错的示例来观察 Job 的失败处理行为。1. 制造失败场景修改myjob.yml将 command 改为一个不存在的命令command: [easasacho, hello k8s] # 故意写错命令然后重新创建 Job[rootmaster k8s]# kubectl delete -f myjob.yml [rootmaster k8s]# kubectl apply -f myjob.yml2. 观察 Pod 状态[rootmaster k8s]# kubectl get pod NAME READY STATUS RESTARTS AGE myjob-mq7sd 0/1 ContainerCannotRun 0 88s myjob-ngpxc 0/1 ContainerCannotRun 0 2m48s myjob-qs2sb 0/1 ContainerCannotRun 0 3m36s myjob-w7gmgf 0/1 ContainerCannotRun 0 3m53s myjob-x2jp9 0/1 ContainerCannotRun 0 3m8s会看到大量失败的 Pod 被不断创建为什么会出现这种情况3. 原因分析当restartPolicy: Never时第一个 Pod 启动容器因“命令不存在”错误退出。根据策略Never表示不重启该容器Pod 保持在失败状态如ContainerCannotRun。Job 控制器检查当前状态期望成功数为 1实际成功数为 0。不满足期望于是 Job controller 不断创建新的 Pod 来尝试达到成功目标。由于命令始终错误永远不会成功因此 Job controller 会无限循环创建 Pod直到用户手动删除该 Job。当restartPolicy: OnFailure时第一个 Pod 启动失败容器退出。根据策略OnFailure会重启容器而不是重建 Pod。因此只会看到一个 Pod但RESTARTS次数会不断增加。4. 查看失败原因使用kubectl describe pod可以查看具体错误[rootmaster k8s]# kubectl describe pod myjob-w7gmf ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled unknown default-scheduler Successfully assigned default/myjob-w7gmf to node1 Normal Pulling 26s kubelet, node1 Pulling image busybox Normal Pulled 10s kubelet, node1 Successfully pulled image busybox Normal Created 10s kubelet, node1 Created container job Warning Failed 10s kubelet, node1 Error: failed to start container job: ... executable file not found in $PATH错误信息executable file not found in $PATH明确指出了问题所在。5. 如何终止失败的 Job对于restartPolicy: Never且一直失败的 Job必须手动删除它[rootmaster k8s]# kubectl delete job myjob删除 Job 时由其创建的所有 Pod 也会被一并删除。四、定时执行JobCronJobLinux 系统中有crontab可以定时执行任务Kubernetes 也提供了CronJob来实现定时触发 Job 的功能。1. CronJob 配置文件示例apiVersion: batch/v1beta1 kind: CronJob metadata: name: hellojob spec: schedule: */2 * * * * # 每2分钟执行一次 jobTemplate: spec: template: spec: containers: - name: hello image: busybox command: [echo, hello k8s cron_job!] restartPolicy: OnFailure2. 关键字段说明字段说明apiVersionCronJob 的 API 版本较新集群推荐batch/v1旧版为batch/v1beta1kindCronJobschedule与 Linux crontab 格式一致分 时 日 月 周。*/2 * * * *表示每 2 分钟执行一次jobTemplateJob 模板内部结构与普通的 Job 完全相同restartPolicy对于 CronJob 产生的 Job通常设置为OnFailure或Never3. 操作示例# 创建 CronJob [rootmaster k8s]# kubectl apply -f cronjob.yml cronjob.batch/hellojob created # 查看 CronJob 状态 [rootmaster k8s]# kubectl get cronjob NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE hellojob */2 * * * * False 0 none 5s # 等待几分钟后查看由此创建的 Job [rootmaster k8s]# kubectl get job NAME COMPLETIONS DURATION AGE hellojob-1596513900 1/1 2s 2m # 查看产生的 Pod [rootmaster k8s]# kubectl get pod NAME READY STATUS RESTARTS AGE hellojob-1596513900-hl7nv 0/1 Completed 0 2m # 查看 Pod 日志 [rootmaster k8s]# kubectl logs hellojob-1596513900-hl7nv hello k8s cron_job! # 删除 CronJob会自动删除关联的 Job 和 Pod [rootmaster k8s]# kubectl delete -f cronjob.yml4. CronJob 常用调度格式表达式含义*/1 * * * *每分钟执行一次0 */2 * * *每2小时整点执行0 8 * * *每天早上8点执行0 0 * * 0每周日午夜执行0 0 1 * *每月1号午夜执行注意CronJob 的时区默认使用 kube-controller-manager 的时区通常是 UTC。如需指定时区可参考官方文档设置timezone字段v1.25 支持。结语Kubernetes Job 为运行一次性批处理任务提供了完整的生命周期管理能力。通过本文的学习我们掌握了使用场景区分服务类容器与工作类容器明确 Job 的定位。基本配置YAML 文件的各个字段含义特别是restartPolicy在 Job 中的限制。失败处理策略Never会导致无限创建新 PodOnFailure会在同一 Pod 内重启容器。生产环境中应根据任务性质选择合适的策略避免资源浪费。定时任务利用 CronJob 可以轻松实现周期性任务调度其语法与 Linux crontab 完全一致。在实际使用中建议为 Job 设置backoffLimit默认为6来限制重试次数避免错误任务无限循环对于运行时间较长的批处理任务还可以结合activeDeadlineSeconds设置任务超时时间。Job 与 CronJob 是 Kubernetes 中处理任务型负载的利器掌握它们之后你可以将更多的运维和数据处理任务无缝接入到容器化平台中实现统一调度与管理。