k8s集群Job Pod 容器可能因为多种原因失效,想要更加稳定的使用Job负载,有哪些需要注意的地方?
面试官:“计数性Job默认完成模式是什么?Indexed模式如何发布自定义索引呢?”
面试官:“k8s的Job Pod 中的容器可能因为多种不同原因失效,想要更加稳定的使用Job负载,有哪些可以注意的地方?“
面试官:“为什么k8s建议在调试 Job 时将
restartPolicy
设置为 “Never”?”面试官:“Job 终止与清理了解嘛?Pod重试次数还未 达到
backoffLimit
所设的限制,为什么突然被终止了?猜测原因?“
囧么肥事-胡说八道
计数性Job默认完成模式是什么?Indexed模式如何发布自定义索引呢?
计数性Job默认完成模式是无索引模式
NonIndexed
。
实际上,带有
确定完成计数
的 Job,即
.spec.completions
不为 null 的 Job, 都可以在其
.spec.completionMode
中设置完成模式:
NonIndexed
(默认)和
Indexed
两种。
先看默认模式NonIndexed,无索引模式
👨💻
1、每个Job完成事件都是独立无关且同质的
2、成功完成的Pod个数达到.spec.completions值时认为Job已经完成
3、当.spec.completions取值null时,Job被隐式处理为NonIndexed
再看Indexed,索引模式
👨💻
1、Job 的 Pod 会分配对应的完成索引
2、索引取值为 0 到.spec.completions-1
3、当每个索引都对应一个完成的 Pod 时,Job 被认为是已完成的
4、同一索引值可能被分配给多个Pod,但是只有一个会被记入完成计数
对于索引模式来说,我下发10个索引,我不关注10个索引分别由多少个Pod去完成,我只关注10个索引任务是否按需完成即可。
Indexed模式下,索引有三种获取方式:🤓
-
第一种:基于注解,Pod 索引在注解
batch.kubernetes.io/job-completion-index
中呈现,具体表示为一个十进制值字符串。 -
第二种:基于主机名,作为 Pod 主机名的一部分,遵循模式
$(job-name)-$(index)
。当你同时使用带索引的 Job(Indexed Job)与服务(Service), Job 中的 Pods 可以通过 DNS 使用确切的主机名互相寻址。 -
第三种:基于环境变量,对于容器化的任务,在环境变量
JOB_COMPLETION_INDEX
中体现。
Indexed模式如何发布自定义索引呢?
上面提到了三种获取索引的方式:注解,主机名,环境变量。
Downward API
机制有两种方式可以把将
Pod 和 Container
字段信息呈现给 Pod 中运行的容器:
-
环境变量
-
卷文件
你使用 Job 控制器为所有容器设置的内置
JOB_COMPLETION_INDEX
环境变量。
Init 容器将索引映射到一个静态值
,并将其写入一个文件,该文件
通过 emptyDir 卷与运行 worker 的容器共享
。
举例
👨💻
-
定义使用
带索引完成信息的 Job 清单
。 -
Downward API
将 Pod
索引注释作为环境变量
或
文件
传递给容器。例如环境变量控制平面自动设置 downward API 以在
JOB_COMPLETION_INDEX
环境变量中公开索引 -
根据该清单启动一个带索引(
Indexed
)的 Job。
Pod 中的容器可能因为多种不同原因失效,想要更加稳定的使用Job负载,有哪些可以注意的地方?
首先需要理解的是,失效有两种形式,需要适配的能力也不同。
第一种Pod管理的部分容器失效
第二种Pod失效
第一种Pod管理的部分容器失效
Pod 中的容器可能因为多种不同原因失效,例如因为其中的进程退出时返回值非零, 或者容器因为超出内存约束而被杀死等。
如果发生这类事件,并且
.spec.template.spec.restartPolicy = "OnFailure"
, Pod 则继续留在当前节点,但容器会被重新运行。
面对这种场景,你的程序需要具备能够处理在本地被重启的情况的能力,或者容器设置
.spec.template.spec.restartPolicy = "Never"
注意
,即使你将
.spec.parallelism
设置为 1,且将
.spec.completions
设置为 1,并且
.spec.template.spec.restartPolicy
设置为 “Never”,
同一程序仍然有可能被启动两次
😈,程序猿思维:“永远不要假想某某情况不会发生🤣🤣🤣”。
它就发生了,你能咋滴,不管啊???
🤡🤡🤡
第二种Pod失效
整个 Pod 也可能会失败,且原因各不相同。😇😇😇
例如,当 Pod 启动时,
节点失效(被升级、被重启、被删除等)
🙃
或者其中的
容器失败
并且设置了
.spec.template.spec.restartPolicy = "Never"
。
当 Pod 失败时,Job 控制器会启动一个新的 Pod 替身,去接替失败的Pod未处理完成的工作。
这意味着,你的应用需要处理在一个新 Pod 中被重启的情况。尤其是应用需要
处理之前运行所产生的临时文件、锁、不完整的输出
等问题。
再次注意
😈
如果你将
.spec.parallelism
和
.spec.completions
都设置为比 1 大的值, 那就有可能同时出现多个 Pod 运行的情况。
为此,你的 Pod 也必须能够处理并发性问题☺️。
为什么k8s建议在调试 Job 时将 `restartPolicy` 设置为 “Never”?
回答这个问题前,先看下Job Pod 回退失效策略
在有些情形下,你可能希望
Job 在经历若干次重试之后直接进入失败状态
,因为这很可能意味着Job遇到了配置错误。
-
.spec.backoffLimit
字段设置Job Pod 回退失效策略,标识Job失败重试次数,失效回退的限制值默认为 6。 -
与 Job 相关的
失效的 Pod
会被 Job 控制器
重建
,同时
回退重试时间将会按指数增长
(从 10 秒、20 秒到 40 秒)最多至 6 分钟。 -
当 Job 的 Pod 被删除,或者 Pod 成功时没有其它 Pod 处于失败状态,失效回退的次数也会
被重置
(为 0)。
好了,这下可以回答刚才的问题,
为什么重启策略要设置为Never?
如果你的 Job 的
restartPolicy
被设置为 ”
OnFailure
“,那么该 Job 管理的 Pod 会在 Job 到达
失效回退次数上限时自动被终止
。
Pob 被终止,那么调试 Job 中可执行文件的工作变得非常棘手,难以把控。也许你刚调试没多久,结果Pod终止了,调试过程中断了,绝望不!!!
为了
解决Pod终止后 Jobs 的输出遗失掉的问题
,k8s建议在调试 Job 时将
restartPolicy
设置为 “Never”, 或者使用日志系统来确保失效 Jobs 的输出不会意外遗失。
Job 终止与清理了解嘛?Pod重试次数还未 达到 `backoffLimit` 所设的限制,为什么突然被终止了?猜测原因?
Job终止和清理策略
Job
完成时不会再创建新的 Pod
,不过已有的 Pod
也不会被删除
。
保留这些 Pod 使得你可以
查看已完成的 Pod 的日志输出
,以便检查错误、警告 或者其它诊断性输出。
Job 完成时 Job 对象也一样被保留下来,这样你就可以查看它的状态。
删除老的 Job 的操作留给了用户自己,在查看了 Job 状态之后,你可以使用
kubectl
来删除 Job(例如,
kubectl delete jobs/pi
或者
kubectl delete -f ./job.yaml
)。当使用
kubectl
来删除 Job 时,该 Job 所创建的 Pods 也会被删除。
默认情况下,Job 会持续运行,除非某个 Pod 失败(
restartPolicy=Never
) 或者某个容器出错退出(
restartPolicy=OnFailure
)。这时,Job 基于前述的
spec.backoffLimit
来决定是否以及如何重试。一旦重试次数到达
.spec.backoffLimit
所设的上限,Job 会被标记为失败, 其中运行的 Pods 都会被终止。
终止 Job 的
另一种方式是设置一个活跃期限
。你可以为 Job 的
.spec.activeDeadlineSeconds
设置一个秒数值。该值适用于 Job 的整个生命期,无论 Job 创建了多少个 Pod。一旦 Job 运行时间达到
activeDeadlineSeconds
秒,其所有运行中的 Pod 都会被终止,并且 Job 的状态更新为
type: Failed
及
reason: DeadlineExceeded
。
注意 Job 的
.spec.activeDeadlineSeconds
优先级高于其
.spec.backoffLimit
设置。
因此,如果一个 Job 正在重试一个或多个失效的 Pod,该 Job 一旦到达
activeDeadlineSeconds
所设的时限,即不再部署额外的 Pod,即使其重试次数还未达到
backoffLimit
所设的限制
。
注意问题
Job 规约和 Job 中的Pod 模版规约都有
activeDeadlineSeconds
字段。请确保你在合适的层次设置正确的字段。
还要注意的是,
restartPolicy
对应的是 Pod,而不是 Job 本身:一旦 Job 状态变为
type: Failed
,就不会再发生 Job 重启的动作。换言之,由
.spec.activeDeadlineSeconds
和
.spec.backoffLimit
所触发的 Job 终结机制
都会导致 Job 永久性的失败
,而这类状态都需要手工干预才能解决。