Apache Spark、Apache Hadoop、TensorFlow トレーニングなどの分散ジョブでは、すべての Pod を同時に実行する必要があります。ギャングスケジューリングがない場合、一部の Pod が起動してリソースを確保したまま、残りの Pod を待機するため、どのジョブも進行しないクラスター全体のデッドロックが発生する可能性があります。ギャングスケジューリングは、All-or-Nothing の保証によってこの問題を解決します。つまり、必要なすべての Pod が一括でディスパッチされるか、まったくディスパッチされないかのいずれかになります。このトピックでは、Container Service for Kubernetes (ACK) でギャングスケジューリングを有効にして設定する方法について説明します。
仕組み
ACK は、PodGroup リソースを通じてギャングスケジューリングを実装します。分散ジョブの各 Pod は PodGroup に割り当てられ、最小 Pod 数 (min-available) を指定します。スケジューラは、最小要件を満たすまでグループ全体を Pending 状態に保持します。要件が満たされると、グループ内のすべての Pod が一括でディスパッチされます。
ACK は、PodGroup を定義するために 3 つの方法をサポートしています。
| 方法 | Pod のグループ化方法 | 最適なケース |
|---|---|---|
| ラベル | kube-scheduler が PodGroup を自動的に作成 | 単純なジョブ、個別の PodGroup オブジェクトは不要 |
| PodGroup CRD | タイムアウト制御が可能な明示的な PodGroup リソース | scheduleTimeoutSeconds |
| Koordinator アノテーション | アノテーションベースのグループ化 | Koordinator スケジューリングスタックを使用するクラスター |
3 つの方法すべてで、Pod とその PodGroup が同じ名前空間にある必要があります。PodGroup 内のすべての Pod は、同じ優先度を共有する必要があります。
前提条件
開始する前に、以下を確認してください。
Kubernetes 1.16 以降を実行している ACK マネージドプロ版クラスター。必要に応じてクラスターをアップグレードしてください。
詳細設定 (GangGroup およびマッチポリシー) の場合: Kubernetes 1.22 以降を実行し、kube-scheduler のバージョンが
1.xx.xx-aliyun-4.0より新しいクラスター。
エラスティックノードプールのリソース容量とノードラベルが Pod スケジューリングの要件を満たしていることを確認してください。そうでない場合、Pod がノードプールのノードにスケジュールされない可能性があります。
ギャングスケジューリングの有効化
方法 1: ラベル (推奨)
各 Pod に 2 つのラベルを追加します。kube-scheduler は、pod-group.scheduling.sigs.k8s.io/name の値にちなんで名付けられた PodGroup を自動的に作成します。
labels:
pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu # PodGroup 名 (有効な DNS サブドメインである必要があります)
pod-group.scheduling.sigs.k8s.io/min-available: "3" # ジョブの開始に必要な最小 Pod 数pod-group.scheduling.sigs.k8s.io/name の値は、有効な DNS サブドメイン名である必要があります。命名規則については、「オブジェクト名と ID」をご参照ください。
方法 2: PodGroup CRD
PodGroup リソースを明示的に作成し、各 Pod からラベルを使用して参照します。
ACK バージョン 1.31 以降、scheduling.x-k8s.io/v1alpha1 API バージョンのみがサポートされています。scheduling.sigs.k8s.io/v1alpha1 バージョンはサポートされなくなりました。
# PodGroup リソース
apiVersion: scheduling.x-k8s.io/v1alpha1
kind: PodGroup
metadata:
name: nginx
spec:
scheduleTimeoutSeconds: 10 # min-available が満たされない場合にグループを拒否するまでの待機秒数
minMember: 3 # ジョブの開始に必要な最小 Pod 数
---
# Pod ラベル — PodGroup の名前と名前空間に一致する必要があります
labels:
pod-group.scheduling.sigs.k8s.io/name: nginx方法 3: Koordinator アノテーション
各 Pod にアノテーションを追加します。この方法は、Koordinator API の total-number または mode パラメーターをサポートしていません。
annotations:
gang.scheduling.koordinator.sh/name: "gang-example"
gang.scheduling.koordinator.sh/min-available: "2"詳細設定
複数のギャングのグループ化 (GangGroup)
一部のジョブでは、パラメータサーバーとワーカーを持つ PyTorch ジョブのように、異なるロールで個別の min-available 要件を使用します。単一の PodGroup ではロールごとの最小要件を表現できず、別々の PodGroup ではロール間のスケジューリングを調整できません。
GangGroup は、複数の PodGroup をリンクすることでこの問題を解決します。ジョブは、グループ内のすべてのギャングが自身の min-available を満たしたときにのみ開始されます。各 Pod または PodGroup に次のラベルを追加します (Koordinator メソッドの場合はアノテーションキーを使用します):
| 方法 | リソース | キー |
|---|---|---|
| ラベル | Pod | pod-group.scheduling.sigs.k8s.io/groups |
| PodGroup CRD | PodGroup | pod-group.scheduling.sigs.k8s.io/groups |
| Koordinator アノテーション | Pod | gang.scheduling.koordinator.sh/groups |
値の例 (JSON 配列の <namespace>/<gang-name> エントリ):
pod-group.scheduling.sigs.k8s.io/groups: "[\"default/gang-example1\", \"default/gang-example2\"]"GangGroup には、Kubernetes 1.22 以降と、1.xx.xx-aliyun-4.0 より新しいバージョンの kube-scheduler が必要です。マッチポリシーの設定
デフォルトでは、PodGroup はリソースの事前割り当てを完了した Pod のみ (only-waiting) をカウントします。match-policy を使用して、他の状態の Pod を最小カウントに含めることができます。たとえば、以前のスケジューリングサイクルの一部の Pod がまだ実行中であり、最小カウントに含めるべき場合などです。
ラベルを各 Pod (ラベルメソッド) または PodGroup リソース (PodGroup CRD メソッド) に追加します。Koordinator アノテーションメソッドは once-satisfied のみをサポートします。
| マッチポリシー | min-available にカウントされる Pod | 使用するケース |
|---|---|---|
only-waiting | リソースの事前割り当てを完了した Pod | 最も厳格 — 実行中の Pod が新しいスケジューリングサイクルでカウントされるのを防ぎます。以前のサイクルからの持ち越しがないステートレスジョブに使用します。 |
waiting-and-running | Running 状態の Pod + 事前割り当てを完了した Pod | 以前のサイクルの一部の Pod がまだ実行中であり、最小カウントに含めるべき場合に使用します。過度に厳格なカウントによるアイドルリソースの確保のリスクを軽減します。 |
waiting-running-succeed | Succeeded 状態の Pod + Running + 事前割り当てを完了した Pod | 部分的な再起動を許容するジョブに使用します — すでに成功した Pod もカウントされます。すでに完了した Pod の再スケジューリングを回避します。 |
once-satisfied | リソースの事前割り当てを完了した Pod。PodGroup は一度満たされると無効になります | ワンショットジョブに使用します。ギャングがディスパッチされると、PodGroup は無効になります。 |
ラベルベースの例:
pod-group.scheduling.sigs.k8s.io/match-policy: "waiting-and-running"PodGroup CRD の例 (Pod ではなく PodGroup に追加):
pod-group.scheduling.sigs.k8s.io/match-policy: "waiting-and-running"マッチポリシーの設定には、Kubernetes 1.22 以降と、1.xx.xx-aliyun-4.0 より新しいバージョンの kube-scheduler が必要です。例: 分散 TensorFlow ジョブ
この例では、ギャングスケジューリングを使用して分散 TensorFlow ジョブを実行する場合と使用しない場合の違いを示します。クラスターには 4 つの GPU があります。ジョブは 1 つのパラメータサーバー (PS) Pod と 4 つのワーカー Pod を実行します。各ワーカーは 2 つの GPU を必要とし、最小 Pod 数は 5 です。
ステップ 1: Arena をインストールし、TensorFlow ジョブを実行できるようにクラスターを準備します。セットアップ手順については、「Arena のインストール」をご参照ください。
Arena は Kubeflow のサブプロジェクトであり、CLI または SDK を通じて、環境設定、データ準備、モデル開発、モデルトレーニング、予測など、機械学習ジョブのライフサイクルを管理します。
ステップ 2: 次のマニフェストを使用して TensorFlow ジョブを送信します。PS とワーカーの両方のテンプレートには、min-available: "5" を持つギャングスケジューリングラベルが含まれています。
apiVersion: "kubeflow.org/v1"
kind: "TFJob"
metadata:
name: "tf-smoke-gpu"
spec:
tfReplicaSpecs:
PS:
replicas: 1
template:
metadata:
creationTimestamp: null
labels:
pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu
pod-group.scheduling.sigs.k8s.io/min-available: "5"
spec:
containers:
- args:
- python
- tf_cnn_benchmarks.py
- --batch_size=32
- --model=resnet50
- --variable_update=parameter_server
- --flush_stdout=true
- --num_gpus=1
- --local_parameter_device=cpu
- --device=cpu
- --data_format=NHWC
image: registry.cn-hangzhou.aliyuncs.com/kubeflow-images-public/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3
name: tensorflow
ports:
- containerPort: 2222
name: tfjob-port
resources:
limits:
cpu: '1'
workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarks
restartPolicy: OnFailure
Worker:
replicas: 4
template:
metadata:
creationTimestamp: null
labels:
pod-group.scheduling.sigs.k8s.io/name: tf-smoke-gpu
pod-group.scheduling.sigs.k8s.io/min-available: "5"
spec:
containers:
- args:
- python
- tf_cnn_benchmarks.py
- --batch_size=32
- --model=resnet50
- --variable_update=parameter_server
- --flush_stdout=true
- --num_gpus=1
- --local_parameter_device=cpu
- --device=gpu
- --data_format=NHWC
image: registry.cn-hangzhou.aliyuncs.com/kubeflow-images-public/tf-benchmarks-gpu:v20171202-bdab599-dirty-284af3
name: tensorflow
ports:
- containerPort: 2222
name: tfjob-port
resources:
limits:
nvidia.com/gpu: 2
workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarks
restartPolicy: OnFailureギャングスケジューリングを有効にしない場合:
次のコマンドを実行して Pod のステータスを確認します。
kubectl get pods利用可能な GPU が 4 つしかないため、2 つのワーカー Pod が Running 状態になりすべての GPU を確保し、残りの 2 つのワーカーは Pending 状態のままになります。実行中のワーカーは他のワーカーを待機してブロックされます。
NAME READY STATUS RESTARTS AGE
tf-smoke-gpu-ps-0 1/1 Running 0 6m43s
tf-smoke-gpu-worker-0 1/1 Running 0 6m43s
tf-smoke-gpu-worker-1 1/1 Running 0 6m43s
tf-smoke-gpu-worker-2 0/1 Pending 0 6m43s
tf-smoke-gpu-worker-3 0/1 Pending 0 6m43s実行中のワーカーのログを確認します。
kubectl logs -f tf-smoke-gpu-worker-0ログは、ワーカーが Pending 状態の Pod を待機してストールしていることを示しています — GPU は確保されていますが、トレーニングは実行されません。
INFO|2020-05-19T07:02:18|/opt/launcher.py|27| 2020-05-19 07:02:18.199696: I tensorflow/core/distributed_runtime/master.cc:221] CreateSession still waiting for response from worker: /job:worker/replica:0/task:3
INFO|2020-05-19T07:02:28|/opt/launcher.py|27| 2020-05-19 07:02:28.199798: I tensorflow/core/distributed_runtime/master.cc:221] CreateSession still waiting for response from worker: /job:worker/replica:0/task:2ギャングスケジューリングを有効にした場合:
クラスターに min-available: 5 を満たすのに十分なリソースができるまで、5 つの Pod すべてが Pending 状態のままになります。
NAME READY STATUS RESTARTS AGE
tf-smoke-gpu-ps-0 0/1 Pending 0 43s
tf-smoke-gpu-worker-0 0/1 Pending 0 43s
tf-smoke-gpu-worker-1 0/1 Pending 0 43s
tf-smoke-gpu-worker-2 0/1 Pending 0 43s
tf-smoke-gpu-worker-3 0/1 Pending 0 43sクラスターに 4 つの GPU が追加されると、スケジューラは 5 つの Pod すべてを同時にディスパッチします。
kubectl get pods期待される出力:
NAME READY STATUS RESTARTS AGE
tf-smoke-gpu-ps-0 1/1 Running 0 3m16s
tf-smoke-gpu-worker-0 1/1 Running 0 3m16s
tf-smoke-gpu-worker-1 1/1 Running 0 3m16s
tf-smoke-gpu-worker-2 1/1 Running 0 3m16s
tf-smoke-gpu-worker-3 1/1 Running 0 3m16sワーカーのログを確認して、トレーニングが開始されたことを確認します。
kubectl logs -f tf-smoke-gpu-worker-0期待される出力:
INFO|2020-05-19T07:15:24|/opt/launcher.py|27| Running warm up
INFO|2020-05-19T07:21:04|/opt/launcher.py|27| Done warm up
INFO|2020-05-19T07:21:04|/opt/launcher.py|27| Step Img/sec loss
INFO|2020-05-19T07:21:05|/opt/launcher.py|27| 1 images/sec: 31.6 +/- 0.0 (jitter = 0.0) 8.318
INFO|2020-05-19T07:21:15|/opt/launcher.py|27| 10 images/sec: 31.1 +/- 0.4 (jitter = 0.7) 8.343
INFO|2020-05-19T07:21:25|/opt/launcher.py|27| 20 images/sec: 31.5 +/- 0.3 (jitter = 0.7) 8.142トラブルシューティング
エラー:「rejected by podgroup xxx」
クラスター内に複数の PodGroup が存在する場合、kube-scheduler のバックオフキューにより、あるスケジューリングサイクルでリソースの事前割り当てを完了した Pod が、後の PodGroup が処理される際に拒否されることがあります。
これは想定される動作です。この状況が 20 分以上続かない場合は、このエラーを無視できます。エラーが 20 分以上続く場合は、チケットを送信してください。
次のステップ
容量スケジューリングの操作 — エラスティッククォータグループを使用してクラスターのリソース使用率を向上させる