K8sController-Job(批处理任务)

基于K8s 1.31

主要配置和工作机制

apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
# 可以并行任务数量,默认1
parallelism: 3
completions: 3
# Pod完成模式,NonIndexed(数量达到completions推出,默认),Indexed
# Indexed模式:会被设置为Pod服务名
# - 设置Pod名称:<Job Name>-<索引序号>-<随机字符串>
# - 设置Annotation "batch.kubernetes.io/job-completion-index": <索引序号>
# - 设置Label "batch.kubernetes.io/job-completion-index": <索引序号>,从K8s v1.28 需要开启PodIndexLabel特性门控
# - 在容器内注入环境变量JOB_COMPLETION_INDEX,值为索引序号
# - 与一个Headless Service搭配使用,Pod网络访问域名将被设置为<$(job_name)-$(index)>.<headless-service-name>,并且设置正确的DNS记录
completionMode: NonIndexed
# 失败任务的最大重试次数上限
backoffLimit: 4
# K8s v1.28 开始支持,Pod失效时候,支持Ignore、FailJob、Count或FailIndex
podFailurePolicy:
rules:
- action: FailJob
# K8s v1.28 开始支持,Pod失效时候,替换策略
podReplacementPolicy: OnPodFailure
# K8s v1.27 引入手动设置标签选择器
manualSelector: true
template:
spec:
restartPolicy: Never
containers:
- name: hello
image: busybox
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster

并行处理机制和常用模式

Job适用于3种形式运行的Job

  1. 单个一次性任务:通常一个Job只启动一个Pod,除非Pod异常,才会重启该Pod,一旦该Pod正常结束,Job将完成
  2. 具有指定完成数量的并行任务,并行任务会启动多个Pod
  3. 带有工作队列的并行任务:任务队列的方式并行Pod需要一个独立的Queue,工作项都在一个Queue中存放,不能设置Job的Spec.completions参数,Job此刻有以下的特性
    1. 多个Pod之间必须能够协调好分别处理哪个工作项,或者需要借助外部服务来确定
    2. 每个Pod都能确定其他Pod是否完成工作,进而确定Job是否完成
    3. 如果某个Pod正常结束,则Job不会再启动新的Pod
    4. 如果一个Pod成功结束,则此时应该不存在其他Pod还在工作的情况,它们应该处于即将结束、退出的状态

在并行运行的情况下,实际运行的Pod数量可能为parallelism设置的略大或者略小,可能原因如下:

  • 对于具有指定完成的并行数量(completions>0),实际运行的Pod数量不会超过未完成的剩余数量,如果parallelism比cmpletions的值大,系统会忽略parallelism的设置
  • 对于带有工作队列的并行任务,只要有一个Pod成功结束,就不会创建新的Pod,并且剩下的Pod开始自动退出流程
  • 在Job工作负载控制器没有即使响应,实际运行的Pod数量可能略少
  • 当Job Controller在某些情况下无法创建Pod(资源不足),可能略少
  • Job Controller因为同一个Pod失败次数太多而不再创建新的Pod
  • 在某个Pod处于优雅终止的过程中,需要消耗更多时间Pod才能终止,实际运行Pod数量可能略多

Pod失效时的处理机制

对于Job的Pod的restartPolicy只能被设置为Never和OnFailed,不能被设置为Always

K8s为了更好的控制Pod失效之后,设置了一些新的配置,失效策略(podFailurePolicy)、失效次数上限(backoffLimit)、每个索引失效的次数上限(backoffLimitPerIndex)等机制

使用Pod失效次数上限(backoffLimit)控制Pod终止时机

在某些场景下,看你要求Pod在不断失败经过有限的重试次数就不再尝试,而是将Job置为失败的情况,可以通过设置backoffLimit进行控制,默认值为6。在Pod失效后,Job Controller会重建Pod,而重试间隔从10s开始,后一次是前一次的2倍,最长时间为6min

判断Job处于失败的计算逻辑:以下俩个值之一达到用backoffLimit设置的次数,系统就设置Job的状态为失败,并且删除一直失败的Pod

  1. status.phase=”Failed”时的Pod数量
  2. restartPolicy=OnFailure时,status.phase=”Pending”或”Running”的Pod,其中容器的重启次数

使用每个索引的失效次数上限(backoffLimitPerIndex)控制器何时终止带索引的Job

从K8s 1.28开始,引入了针对带索引的Job的失效次数上限设置,需要开启K8s各个服务的JobBackoffLimitPerIndex的特性门控启用,该特性到v1.29版进入到Beta

使用该特性:必须设置completionMode=Indexed,Pod的restartPolicy:Never

  • 容器计算逻辑:当环境变量JOB_COMPLETOP_INDEX为基数异常退出,偶数正常退出

Pod失效策略

从K8s v1.25版本开始,引入了Pod失效策略(podFailurePolicy),使用用户可以根据某些条件更好的控制失效时的处理方式,该机制在v1.26进入Beta,需要开启特性门控JobPodFailurePolicy特性门控

常见的应用场景:

  • 根据特定的退出码立刻终止Job无需重试
  • 忽略某些干扰因素(例如优先级抢占、被驱逐)等导致的Pod失效,不应收到基于backoffLimit的失效次数重试
spec
# K8s v1.28 开始支持,Pod失效时候,支持Ignore、FailJob、Count或FailIndex
podFailurePolicy:
# 按照顺序对多个规则进行评估,一旦某个规则匹配,则立即停止评估后续规则
rules:
# FailJob: 立即终止Job并且标记为Failed,同时停止全部未终止的Pod
# Ignore: 重建一个新的Pod,同时不计入backoffLimit
# FailIndex: 在使用索引的情况下,将失效索引的Pod设置为不再重试
- action: FailJob
# onPodConditions:根据Pod的情况,采用什么样子的action
# onExitCode: 根据Pod退出码,采用什么样子的action
onPodConditions:
- type: Disrupted
status: "true"

在使用Pod失效策略的时候,Job Controller 只对于失效阶段的Pod进行规则匹配,不考试Succeeded或者Terminating的Pod

从K8s的V1.27版中,kubelet会将已经删除的Pod转换到终止阶段(Failed或者Succeeded),转换为失效阶段的Pod将也被Pod失效策略纳入考虑

Pod替换策略

从K8s v1.28引入新的Pod替换策略(podReplacementPolicy)机制,用于管理创建替换Pod对时间,该特性v1.29版本上处于Beta通过开启JobPodReplacementPolicy特性门控进行启用

在默认情况下,在Pod失效和停止中时,Job Controller会立刻创建一个新的替换Pod。Pod 替换策略允许用户通过控制器延迟来创建新的替换Pod,例如,设置pod ReplacementPolicy=Failed来表示只在Pod完全失效(statsu.phase=Failed)才让系统替换Pod

  • Failed:失效Pod处于失效状态,重建替换Pod
  • TerminatingOrFailed:失效Pod处于停止中和失效状态,重建替换Pod,这是系统默认的策略

从K8s v1.28版本后,在启用Pod失效策略机制时,Job Controller会判断仅当Pod为失效状态,才重建新的替换Pod。如果设置Pod替换策略,只允许设置podReplacementPolicy=Failed

Job的终止和清理机制

Job在结束之后,不会创建新的Pod,通常也不会删除已经结束的Pod。保留结束的Pod,可以提供用户执行查看容器日志、排查勘误等操作

从K8s v1.20开始,引入了ttlSecondsAfterFinished字段,用于设置TTL Controller机制,到v1.22阶段进入到Stable阶段

spec
# Pod最长运行时间,单位s
activeDeadlineSeconds: 2
# 用于设置TTL Controller自动清理机制
ttlSecondsAfterFinished: 10

Job的挂起与恢复

K8s从开始,引入一个新的刮起的特性,通过一个新字段suspend,暂停对Pod得创建操作,在v1.24进入Stable

需要挂起Job,只需要设计suspend=true,如果初始化设置了pod suspend=true,那吗创建Pod之后处于挂起