用于并行处理的带索引作业,具有静态工作分配

功能状态: Kubernetes v1.24 [稳定]

在这个示例中,您将运行一个使用多个并行工作进程的 Kubernetes Job。每个工作进程都是一个在自己的 Pod 中运行的不同容器。这些 Pod 具有由控制平面自动设置的索引号,这使得每个 Pod 都可以识别要处理的整体任务的哪一部分。

Pod 索引在注释 batch.kubernetes.io/job-completion-index 中可用,它是一个字符串,表示其十进制值。为了让容器化的任务进程获取此索引,您可以使用下行 API 机制发布注释的值。为了方便起见,控制平面会自动设置下行 API 以在JOB_COMPLETION_INDEX 环境变量中公开索引。

以下是此示例中步骤的概述

  1. 使用索引完成定义 Job 清单。 下行 API 允许您将 Pod 索引注释作为环境变量或文件传递给容器。
  2. 根据该清单启动一个Indexed Job.

在您开始之前

您应该已经熟悉Job 的基本非并行使用。

您需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议您在至少有两个节点的集群上运行本教程,这些节点不充当控制平面主机。如果您还没有集群,您可以使用minikube 创建一个集群,或者您可以使用以下 Kubernetes 游乐场之一

您的 Kubernetes 服务器必须是 v1.21 或更高版本。要检查版本,请输入kubectl version

选择一种方法

要访问来自工作程序程序的工作项,您有几个选项

  1. 读取JOB_COMPLETION_INDEX 环境变量。Job 控制器 会自动将此变量链接到包含完成索引的注释。
  2. 读取包含完成索引的文件。
  3. 假设您无法修改程序,您可以使用一个脚本对其进行包装,该脚本使用上述任何方法读取索引,并将其转换为程序可以作为输入使用的内容。

在本示例中,假设您选择了选项 3,并且您想要运行rev 实用程序。该程序接受一个文件作为参数,并打印其内容的反转形式。

rev data.txt

您将使用来自busybox 容器镜像的rev 工具。

由于这只是一个示例,因此每个 Pod 仅执行一小部分工作(反转一个短字符串)。在实际工作负载中,您可能会创建一个代表根据场景数据生成 60 秒视频的任务的 Job。视频渲染 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 卷 与运行工作程序的容器共享。或者,您可以通过下行 API 定义自己的环境变量 以将索引发布到容器。您也可以选择从ConfigMap 加载一个值列表作为环境变量或文件

或者,您可以直接使用下行 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 的值决定一次可以运行多少个 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
最后修改时间:2023 年 8 月 24 日下午 6:38 PST:使用 code_sample 短代码而不是 code 短代码 (e8b136c3b3)