k8s的Job Pod 容器可能因为多种原因失效,想要更加稳定的使用Job负载,有哪些需要注意的地方?
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 永久性的失败,而这类状态都需要手工干预才能解决。