用于静态工作分配的索引作业
Kubernetes v1.24 [stable]
在此示例中,你将运行一个 Kubernetes Job,它使用多个并行工作进程。 每个工作进程都是一个在各自 Pod 中运行的不同容器。 Pod 具有由控制面自动设置的**索引号**,这使得每个 Pod 都能识别要处理的整体任务的哪个部分。
Pod 索引号可以在annotation `batch.kubernetes.io/job-completion-index` 中以十进制字符串表示。为了使容器化任务进程获取此索引,你可以使用downward API 机制发布该 annotation 的值。为方便起见,控制平面会自动设置 downward API,以在 `JOB_COMPLETION_INDEX` 环境变量中公开该索引。
以下是本示例中的步骤概述:
- 使用索引完成定义 Job 清单。downward API 允许你将 Pod 索引 annotation 作为环境变量或文件传递给容器。
- 根据清单启动一个 `Indexed` Job.
准备工作
你应该已经熟悉 Job 的基本非并行用法。
你需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。 建议你在至少有两个不充当控制平面主机的节点的集群上运行本教程。 如果你还没有集群,可以使用 minikube 创建一个,或者使用这些 Kubernetes 操场之一
你的 Kubernetes 服务器版本必须是 v1.21 或更高。要检查版本,请输入 kubectl version
。
选择一种方法
要从 worker 程序访问工作项,你有以下几种选择
- 读取 `JOB_COMPLETION_INDEX` 环境变量。Job 控制器自动将此变量链接到包含完成索引的 Annotation。
- 读取包含完成索引的文件。
- 假设你无法修改程序,你可以用一个脚本将其包装起来,该脚本使用上述任何方法读取索引并将其转换为程序可以用作输入的内容。
对于此示例,假设你选择了选项 3,并且你想运行 rev 工具。此程序接受一个文件作为参数,并反向打印其内容。
rev data.txt
你将使用 `busybox` 容器镜像中的 `rev` 工具。
由于这只是一个示例,每个 Pod 只做一点点工作(反转一个短字符串)。在实际工作负载中,你可能会创建一个 Job,例如,该 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` 环境变量。一个Init 容器将索引映射到一个静态值,并将其写入一个文件,该文件通过emptyDir 卷与运行 worker 的容器共享。你还可以选择通过 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,每个 Pod 对应你指定的每个索引。`。spec.parallelism` 的值决定了可以同时运行多少个 Pod,而 `。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