使用擴充套件進行並行處理

本任務演示如何基於通用模板執行多個Job。你可以使用此方法並行處理批處理工作。

本示例中只有三個專案:_apple_、_banana_ 和 _cherry_。示例 Job 透過列印字串然後暫停來處理每個專案。

請參閱在實際工作負載中使用 Job,瞭解此模式如何適用於更真實的用例。

準備工作

你應該熟悉 Job 的基本、非並行用法。

你需要擁有一個 Kubernetes 叢集,並且 kubectl 命令列工具必須配置為與你的叢集通訊。建議在至少有兩個不作為控制平面主機的節點的叢集上執行本教程。如果你還沒有叢集,可以使用 minikube 建立一個叢集,或者你可以使用這些 Kubernetes 演練場之一

對於基本模板,你需要命令列工具 sed

要遵循高階模板示例,你需要安裝可工作的 Python 以及 Python 的 Jinja2 模板庫。

設定好 Python 後,可以透過執行以下命令安裝 Jinja2:

pip install --user jinja2

基於模板建立 Job

首先,將以下 Job 模板下載到名為 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 清單。相反,該模板是 Job 物件的 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),或者編寫程式來生成 Job 清單。

從清單建立 Job

接下來,使用一個 kubectl 命令建立所有 Job:

kubectl create -f ./jobs

輸出類似於:

job.batch/process-item-apple created
job.batch/process-item-banana created
job.batch/process-item-cherry created

現在,檢查 Job:

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

使用 kubectl 的 -l 選項僅選擇屬於此 Job 組的 Job(系統中可能存在其他不相關的 Job)。

你也可以使用相同的標籤選擇器檢查 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

我們可以使用此單個命令一次性檢查所有 Job 的輸出:

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

使用高階模板引數

第一個示例中,模板的每個例項都有一個引數,該引數也用於 Job 的名稱。但是,名稱被限制為只包含某些字元。

這個稍微複雜一些的示例使用 Jinja 模板語言從清單生成清單,然後從這些清單生成物件,每個 Job 具有多個引數。

對於本任務的這一部分,你將使用一個單行 Python 指令碼將模板轉換為一組清單。

首先,將以下 Job 物件的模板複製並貼上到名為 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 行)為每個 Job 物件定義了兩個引數。一個 for 迴圈為每組引數發出一個 Job 清單(其餘行)。

此示例依賴於 YAML 的一個特性。一個 YAML 檔案可以包含多個文件(在本例中為 Kubernetes 清單),由單獨一行上的 --- 分隔。你可以將輸出直接透過管道傳輸到 kubectl 以建立 Job。

接下來,使用這個單行 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 接受並執行你建立的 Job。

清理

# Remove the Jobs you created
# Your cluster automatically cleans up their Pods
kubectl delete job -l jobgroup=jobexample

在實際工作負載中使用 Job

在實際用例中,每個 Job 都執行一些重要的計算,例如渲染電影的幀,或處理資料庫中的一系列行。如果你正在渲染電影,你可以將 $ITEM 設定為幀號。如果你正在處理資料庫表中的行,你可以將 $ITEM 設定為表示要處理的資料庫行範圍。

在本任務中,你透過獲取 Pod 的日誌來收集 Pod 的輸出。在實際用例中,Job 的每個 Pod 在完成之前將其輸出寫入持久儲存。你可以為每個 Job 使用 PersistentVolume,或者使用外部儲存服務。例如,如果你正在渲染電影幀,可以使用 HTTP 將渲染的幀資料 PUT 到 URL,每個幀使用不同的 URL。

Job 和 Pod 上的標籤

建立 Job 後,Kubernetes 會自動新增額外的標籤,以區分一個 Job 的 Pod 和另一個 Job 的 Pod。

在此示例中,每個 Job 及其 Pod 模板都有一個標籤:jobgroup=jobexample

Kubernetes 本身不關心名為 jobgroup 的標籤。為從模板建立的所有 Job 設定標籤,可以方便地一次性操作所有這些 Job。在第一個示例中,你使用模板建立了幾個 Job。該模板確保每個 Pod 也獲得相同的標籤,因此你可以使用一個命令檢查這些模板化 Job 的所有 Pod。

替代方案

如果你計劃建立大量的 Job 物件,你可能會發現:

  • 即使使用標籤,管理如此多的 Job 也很麻煩。
  • 如果你在批處理中建立許多 Job,你可能會給 Kubernetes 控制平面帶來高負載。或者,Kubernetes API 伺服器可能會對你進行速率限制,暫時拒絕你的請求,狀態為 429。
  • 你受到 Job 的資源配額限制:當你在一個批處理中建立大量工作時,API 伺服器會永久拒絕你的一些請求。

還有其他Job 模式,你可以使用它們來處理大量工作,而無需建立非常多的 Job 物件。

你還可以考慮編寫自己的控制器來自動管理 Job 物件。

最後修改於 2023 年 8 月 24 日下午 6:38 PST:使用 code_sample 簡碼代替 code 簡碼 (e8b136c3b3)