使用展开的方式进行并行处理

    在本任务示例中,只有三个工作条目:applebananacherry。 示例任务处理每个条目时打印一个字符串之后结束。

    参考在真实负载中使用 Job了解更适用于真实使用场景的模式。

    你应先熟悉基本的、非并行的 Job 的用法。

    你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

    任务中的基本模板示例要求安装命令行工具 。 要使用较高级的模板示例,你需要安装 Python, 并且要安装 Jinja2 模板库。

    一旦 Python 已经安装好,你可以运行下面的命令安装 Jinja2:

    基于模板创建 Job

    首先,将以下作业模板下载到名为 job-tmpl.yaml 的文件中。

    application/job/job-tmpl.yaml

    1. apiVersion: batch/v1
    2. kind: Job
    3. metadata:
    4. name: process-item-$ITEM
    5. labels:
    6. jobgroup: jobexample
    7. spec:
    8. template:
    9. metadata:
    10. name: jobexample
    11. labels:
    12. jobgroup: jobexample
    13. spec:
    14. containers:
    15. - name: c
    16. image: busybox:1.28
    17. command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
    18. restartPolicy: Never
    1. # 使用 curl 下载 job-tmpl.yaml

    你所下载的文件不是一个合法的 Kubernetes 。 这里的模板只是 Job 对象的 yaml 表示,其中包含一些占位符,在使用它之前需要被填充。 $ITEM 语法对 Kubernetes 没有意义。

    下面的 Shell 代码片段使用 sed 将字符串 $ITEM 替换为循环变量,并将结果 写入到一个名为 jobs 的临时目录。

    1. # 展开模板文件到多个文件中,每个文件对应一个要处理的条目
    2. mkdir ./jobs
    3. for i in apple banana cherry
    4. do
    5. cat job-tmpl.yaml | sed "s/\$ITEM/$i/" > ./jobs/job-$i.yaml
    6. done

    检查上述脚本的输出:

    输出类似于:

    1. job-apple.yaml
    2. job-banana.yaml
    3. job-cherry.yaml

    你可以使用任何一种模板语言(例如:Jinja2、ERB),或者编写一个程序来 生成 Job 清单。

    输出类似于:

    1. job.batch/process-item-apple created
    2. job.batch/process-item-banana created
    3. job.batch/process-item-cherry created

    现在检查 Job:

    1. kubectl get jobs -l jobgroup=jobexample

    输出类似于:

    1. NAME COMPLETIONS DURATION AGE
    2. process-item-apple 1/1 14s 22s
    3. process-item-banana 1/1 12s 21s
    4. process-item-cherry 1/1 12s 20s

    使用 kubectl 的 -l 选项可以仅选择属于当前 Job 组的对象 (系统中可能存在其他不相关的 Job)。

    你可以使用相同的 标签选择算符 来过滤 Pods:

    1. kubectl get pods -l jobgroup=jobexample

    输出类似于:

    1. NAME READY STATUS RESTARTS AGE
    2. process-item-apple-kixwv 0/1 Completed 0 4m
    3. process-item-banana-wrsf7 0/1 Completed 0 4m
    4. process-item-cherry-dnfu9 0/1 Completed 0 4m

    我们可以用下面的命令查看所有 Job 的输出:

    输出类似于:

    1. Processing item apple
    2. Processing item banana
    3. Processing item cherry
    1. # 删除所创建的 Job
    2. # 集群会自动清理 Job 对应的 Pod
    3. kubectl delete job -l jobgroup=jobexample

    在中,模板的每个示例都有一个参数 而该参数也用在 Job 名称中。不过,对象 名称 被限制只能使用某些字符。

    这里的略微复杂的例子使用 来生成清单,并基于清单来生成对象,每个 Job 都有多个参数。

    在本任务中,你将会使用一个一行的 Python 脚本,将模板转换为一组清单文件。

    首先,复制下面的 Job 对象模板到一个名为 job.yaml.jinja2 的文件。

    1. {% set params = [{ "name": "apple", "url": "http://dbpedia.org/resource/Apple", },
    2. { "name": "banana", "url": "http://dbpedia.org/resource/Banana", },
    3. %}
    4. {% for p in params %}
    5. {% set name = p["name"] %}
    6. {% set url = p["url"] %}
    7. apiVersion: batch/v1
    8. kind: Job
    9. metadata:
    10. name: jobexample-{{ name }}
    11. labels:
    12. jobgroup: jobexample
    13. spec:
    14. template:
    15. metadata:
    16. name: jobexample
    17. labels:
    18. jobgroup: jobexample
    19. spec:
    20. containers:
    21. - name: c
    22. image: busybox:1.28
    23. command: ["sh", "-c", "echo Processing URL {{ url }} && sleep 5"]
    24. restartPolicy: Never
    25. {% endfor %}

    上面的模板使用 python 字典列表(第 1-4 行)定义每个作业对象的参数。 然后使用 for 循环为每组参数(剩余行)生成一个作业 yaml 对象。 我们利用了多个 YAML 文档(这里的 Kubernetes 清单)可以用 --- 分隔符连接的事实。 我们可以将输出直接传递给 kubectl 来创建对象。

    1. alias render_template='python -c "from jinja2 import Template; import sys; print(Template(sys.stdin.read()).render());"'

    使用 render_template 将参数和模板转换成一个 YAML 文件,其中包含 Kubernetes 资源清单:

    1. # 此命令需要之前定义的别名
    2. cat job.yaml.jinja2 | render_template > jobs.yaml

    你可以查看 jobs.yaml 以验证 render_template 脚本是否正常工作。

    当你对输出结果比较满意时,可以用管道将其输出发送给 kubectl,如下所示:

    Kubernetes 接收清单文件并执行你所创建的 Job。

    1. # 删除所创建的 Job
    2. # 集群会自动清理 Job 对应的 Pod
    3. kubectl delete job -l jobgroup=jobexample

    在真实负载中使用 Job

    在真实的负载中,每个 Job 都会执行一些重要的计算,例如渲染电影的一帧, 或者处理数据库中的若干行。这时,$ITEM 参数将指定帧号或行范围。

    在此任务中,你运行一个命令通过取回 Pod 的日志来收集其输出。 在真实应用场景中,Job 的每个 Pod 都会在结束之前将其输出写入到某持久性存储中。 你可以为每个 Job 指定 PersistentVolume 卷,或者使用其他外部存储服务。 例如,如果你在渲染视频帧,你可能会使用 HTTP 协议将渲染完的帧数据 用 ‘PUT’ 请求发送到某 URL,每个帧使用不同的 URl。

    你创建了 Job 之后,Kubernetes 自动为 Job 的 Pod 添加 ,以便能够将一个 Job 的 Pod 与另一个 Job 的 Pod 区分开来。

    在本例中,每个 Job 及其 Pod 模板有一个标签: jobgroup=jobexample

    Kubernetes 自身对标签名 没有什么要求。 为创建自同一模板的所有 Job 使用同一标签使得我们可以方便地同时操作组中的所有作业。 在第一个例子中,你使用模板来创建了若干 Job。 模板确保每个 Pod 都能够获得相同的标签,这样你可以用一条命令检查这些模板化 Job 所生成的全部 Pod。

    说明: 标签键 jobgroup 没什么特殊的,也不是保留字。 你可以选择你自己的标签方案。 如果愿意,有一些 可供使用。

    替代方案

    如果你有计划创建大量 Job 对象,你可能会发现:

    • 即使使用标签,管理这么多 Job 对象也很麻烦。
    • 你可能会受到 Job 相关的 限制:如果你在一个批量请求中触发了太多的任务,API 服务器会永久性地拒绝你的某些请求。

    还有一些其他作业模式 可供选择,这些模式都能用来处理大量任务而又不会创建过多的 Job 对象。

    你也可以考虑编写自己的 来自动管理 Job 对象。