AIOps 一场颠覆传统运维的盛筵
842
2022-10-27
kubernetes - 控制器 - Job
控制器 - Job目标介绍 Job创建 Job编写 Job 的定义Job 的终止和清理Job 的自动清理Job 的模式Job 的特殊操作小练习 - 使用扩展进行并行处理什么是控制器 -Jobkubernetes 中的 Job 控制器,可以创建一个或多个 Pod ,并且确保指定数量的 Pod 都可以执行到进程结束当 Job 创建的 Pod 执行完成并正常结束时,Job 会记录 Pod 成功结束的数量当成功结束的 Pod 达到了指定的实例,Job 将完成执行删除 Job 对象时,将清理掉 Job 创建的 Pod一个简单的例子是:创建一个 Job 对象用来确保一个 Pod 的成功执行并结束。在第一个 Pod 执行失败或者被删除(例如,节点硬件故障或机器重启)的情况下,该 Job 对象将创建一个新的 Pod 以重新执行当然,您也可以使用 Job 对象并行执行多个 Pod创建 Job下面举个 Job 的例子,Pod 执行了一个跟 π相关的计算,并打印最终结果apiVersion: batch/v1kind: Jobmetadata: name: pispec: template: spec: containers: - name: pi image: perl command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] restartPolicy: Never backoffLimit: 4 执行下面命令创建 Job 对象kubectl apply -f pi-job.yaml执行下面命令查看结果kubectl describe jobs/pi输出结果Name: piNamespace: defaultSelector: controller-uid=7ba0b221-db44-427a-ae6b-3a6fc778c8caLabels: controller-uid=7ba0b221-db44-427a-ae6b-3a6fc778c8ca job-name=piAnnotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"pi","namespace":"default"},"spec":{"backoffLimit":4,"template":...Parallelism: 1Completions: 1Start Time: Mon, 25 May 2020 11:33:43 +0800Pods Statuses: 1 Running 0 Succeeded 0 FailedPod Template: Labels: controller-uid=7ba0b221-db44-427a-ae6b-3a6fc778c8ca job-name=pi Containers: pi: Image: perl Port:
处理 Pod 和容器的失败Pod 中的容器可能会因为多种原因执行失败,例如:容器中的进程退出了,且退出码不为 0容器因为超出内存限制而被 kill其他原因如果 Pod 中的容器执行失败,且.spec.template.spec.restartPolicy = "OnFailur",则 Pod 将停留在该节点上,但是容器将被重新执行,此时,你的应用程序需要处理在原节点(失败的节点)上重启的情况,或者你可以设置为.spec.template.spec.restartPolicy = "Never"整个 Pod 也会出现多种原因执行失败,例如:Pod 从节点上被驱逐Pod 的容器执行失败,且 .spec.template.spec.restartPolicy = "Never"当 Pod 执行失败后,Job 控制器会新建一个 Pod,此时,你的应用程序需要处理一个新的 Pod 中重启的情况,具体来说,需要处理临时文件,锁,未完成的输出信息以及前一次执行可能留下来的其他东西即使你指定 .spec.parallelism = 1、 .spec.completions = 1 以及 .spec.template.spec.restartPolicy = "Never",同一个应用程序仍然可能被启动多次如果指定 .spec.parallelism 和 .spec.completions 的值都大于 1,则,将可能有多个 Pod 同时执行。此时,你的 Pod 还必须能够处理并发的情况Pod 的重试某些情况下(例如,配置错误),你可能期望在 Job 重试仍然失败的情况下停止 Job,此时,你可以通过.spec.backoffLimit来设定 Job 的最大重试次数,默认是 6Job 中的 Pod 执行失败后,Job 控制器将按照一个指数增大的时间延迟(10s,20s,40s ... 最大为 6 分钟)来多次重新创建 Pod,如果没有新的 Pod 执行失败,则重试次数的计数将被重置如果 restartPolicy = "OnFailure",执行该 Job 的容器在 job 重试次数达到以后将被终止。这种情况使得 Job 程序的 debug 工作变得较为困难。建议在 debug 时,设置 restartPolicy = "Never",或者使用日志系统确保失败的 Job 的日志不会丢失Job 的终止和清理当 Job 完成后:将不会创建新的 Pod已经创建的 Pod 也不会被清理,此时,你仍然可以查看已结束 Pod 的日志Job 也会被保留,以便查看该 Job 的状态由用户决定是否删除已完成 Job 或 Pod可以通过 kubectl delete jobs/
Job的特殊模式在创建 Job 时,系统默认将为其指定一个.spec.selector的取值,并确保其不会与任何其他 Job 重叠在少数情况下,你仍然可能需要覆盖这个自动设置的.spec.selecor的取值,在做此操作时,一定要小心如果指定的 .spec.selector不能确定性的标识出该 Job 的 Pod,并可能选中无关的 Pod (例如,selector 可能选中其他控制器创建的 Pod),则:与该 Job 不相关的 Pod 可能被删除该 Job 可能将不相关的 Pod 纳入到 .spec.completions 的计数一个或多个 Job 可能不能够创建 Pod,或者不能够正常结束如果指定的 .spec.selector 不是唯一的,则其他控制器(例如,Deployment、StatefulSet 等)及其 Pod 的行为也将难以预测在 Kubernetes 中,系统并不能帮助你避免此类型的错误,你需要自己关注所指定的 .spec.selector 的取值是否合理让我们来看一个实际使用 .spec.selector 的例子:假设 Job old 已经运行,你希望已经创建的 Pod 继续运行,但是你又想要修改该 Job 的 名字,同时想要使该 Job 新建的 Pod 使用新的 template。此时你不能够修改已有的 Job 对象,因为这些字段都是不可修改的。此时,你可以执行命令 kubectl delete jobs/old --cascade=false,以删除 Job old 但是保留其创建的 Pod在删除之前先记录 Job old 的 selector,执行命令如下:kubectl get jos/old -o yaml输出结果kind: Jobmetadata: name: old ...spec: selector: matchLabels: controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002 ...创建新的 Job new,并使用已有的 selector。由于已创建的 Pod 带有标签 controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002,这些 Pod 也将被新的 Job new 所管理。使用类似如下的 yaml 文件创建 Job new:kind: Jobmetadata: name: new ...spec: manualSelector: true # 手动指定Selector selector: matchLabels: controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002 # old 的Selector的值 ...当你不使用系统自动创建的 .spec.selector 时,需要在 Job new 中指定 .spec.manualSelector: true新建的 Job new 其 uid 将不同于 a8f3d00d-c6d2-11e5-9f87-42010af00002。设置 .spec.manualSelector: true 意味着,你知道这个设定是有意为之,系统将使用你指定的 .spec.selector,而不是使用 Job new 的 uid 作为 .spec.selector 的取值小练习 - 使用扩展进行并行处理在下面的例子中,我们将使用用一个模板创建多个 kubernetes Job基于模板扩展多模板参数基于模板扩展下面有个 Job 的模板文件:apiVersion: batch/v1kind: Jobmetadata: name: process-item-$ITEM labels: jobgroup: jobexamplespec: template: metadata: name: jobexample labels: jobgroup: jobexample spec: containers: - name: c image: busybox command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"] restartPolicy: Never与 pod template 不同,此处的 job template 并不是一个 Kubernetes API 对象,而仅仅是 Job 对象的 yaml 文件(其中包含一些占位符)。例子文件中的 $ITEM 对 Kubernetes 并没有任何意义,仅仅是一个占位符,必须在使用时用实际数值替换。在此例子中,容器唯一做的事情就是 echo 一个字符串,并等待一小段时间。在实际使用中,应该是某种有意义的计算,例如渲染视频的某一帧图片,或处理数据库中某些记录。模板中的 $ITEM 占位符可用来指定视频的帧数,或者数据库记录的行数。此 Job 及其 Pod template 包含标签 jobgroup=jobexample。对系统来说此标签并没有任何特别之处。该标签可以用来方便地操作这一组 Job。Pod 上也包含该标签,以便我们可以使用一条命令即可检查这些 Job 的所有 Pod。在 Job 创建以后,系统将添加更多的标签以区分不同的 Job 创建的 Pod。jobgroup 并不是 Kubernetes 特殊要求的标签,你可以使用任何标签达到此目的。接下来,下载该模板文件,并将其扩展为多个文件:# 将上面的模板文件保存为 job-tmpl.yaml 文件# 将模板文件扩展为多个文件,并存到一个临时的文件夹mkdir ./jobsfor i in apple banana cherrydo cat job-tmpl.yaml | sed "s/\$ITEM/$i/" > ./jobs/job-$i.yamldone检查输出结果# 执行命令ll jobs/# 输出结果-rw-r--r-- 1 root root 366 May 25 17:13 job-apple.yaml-rw-r--r-- 1 root root 368 May 25 17:13 job-banana.yaml-rw-r--r-- 1 root root 368 May 25 17:13 job-cherry.yaml此处,我们使用 sed 将占位符 $ITEM 替换为循环变量的值。你可以使用任意模板语言(jinja2,erb等)或编写一个程序来生成 Job 对象。接下来,创建这些 Job:kubectl apply -f ./jobs输出结果job.batch/process-item-apple createdjob.batch/process-item-banana createdjob.batch/process-item-cherry created执行命令,检查 Pod 的状态:kubectl get jobs -l jobgroup=jobexample输出结果NAME COMPLETIONS DURATION AGEprocess-item-apple 1/1 37s 37sprocess-item-banana 1/1 26s 37sprocess-item-cherry 1/1 15s 37s此处,我们使用 -l 选项选择这一组 Job。执行如下命令可查看这组 Job 所有的 Pod:kubectl get pods -l jobgroup=jobexample输出结果NAME READY STATUS RESTARTS AGEprocess-item-apple-pn9tv 0/1 Completed 0 78sprocess-item-banana-4f8rg 0/1 Completed 0 78sprocess-item-cherry-m64dm 0/1 Completed 0 78s目前还不能直接使用一个命令去检查所有 Pod 的输出,但是,可以通过一个循环来检查所有 Pod 的输出:for p in $(kubectl get pods -l jobgroup=jobexample -o name)do kubectl logs $pdone输出结果Processing item appleProcessing item bananaProcessing item cherry多模板参数在前面的例子中,每一个模板的实例包含一个参数,同时,此参数也被用作标签。然而,标签的主键是受限于 句法和字符集 的下面这个例子稍微复杂一些,使用了 jinja2 模板语言来生成 Job 对象。我们将使用 python 脚本将模板转换为一个文件。首先,创建一个文件 job.yaml.jinja2,其内容如下所示:{%- set params = [{ "name": "apple", "url": "http://orangepippin.com/apples", }, { "name": "banana", "url": "https://en.wikipedia.org/wiki/Banana", }, { "name": "raspberry", "url": "https://raspberrypi.org/" }]%}{%- for p in params %}{%- set name = p["name"] %}{%- set url = p["url"] %}apiVersion: batch/v1kind: Jobmetadata: name: jobexample-{{ name }} labels: jobgroup: jobexamplespec: template: metadata: name: jobexample labels: jobgroup: jobexample spec: containers: - name: c image: busybox command: ["sh", "-c", "echo Processing URL {{ url }} && sleep 5"] restartPolicy: Never---{%- endfor %}此模板中,使用 python 字典表(1-4行)为每个 Job 对象定义了参数。然后通过一个循环遍历这些参数,并生成 Job 的 yaml 对象文件。多个 yaml 对象可以合并到一个 yaml 文件,使用 --- 分隔即可在运行时,你的环境中需要由 jinja2 包,如果没有,可以通过 pip install --user jinja2 命令安装执行 Python 脚本,以将模板扩展为多个 yaml 文件alias reder_template='python -c "from jinja2 import Template; import sys; print(Template(sys.stdin.read()).render());"'输出的结果保存到一个文件中,例如:cat job.yaml.jinja2 > reder_template > jobs.yaml或者直接通过 kubectl 执行,例如:cat job.yaml.jinja2 | reder_template | kubectl apply -f -
发表评论
暂时没有评论,来抢沙发吧~