使用扩展进行并行处理
此任务演示了基于通用模板运行多个 作业。您可以使用此方法并行处理批处理工作。
对于此示例,只有三个项目:apple、banana 和 cherry。示例作业通过打印字符串然后暂停来处理每个项目。
请参阅 在实际工作负载中使用作业,了解此模式如何适应更现实的用例。
开始之前
您应该熟悉 作业 的基本非并行使用。
您需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点(不充当控制平面主机)的集群上运行本教程。如果您还没有集群,可以使用 minikube 创建一个,或者可以使用以下 Kubernetes 游乐场之一
对于基本模板,您需要命令行实用程序 sed
。
要遵循高级模板示例,您需要一个工作安装的 Python,以及 Python 的 Jinja2 模板库。
设置好 Python 后,您可以通过运行以下命令安装 Jinja2
pip install --user jinja2
基于模板创建作业
首先,将以下作业模板下载到名为 job-tmpl.yaml
的文件中。以下是您将下载的内容
apiVersion: batch/v1
kind: Job
metadata:
name: process-item-$ITEM
labels:
jobgroup: jobexample
spec:
template:
metadata:
name: jobexample
labels:
jobgroup: jobexample
spec:
containers:
- name: c
image: busybox:1.28
command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
restartPolicy: Never
# Use curl to download job-tmpl.yaml
curl -L -s -O https://k8s.io/examples/application/job/job-tmpl.yaml
您下载的文件还不是有效的 Kubernetes 清单。相反,该模板是作业对象的 YAML 表示形式,其中包含一些需要在使用之前填充的占位符。$ITEM
语法对 Kubernetes 来说没有意义。
从模板创建清单
以下 shell 代码段使用 sed
将字符串 $ITEM
替换为循环变量,写入名为 jobs
的临时目录中。立即运行此代码
# Expand the template into multiple files, one for each item to be processed.
mkdir ./jobs
for i in apple banana cherry
do
cat job-tmpl.yaml | sed "s/\$ITEM/$i/" > ./jobs/job-$i.yaml
done
检查是否成功
ls jobs/
输出类似于以下内容
job-apple.yaml
job-banana.yaml
job-cherry.yaml
您可以使用任何类型的模板语言(例如:Jinja2;ERB),或编写程序来生成作业清单。
从清单创建作业
接下来,使用一个 kubectl 命令创建所有作业
kubectl create -f ./jobs
输出类似于以下内容
job.batch/process-item-apple created
job.batch/process-item-banana created
job.batch/process-item-cherry created
现在,检查作业
kubectl get jobs -l jobgroup=jobexample
输出类似于以下内容
NAME COMPLETIONS DURATION AGE
process-item-apple 1/1 14s 22s
process-item-banana 1/1 12s 21s
process-item-cherry 1/1 12s 20s
使用 -l
选项为 kubectl 仅选择属于此组作业的作业(系统中可能存在其他无关作业)。
您也可以使用相同的 标签选择器 检查 Pod。
kubectl get pods -l jobgroup=jobexample
输出类似于
NAME READY STATUS RESTARTS AGE
process-item-apple-kixwv 0/1 Completed 0 4m
process-item-banana-wrsf7 0/1 Completed 0 4m
process-item-cherry-dnfu9 0/1 Completed 0 4m
我们可以使用此单个命令一次性检查所有作业的输出
kubectl logs -f -l jobgroup=jobexample
输出应为
Processing item apple
Processing item banana
Processing item cherry
清理
# Remove the Jobs you created
# Your cluster automatically cleans up their Pods
kubectl delete job -l jobgroup=jobexample
使用高级模板参数
在 第一个示例 中,模板的每个实例都有一个参数,该参数也用于作业的名称。但是,名称 仅限于包含某些字符。
此稍微复杂一些的示例使用 Jinja 模板语言 生成清单,然后从这些清单中生成对象,每个作业有多个参数。
对于此任务的这一部分,您将使用一个单行 Python 脚本将模板转换为一组清单。
首先,将以下作业对象模板复制粘贴到名为 job.yaml.jinja2
的文件中
{% set params = [{ "name": "apple", "url": "http://dbpedia.org/resource/Apple", },
{ "name": "banana", "url": "http://dbpedia.org/resource/Banana", },
{ "name": "cherry", "url": "http://dbpedia.org/resource/Cherry" }]
%}
{% for p in params %}
{% set name = p["name"] %}
{% set url = p["url"] %}
---
apiVersion: batch/v1
kind: Job
metadata:
name: jobexample-{{ name }}
labels:
jobgroup: jobexample
spec:
template:
metadata:
name: jobexample
labels:
jobgroup: jobexample
spec:
containers:
- name: c
image: busybox:1.28
command: ["sh", "-c", "echo Processing URL {{ url }} && sleep 5"]
restartPolicy: Never
{% endfor %}
上面的模板使用 python 字典列表(第 1-4 行)为每个作业对象定义两个参数。一个 for
循环为每组参数(剩余行)发出一个作业清单。
此示例依赖于 YAML 的一项功能。一个 YAML 文件可以包含多个文档(在本例中为 Kubernetes 清单),这些文档由单独的一行上的 ---
分隔。您可以将输出直接管道到 kubectl
以创建作业。
接下来,使用此单行 Python 程序扩展模板
alias render_template='python -c "from jinja2 import Template; import sys; print(Template(sys.stdin.read()).render());"'
使用 render_template
将参数和模板转换为包含 Kubernetes 清单的单个 YAML 文件
# This requires the alias you defined earlier
cat job.yaml.jinja2 | render_template > jobs.yaml
您可以查看 jobs.yaml
以验证 render_template
脚本是否按预期工作。
一旦您对 render_template
的工作方式感到满意,就可以将其输出管道到 kubectl
cat job.yaml.jinja2 | render_template | kubectl apply -f -
Kubernetes 接受并运行您创建的作业。
清理
# Remove the Jobs you created
# Your cluster automatically cleans up their Pods
kubectl delete job -l jobgroup=jobexample
在实际工作负载中使用作业
在实际用例中,每个作业执行一些大量的计算,例如渲染电影的帧,或处理数据库中的行范围。如果您正在渲染电影,则会将 $ITEM
设置为帧号。如果您正在处理数据库表中的行,则会将 $ITEM
设置为表示要处理的数据库行范围。
在任务中,您运行了一个命令来通过获取 Pod 日志来收集 Pod 的输出。在实际用例中,每个作业的 Pod 在完成之前将其输出写入持久性存储。您可以为每个作业使用持久卷,或使用外部存储服务。例如,如果您正在渲染电影帧,请使用 HTTP 将渲染的帧数据 PUT
到 URL,为每个帧使用不同的 URL。
作业和 Pod 上的标签
创建作业后,Kubernetes 会自动添加其他 标签,以区分一个作业的 Pod 与另一个作业的 Pod。
在此示例中,每个作业及其 Pod 模板都有一个标签:jobgroup=jobexample
。
Kubernetes 本身并不关注名为 jobgroup
的标签。为从模板创建的所有作业设置标签可以方便地一次性操作所有这些作业。在 第一个示例 中,您使用模板创建了多个作业。模板确保每个 Pod 也获得相同的标签,因此您可以使用单个命令检查这些模板作业的所有 Pod。
替代方案
如果您计划创建大量作业对象,您可能会发现
- 即使使用标签,管理如此多的作业也很麻烦。
- 如果您批量创建许多作业,您可能会给 Kubernetes 控制平面带来高负载。或者,Kubernetes API 服务器可能会限制您的速率,暂时拒绝您的请求,并返回 429 状态。
- 您受到 资源配额 对作业的限制:当您在一个批次中创建大量工作时,API 服务器会永久拒绝您的一些请求。
您可以使用其他 作业模式 来处理大量工作,而无需创建太多作业对象。
您也可以考虑编写自己的 控制器 来自动管理作业对象。