带静态工作分配的索引式并行处理作业
Kubernetes v1.24 [stable]
在此示例中,你将运行一个使用多个并行工作进程的 Kubernetes Job。每个工作进程是运行在自己 Pod 中的不同容器。Pod 具有控制平面自动设置的*索引号*,这使得每个 Pod 可以识别整个任务的哪个部分来工作。
Pod 索引可在注解 batch.kubernetes.io/job-completion-index
中获取,其格式为表示十进制值的字符串。为了让容器化的任务进程获取此索引,你可以使用Downward API 机制发布注解的值。为方便起见,控制平面自动设置 Downward API,以在环境变量 JOB_COMPLETION_INDEX
中暴露该索引。
下面是此示例的步骤概述
- 使用索引式完成定义 Job 清单。Downward API 允许你将 Pod 索引注解作为环境变量或文件传递给容器。
- 基于该清单启动一个
Indexed
Job.
开始之前
你应该已经熟悉 Job 的基本非并行用法。
你需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。建议在至少有两个不是控制平面主机的节点上运行此教程。如果你还没有集群,可以使用 minikube 创建一个,或者你可以使用以下 Kubernetes 练手环境之一:
你的 Kubernetes 服务器版本必须是 v1.21 或更高。要检查版本,请输入 kubectl version
。
选择一种方法
要从工作程序访问工作项,你有以下几种选择:
- 读取
JOB_COMPLETION_INDEX
环境变量。Job 控制器自动将此变量链接到包含完成索引的注解。 - 读取包含完成索引的文件。
- 假设你不能修改程序,你可以用一个脚本来封装它,该脚本使用上述任何方法读取索引,并将其转换为程序可以作为输入使用的内容。
对于此示例,假设你选择了选项 3,并且想要运行 rev 工具。这个程序接受一个文件作为参数,并以相反的顺序打印其内容。
rev data.txt
你将使用来自 busybox
容器镜像的 rev
工具。
由于这只是一个示例,每个 Pod 只做一小部分工作(反转一个短字符串)。在实际工作负载中,你可能例如创建一个 Job,表示根据场景数据生成 60 秒视频的任务。视频渲染 Job 中的每个工作项将是渲染该视频片段的特定帧。索引式完成意味着 Job 中的每个 Pod 都知道要渲染和发布的帧,通过从片段的开头计算帧数来实现。
定义索引式 Job
下面是使用 Indexed
完成模式的示例 Job 清单
apiVersion: batch/v1
kind: Job
metadata:
name: 'indexed-job'
spec:
completions: 5
parallelism: 3
completionMode: Indexed
template:
spec:
restartPolicy: Never
initContainers:
- name: 'input'
image: 'docker.io/library/bash'
command:
- "bash"
- "-c"
- |
items=(foo bar baz qux xyz)
echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt
volumeMounts:
- mountPath: /input
name: input
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command:
- "rev"
- "/input/data.txt"
volumeMounts:
- mountPath: /input
name: input
volumes:
- name: input
emptyDir: {}
在上面的示例中,你使用了 Job 控制器为所有容器设置的内置 JOB_COMPLETION_INDEX
环境变量。一个 初始化容器 将索引映射到静态值,并将其写入一个文件,该文件通过一个 emptyDir 卷 与运行工作进程的容器共享。或者,你可以 通过 Downward API 定义自己的环境变量 来向容器发布索引。你还可以选择从 ConfigMap 中加载值列表作为环境变量或文件。
另外,你可以直接使用 Downward API 将注解值作为卷文件传递,如下例所示
apiVersion: batch/v1
kind: Job
metadata:
name: 'indexed-job'
spec:
completions: 5
parallelism: 3
completionMode: Indexed
template:
spec:
restartPolicy: Never
containers:
- name: 'worker'
image: 'docker.io/library/busybox'
command:
- "rev"
- "/input/data.txt"
volumeMounts:
- mountPath: /input
name: input
volumes:
- name: input
downwardAPI:
items:
- path: "data.txt"
fieldRef:
fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
运行 Job
现在运行 Job
# This uses the first approach (relying on $JOB_COMPLETION_INDEX)
kubectl apply -f https://kubernetes.ac.cn/examples/application/job/indexed-job.yaml
创建此 Job 后,控制平面会创建一系列 Pod,每个指定的索引对应一个。.spec.parallelism
的值决定了可以同时运行多少个,而 .spec.completions
决定了 Job 总共创建多少个 Pod。
由于 .spec.parallelism
小于 .spec.completions
,控制平面会在一些首批 Pod 完成后才启动更多 Pod。
你可以等待 Job 成功,设置一个超时时间
# The check for condition name is case insensitive
kubectl wait --for=condition=complete --timeout=300s job/indexed-job
现在,描述 Job 并检查它是否成功。
kubectl describe jobs/indexed-job
输出类似于
Name: indexed-job
Namespace: default
Selector: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
Labels: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
job-name=indexed-job
Annotations: <none>
Parallelism: 3
Completions: 5
Start Time: Thu, 11 Mar 2021 15:47:34 +0000
Pods Statuses: 2 Running / 3 Succeeded / 0 Failed
Completed Indexes: 0-2
Pod Template:
Labels: controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
job-name=indexed-job
Init Containers:
input:
Image: docker.io/library/bash
Port: <none>
Host Port: <none>
Command:
bash
-c
items=(foo bar baz qux xyz)
echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt
Environment: <none>
Mounts:
/input from input (rw)
Containers:
worker:
Image: docker.io/library/busybox
Port: <none>
Host Port: <none>
Command:
rev
/input/data.txt
Environment: <none>
Mounts:
/input from input (rw)
Volumes:
input:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-njkjj
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-9kd4h
Normal SuccessfulCreate 4s job-controller Created pod: indexed-job-qjwsz
Normal SuccessfulCreate 1s job-controller Created pod: indexed-job-fdhq5
Normal SuccessfulCreate 1s job-controller Created pod: indexed-job-ncslj
在这个示例中,你为每个索引运行 Job 并使用自定义值。你可以检查其中一个 Pod 的输出
kubectl logs indexed-job-fdhq5 # Change this to match the name of a Pod from that Job
输出类似于
xuq