今回の記事はKubernetesとKueue、そしてKubeflowを連携したGPUの利用に関してです、 KueueやKubeflowは感嘆にまとめるのが難しかったため、前編、中編、そして今回の後編の3つ二分割しました。
Kueueの基礎については、前編に書いているつもりです。
KubernetesでGPUを使う方法とKueueのセットアップについては、中編でまとめています。
今回は、KueueとKubeflowの連携について書いています。
目的
後編ではKubeflowをKueueと組み合わせて動かすのを試してみます。 GPU利用可能なKubernetes + Kueue環境の上で、以下の3点を動かして確認します。
- Kubeflow Trainer v1(
PyTorchJob) - Kubeflow Trainer v2(
TrainJob) - KueueによるGPUジョブのAdmission / 実行制御
なお、この後はやたら長文とYAMLファイルのオンパレードになります。内容が内容なのでご了承ください。
目次
かなり長いので、今回は目次を付けました。
- 0. Kubeflow と本書の構成
- 1. 前提条件と使用する名前
- 2. 事前確認(Kueue / GPU)
- 3. Trainer v1 セットアップ
- 4. v1 動作検証(PyTorchJob)
- 5. Trainer v2 セットアップ
- 6. v2 動作検証(TrainJob)
- 7. v1 と v2 の見え方の違い
- 8. 片付け
- 9. Kubeflow Pipelines との連携
0. Kubeflow と本書の構成
KubeflowはKubernetes上で機械学習ワークロードを管理するプラットフォームです。Kubeflowは色々なプロジェクトが存在しています。今回はその中からKubeflow TrainerとKubeflow Pipelinesを使って最初に説明したジョブを実行します。
ちなみにKubeflowのプロジェクトは次のページにまとめられています。
まずは「学習ジョブの実行」に特化した Kubeflow Trainer を使ってみます。 Trainerにはv1とv2があります。v2の方が新しいバージョンです。ただ、新しいバージョンはKubeflow Trainerの初学者には分かりにくいため、まずはv1を試して、そのあとv2を試す 方針をとります。
| 観点 | v1(Training Operator) | v2(Trainer) |
|---|---|---|
| API | PyTorchJob など framework ごと |
TrainJob に統一 |
| 記述の重心 | ReplicaSpec / Pod テンプレート | Runtime + Job 定義 |
| 見え方 | Kubernetes 寄り | アプリケーション寄り |
| 構造の見えやすさ | 裏側が見えやすい | 書きやすい |
| 将来性 | legacy | 本命 |
1. 前提条件と使用する名前
前提
以下はすでに完了している前提です。
- Kubernetesクラスタが構築済みで利用可能
- NVIDIA Driver / Container Runtime / Device PluginのGPU利用準備が完了
runtimeClassName: nvidiaが利用可能- Kueueがインストール済み
- Queueが作成済み
本書で使う名前
中編で作成したQueue定義をそのまま今回も使います。 自分の環境で名前が異なる場合は、YAML内の該当箇所を読み替えてください。
| 項目 | 値 |
|---|---|
| Namespace | ml-jobs |
| LocalQueue | gpu-queue |
| ClusterQueue | gpu-clusterqueue |
| RuntimeClass | nvidia |
2. 事前確認(Kueue / GPU)
Kubeflow Jobを投入する前に、KueueのQueueとGPUのノード認識を確認します。
2-1. Queue の存在確認
kubectl get localqueues -A kubectl get clusterqueues kubectl get resourceflavors
次のように表示されれば想定通りです。
ml-jobsnamespaceにgpu-queueが表示されるgpu-clusterqueueが表示される- ResourceFlavor
gpuが表示される
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
gpu-queue が無い |
LocalQueue / ClusterQueue の作成が未完了。Queue 作成手順を先に実施する |
| ResourceFlavor が無い | Flavor が無いと Resource 割り当てが成立しない。Kueue の ResourceFlavor 定義を確認する。 |
2-2. ClusterQueue の quota 確認
Queueが存在していてもGPU quotaが無ければGPU JobはAdmissionされません。
kubectl describe clusterqueue gpu-clusterqueue
次のように表示されれば想定通りです。
Resource Groupsにcpu,memory,nvidia.com/gpuが含まれているConditionsにType: Active / Status: TrueとMessage: Can admit new workloadsが表示される
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
nvidia.com/gpu が無い |
GPU quota が未設定。ClusterQueue に ResourceGroup / quota を追加する |
Active=False |
設定ミスか依存リソース不足。kubectl describe clusterqueue の Conditions を確認する |
2-3. GPU のノード認識確認
Kueueにquotaがあっても、ノードがGPUをAdvertiseしていなければPodは起動できません。
# ノード名を確認 kubectl get nodes # GPU ノードの Capacity / Allocatable を確認 kubectl describe node <GPUノード名> | grep -A5 -E 'Capacity:|Allocatable:' kubectl describe node <GPUノード名> | grep nvidia.com/gpu
次のように表示されれば想定通りです。
CapacityまたはAllocatableにnvidia.com/gpu: 1(以上)が表示される
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
nvidia.com/gpu が表示されない |
NVIDIA Device Plugin が動いていない可能性がある。kubectl get pods -A | grep -i nvidia で確認する |
| Device Plugin は動いているが GPU が出ない | ホスト側で nvidia-smi が動くか確認し、Driver / Container Runtime / Plugin 登録状態を切り分ける |
3. Trainer v1 セットアップ
まずは旧来のTrainer v1を使って、ジョブを流す方法を試してみます。
3-1. インストール
kubectl apply --server-side -k \ "github.com/kubeflow/training-operator.git/manifests/overlays/standalone?ref=v1.8.1"
次のように表示されれば想定通りです。
- CRD, ServiceAccount, Deploymentなどに
serverside-appliedまたはcreatedが並ぶ。
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
| ネットワークエラー | GitHub への到達または名前解決の問題。時間をおいて再実行する |
| エラーなく完了したが Pod が起動しない | 次のステップで Pod 状態を確認する |
3-2. Controller の起動確認
applyが完了してもController Podが起動していなければ PyTorchJob は処理されません。
kubectl get pods -n kubeflow kubectl get crd | grep kubeflow.org
次のように表示されれば想定通りです。
training-operator-xxxxx 1/1 Runningpytorchjobs.kubeflow.orgなどのCRDが表示される
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
| Pod が存在しない | インストールに失敗している。前ステップの apply を再実行する |
CrashLoopBackOff |
kubectl logs -n kubeflow <pod名> でログを確認する |
4. v1 動作検証(PyTorchJob)
4-1. YAML の作成
Kueue経由でGPUを利用する最小構成の PyTorchJob を作成します。
cat <<'EOF' > 10-pytorchjob-v1.yaml
apiVersion: kubeflow.org/v1
kind: PyTorchJob
metadata:
name: pytorch-test
namespace: ml-jobs
labels:
kueue.x-k8s.io/queue-name: gpu-queue
spec:
pytorchReplicaSpecs:
Master:
replicas: 1
restartPolicy: Never
template:
spec:
runtimeClassName: nvidia
containers:
- name: pytorch
image: docker.io/kubeflowkatib/pytorch-mnist-gpu:latest
command:
- python
- mnist.py
- --epochs=1
resources:
limits:
nvidia.com/gpu: "1"
EOF
YAML を作成したら内容を確認:
cat 10-pytorchjob-v1.yaml
| 症状 | 対処 |
|---|---|
gpu-queue が自分の環境に存在しない |
Kueue 管理下に入らない。自分の LocalQueue 名に合わせて修正する |
runtimeClassName: nvidia が存在しない |
Pod 起動に失敗する。自分の RuntimeClass 名に合わせて修正する |
4-2. 投入
kubectl apply -f 10-pytorchjob-v1.yaml
次のように表示されれば想定通りです。
pytorchjob.kubeflow.org/pytorch-test created
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
| YAML 構文エラー | インデントやフィールド名のミス。YAMLファイルの内容を見直す。構文チェックを実施する |
| Webhook / Admission エラー | Controller がまだ起動途中の可能性がある。もう数十秒待って再実行する |
4-3. 実行状態の確認
kubectl get pytorchjobs -n ml-jobs kubectl get workloads -n ml-jobs kubectl get pods -n ml-jobs
Podが起動したらログを確認します。
kubectl logs -n ml-jobs pytorch-test-master-0
次のように表示されれば想定通りです。
PyTorchJobのSTATUS →Running→Succeeded- 対応する
Workloadが作られADMITTED=True,FINISHED=True pytorch-test-master-0PodがCompleted- ログに学習の進捗(
Train Epoch,loss=...など)が出る
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
Workload が Admitted にならない |
GPU quota が空いていないか Queue 設定に問題がある。kubectl describe workload -n ml-jobs を確認する |
Pod が Pending のまま |
GPU スケジューリングや RuntimeClass の問題。kubectl describe pod -n ml-jobs <pod名> を確認する |
| Pod が起動したがすぐ失敗する | イメージ内のコマンドや GPU 利用設定の問題。kubectl logs でコンテナログを確認する |
5. Trainer v2 セットアップ
次に新しいTrainer v2を使って、ジョブを流してみます。
5-1. インストール
v2はmanagerとruntimesの2つによって構成されます。manager の Pod が Running になってから runtimes を apply してください。 起動前にruntimesをapplyするとWebhookエラーになります。
export VERSION=v2.1.0
# Step 1: manager を導入
kubectl apply --server-side -k \
"https://github.com/kubeflow/trainer.git/manifests/overlays/manager?ref=${VERSION}"
# Step 2: Manager Pod が Running になるまで待つ
kubectl get pods -n kubeflow-system
# Step 3: runtimes を導入
kubectl apply --server-side -k \
"https://github.com/kubeflow/trainer.git/manifests/overlays/runtimes?ref=${VERSION}"
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
connection refused のような Webhook エラー |
manager Pod がまだ起動前に runtimes を apply した。Pod を確認してから runtimes apply を再実行する |
| GitHub 到達エラー | ネットワークやプロキシ設定を確認する |
| Pod が存在しない | apply に失敗している。インストール手順を再実行する |
CrashLoopBackOff |
kubectl logs -n kubeflow-system <pod名> でログを確認する |
| Running だが直後に Webhook エラー | 起動直後で Webhook server が安定していない。数十秒待って再実行する |
5-2. Runtime の登録確認
TrainJob が参照するRuntimeが存在していなければJobが意図した動作になりません。
kubectl get crd | grep trainer.kubeflow.org kubectl get clustertrainingruntimes
次のように表示されれば想定通りです。
clustertrainingruntimes.trainer.kubeflow.orgtrainjobs.trainer.kubeflow.orgtorch-distributed(ClusterTrainingRuntimeとして表示)
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
| CRD はあるが Runtime が無い | runtimes の apply が未実行か失敗している。インストール手順を見直す |
torch-distributed が無い |
YAML の runtimeRef.name を kubectl get clustertrainingruntimes の出力に合わせて修正する |
6. v2 動作検証(TrainJob)
6-1. YAML の作成
v2.1.0のschemaに合わせた TrainJob です。スキーマの落とし穴が多いため、以下のYAMLをそのまま使うことを推奨します。
cat <<'EOF' > 20-trainjob-v2.yaml
apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainJob
metadata:
name: trainjob-test
namespace: ml-jobs
labels:
kueue.x-k8s.io/queue-name: gpu-queue
spec:
runtimeRef:
name: torch-distributed
trainer:
image: docker.io/kubeflowkatib/pytorch-mnist-gpu:latest
command:
- python
- mnist.py
- --epochs=1
numNodes: 1 # 文字列 "1" ではなく整数で書く
numProcPerNode: 1 # 文字列 "1" ではなく整数で書く
resourcesPerNode:
requests:
cpu: "4"
memory: "8Gi"
nvidia.com/gpu: "1"
limits:
cpu: "4"
memory: "8Gi"
nvidia.com/gpu: "1"
EOF
よくある YAML のミス:
| ミス | 症状 | 正しい書き方 |
|---|---|---|
spec.model を使っている |
v2.1.0 では不一致になる | 上記 YAML をそのまま使う |
resourcesPerNode.cpu と直書き |
フィールド不一致 | resourcesPerNode.requests.cpu の階層で書く |
numProcPerNode: "1"(文字列) |
Webhook に弾かれる | numProcPerNode: 1(整数) |
6-2. 投入と初期状態の確認
kubectl apply -f 20-trainjob-v2.yaml kubectl get trainjobs -n ml-jobs
次のように表示されれば想定通りです。
trainjob.trainer.kubeflow.org/trainjob-test created
作成直後はSTATEが空または一時的に Suspended に見える場合があります。これは正常です。
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
strict decoding error |
フィールド名が schema と一致していない。上記 YAML を使う |
Webhook で denied the request |
numProcPerNode の型、resourcesPerNode の階層を見直す |
6-3. 実行状態の確認
kubectl get workloads -n ml-jobs kubectl get trainjobs -n ml-jobs kubectl get pods -n ml-jobs
Pod名が分かったらログを確認します。
kubectl logs -n ml-jobs <pod名>
次のように表示されれば想定通りです。
- Workloadが作られ
ADMITTED=True - Podが
Running - ログに
Train Epoch: 1 ... loss=...が出る
補足:STATE が Suspended に見えるとき
kubectl get trainjobs の表示だけでは誤解しやすいことがあります。次のコマンドで詳細を確認してください。
kubectl describe trainjob -n ml-jobs trainjob-test
以下であれば正常に動作しています。
Spec.Suspend: falseType: Suspended / Status: FalseMessage: TrainJob is resumed
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
Workload が Admitted にならない |
GPU quota / Queue 設定を確認する。kubectl describe workload -n ml-jobs を見る |
| Admitted だが Pod が起動しない | Pod 側のスケジューリング問題。kubectl describe pod -n ml-jobs <pod名> を確認する |
| Pod は起動したがログが進まない | イメージ、コマンド、データセット取得、GPU 初期化のいずれかの問題。kubectl logs と kubectl describe pod を確認する |
6-4. 完了確認
kubectl get pods -n ml-jobs -w # -w で完了まで監視
完了後:
kubectl get trainjobs -n ml-jobs kubectl get workloads -n ml-jobs
次のように表示されれば想定通りです。
- Pod:
Completed - TrainJob:
STATE Complete - Workload:
ADMITTED=Trueのまま完了
次の場合は対処の項に従って、問題を解決してください。
| 症状 | 対処 |
|---|---|
Pod が Error で終了 |
kubectl logs -n ml-jobs <pod名> でコンテナログを確認する |
| Pod は終わったのに TrainJob が更新されない | Controller の status 反映遅延の可能性がある。kubectl describe trainjob -n ml-jobs trainjob-test を確認する |
7. v1 と v2 の見え方の違い
| 観点 | v1(PyTorchJob) | v2(TrainJob) |
|---|---|---|
| Job の書き方 | ReplicaSpec + Pod テンプレートをそのまま書く | Runtime を参照し、trainer ブロックだけ書く |
| Pod 構成の見え方 | Master Replica が直接見える |
JobSet が中間に入るため間接的 |
| Kueue との接点 | label をつけるだけ。構造は同じ | label をつけるだけ。構造は同じ |
| 向いている場面 | Kubernetes リソースの構造理解 | 実運用・今後の標準として学ぶ |
v1とv2のYAMLを書いたので、比較してみましょう。vimdiffという便利なコマンドがあるので、それを使って比較してみます。 両者は同じイメージを使ってアプリケーションを実行しています。細かい部分までみる必要は無いのですが、v1はkubeflow.org/v1のPyTorchJobのAPIを使っているのに対して、v2はtrainer.kubeflow.org/v1alpha1のTrainJobを使っているのがわかります。 スペックの書き方も少々異なるようです。
(画像差し込み)
現時点のバージョンではレガシーのv1、新しい設計のv2のどちらでも、Kueueはサポートしています。
8. 片付け
検証後にJobを削除して、再実験しやすい状態に戻します。
kubectl delete -f 10-pytorchjob-v1.yaml kubectl delete -f 20-trainjob-v2.yaml
削除後にJob / Pod / Workloadが消えたことを確認します。
kubectl get pytorchjobs,trainjobs,pods,workloads -n ml-jobs
9. Kubeflow Pipelinesとの連携
9-0. 事前準備
次に、Kubeflow Pipelinesを使ったジョブの実行についても試してみます。
9-0-1. ストレージの準備
これを試す場合、事前にストレージが必要です。 シングルノードではlocal-path-storageが利用可能です。デプロイ後、標準のストレージと設定してください。
マルチノードの場合は、マルチノードでも動作する適切なストレージを展開してください。
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.26/deploy/local-path-storage.yaml
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
9-0-2. 一般ユーザーでの利用
この後の流れでPythonスクリプトでJobを流しますが、root権限のユーザーではこのコードは動作しません。 しかしこれはKubefrow Pipelineの制限では無く、あくまでPythonの制限です。
実行するとKubefrowのジョブにはグリーンマークがつくのですが、そのログをみると「rootユーザーで実行したので阻止した」といわれてジョブが正常にどうさしません。
任意の一般ユーザーでシステムにログインして、Python仮想環境を作成した後に仮想環境内でpip install kfpを実行して実行環境を準備してください。
また、kubeconfigをそのユーザーの.kubeディレクトリーに格納してください。
Pythonのkfpモジュールのついての詳細は、以下をご覧ください。このモジュール名はKubeFlow Pipelinesと頭文字をとってkfpとなっています。
次はシステムにインストール済みのPython 3.12仮想環境を作って、kfpモジュールをインストールする例です。
python3.12 -m venv .venv source ./.venv/bin/activate pip install kfp
KUBECONFIGについてはkubeadmを使って構築した場合は、次のように実行すると良いでしょう。
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
9-1. Pipelines を組み合わせる意味
Trainer + KueueだけでもGPU学習ジョブは実行できます。Pipelineを加えることで「学習ジョブを部品として、前処理・学習・評価などの一連のワークフローにつなぐ」ことが可能になります。
| コンポーネント | 役割 |
|---|---|
| Kueue | GPU などの限られたリソースを考慮して賢くジョブの実行順を制御する |
| Trainer | 学習ジョブを表現して実行する |
| Pipeline | 前処理、学習、評価などをひとつのワークフローとしてつなぐ |
学習→再現可能なワークフローの例:
データ前処理 → TrainJob 投入 → 学習完了 → 評価 → 基準を満たした場合のみ次へ
Trainerだけの場合、この流れは手動の kubectl apply やシェルスクリプトで繋ぐ必要があります。Pipelineを使うと、これを1つの実行単位として定義・再実行できます。
9-2. Kubeflow Pipelines のインストール
export PIPELINE_VERSION=2.15.0
kubectl apply -k \
"github.com/kubeflow/pipelines/manifests/kustomize/cluster-scoped-resources?ref=${PIPELINE_VERSION}"
kubectl wait --for condition=established --timeout=60s crd/applications.app.k8s.io
kubectl apply -k \
"github.com/kubeflow/pipelines/manifests/kustomize/env/dev?ref=${PIPELINE_VERSION}"
確認:
kubectl get pods -A | grep -E 'pipeline|argo|ml-pipeline'
デプロイ後、次のようなステータスになります。
そのうちproxy-agentはCrashLoopBackOffの状態が続きますが、これは後述にあるように、名前解決ができないためです。どうやらKubeflow Pipelinesの現在のバージョンのマニフェストがGCP上の仮想マシンで動かす前提のコードになっており、それ以外の環境(オンプレK8sなど)ではproxy-agentが上手く動かないためです。
今回の検証ではこれが動かなくても支障は無いため、そのまま無視して構いません。気になるようであれば手順に従ってPodを削除してください。
# kubectl get pods -n kubeflow
NAME READY STATUS RESTARTS AGE
cache-deployer-deployment-56d88b59cc-pjxlf 1/1 Running 0 12m
cache-server-6d8bfc7488-vb99v 1/1 Running 0 12m
controller-manager-6685d7dcc7-g2glt 1/1 Running 0 12m
metadata-envoy-deployment-7c499db4bf-f42h7 1/1 Running 0 12m
metadata-grpc-deployment-74b7fd6cb6-hbpxf 1/1 Running 7 (6m12s ago) 12m
metadata-writer-6f8b89765-2mdzp 1/1 Running 3 (3m40s ago) 12m
ml-pipeline-6c4b9d6f9d-f2jdz 1/1 Running 4 (5m13s ago) 12m
ml-pipeline-persistenceagent-75dd4b54cf-dwkl7 1/1 Running 2 (6m43s ago) 12m
ml-pipeline-scheduledworkflow-67f4f96c8d-n49jr 1/1 Running 4 (6m6s ago) 12m
ml-pipeline-ui-69c7f869dd-ttczc 1/1 Running 0 12m
ml-pipeline-viewer-crd-7d8d667b5c-m9s6r 1/1 Running 0 12m
ml-pipeline-visualizationserver-76cc49c944-8rqs7 1/1 Running 0 12m
mysql-66f7cd88c6-cpjsv 1/1 Running 0 12m
proxy-agent-5f586ff77-9d82w 0/1 CrashLoopBackOff 6 (3m25s ago) 12m
seaweedfs-6c5cff9744-2tbzk 1/1 Running 0 12m
training-operator-84469657ff-x25gn 1/1 Running 0 28h
workflow-controller-c5cd4b8d9-mlvwj 1/1 Running 0 12m
# kubectl logs proxy-agent-5f586ff77-9d82w -n kubeflow
+++ dirname /opt/proxy/attempt-register-vm-on-proxy.sh
++ cd /opt/proxy
++ pwd
+ DIR=/opt/proxy
++ kubectl get configmap inverse-proxy-config -o json
++ jq -r '.data.Hostname // empty'
+ HOSTNAME=
+ [[ -n '' ]]
+ [[ ! -z '' ]]
++ curl http://metadata.google.internal/computeMetadata/v1/instance/zone -H 'Metadata-Flavor: Google'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0curl: (6) Could not resolve host: metadata.google.internal
+ INSTANCE_ZONE=/
そのまま動かしても良いみたいですが、なんか気持ち悪ければ消します。
kubectl delete deployment proxy-agent -n kubeflow
その他のPodが落ち続ける場合は、公式ドキュメントの platform-agnostic 構成を検討してください。
9-3. Argo Workflowsパッチと権限の付与
Argo Workflows のセキュリティパッチ (PEP 668対応)
パイプラインの初期化コンテナがrootで動こうとすると弾かれます。これに対応するため、次のように一般ユーザー(ID: 1000)で動かすためのパッチを適用します。
kubectl patch configmap workflow-controller-configmap -n kubeflow \
--patch '{"data":{"executor":"securityContext:\n runAsUser: 1000\n runAsNonRoot: true\n"}}'
kubectl rollout restart deployment workflow-controller -n kubeflow
パイプライン実行役へ管理者権限を付与
パイプライン(pipeline-runner)が他の名前空間(ml-jobs)にジョブを作るためには権限の付与が必要です。権限の付与は次のように実行できます。
kubectl create clusterrolebinding pipeline-runner-admin-binding \ --clusterrole=cluster-admin \ --serviceaccount=kubeflow:pipeline-runner
9-4. Pipeline から PyTorchJob を投入する最小サンプル
このコードはPythonのkfpモジュールを使って、kubeflow Pipelineを流して最終的にPyTorchJobをクラスターで実行します。実行するジョブはGPU版のpytorch-mnistです。これまでと同じものを実行します。
注意: これは構造を掴むための最小例です。実運用では ServiceAccount、RBAC、イメージ、Artifact の扱いを別途整理してください。
注意: パイプライン内でダウンロードする kubectl のバージョンは、クラスターのバージョンと一致させる必要があります。今回の環境はv1.35.3を想定しているため、v1.35.3を設定しています。現在のクラスターに合うバージョンを指定しないと互換性エラーが発生する可能性があるため、今回の場合はv1.35.3または stable.txt から取得する方式を採用してください。
注意: 初稿ではport-forwardを使っていたのですが、かえって説明が難しくなりすぎたのでNodePortを使う方法に書き換えました。こっちの方がすっきりします。テスト環境ではこれで十分ですが、本番環境ではIngressやLoadBalancer等を採用してください。
次の方法で、ml-pipeline-uiとml-pipelineをNodePortサービスで公開します。ml-pipeline-uiがKubernetes pipeline UIへのアクセスを、ml-pipeline はパイプラインを流すAPIエンドポイントを公開するものです。
//ml-pipeline-uiとml-pipelineをサービスで公開
kubectl patch svc ml-pipeline-ui -n kubeflow -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "nodePort": 30080}]}}'
kubectl patch svc ml-pipeline -n kubeflow -p '{"spec": {"type": "NodePort", "ports": [{"port": 8888, "nodePort": 30088}]}}'
//確認
kubectl get svc -n kubeflow | grep -E 'ml-pipeline-ui|ml-pipeline '
ml-pipeline NodePort 10.110.131.20 <none> 8888:30088/TCP,8887:30937/TCP 4d1h
ml-pipeline-ui NodePort 10.109.207.146 <none> 80:30080/TCP 4d1h
ml-pipeline-uiのNodePortにアクセスすると、次のようなKubeflow Pipelineのインターフェースにアクセスできます。

準備が完了したら次のファイルを作ってpython main.pyのように実行してください。
from kfp import dsl from kfp import client # ← これも必要です # 1. コンポーネント(部品)の定義 @dsl.component(base_image="python:3.12-slim") def submit_pytorchjob(): import subprocess import os import urllib.request # kubectlの準備 kubectl_url = "https://dl.k8s.io/release/v1.35.3/bin/linux/amd64/kubectl" kubectl_path = "/tmp/kubectl" urllib.request.urlretrieve(kubectl_url, kubectl_path) os.chmod(kubectl_path, 0o755) # PyTorchJobのYAML yaml_content = """ apiVersion: "kubeflow.org/v1" kind: "PyTorchJob" metadata: name: "pipeline-pytorchjob" namespace: "ml-jobs" spec: pytorchReplicaSpecs: Master: replicas: 1 template: spec: containers: - name: pytorch image: docker.io/kubeflowkatib/pytorch-mnist-gpu:latest """ # 実行 subprocess.run([kubectl_path, "apply", "-f", "-"], input=yaml_content, text=True, check=True) # 2. パイプライン(流れ)の定義 @dsl.pipeline(name="submit-pytorchjob-pipeline") def my_pipeline(): submit_pytorchjob() # 3. 実行(ここで client を使います) if __name__ == "__main__": SERVER_IP = "192.168.x.x" # あなたの環境のサーバーIPに書き換えてください ENDPOINT = f"http://{SERVER_IP}:30088" kfp_client = client.Client(host=ENDPOINT) run = kfp_client.create_run_from_pipeline_func( my_pipeline, arguments={} ) print(f"Run submitted! ID: {run.run_id}")
実行すると次のように表示されます。URLがちょっと変なのですが、Run detailsのほうのURLのNodePortを30088から30080に変えてアクセスすると次のような感じで発行したJobの状況を確認できます。ログなども表示されるので分かりやすいです。

Kubeflow Pipelineを使ったとしても、Kueue との接点は基本的には変わりません。
変わるのは「誰が PyTorchJob を作るか」だけです。
見かけ上はPythonのスクリプトを実行するだけでKubernetes上のKubefrow Pipelineがジョブを実行してくれるようになリます。
9-5. よくある落とし穴
| 症状 | 原因 |
|---|---|
| Pipeline は通るが Job が作られない | Pipeline 実行用 ServiceAccount に PyTorchJob 作成権限がない |
| Job は作られるが Workload が pending のまま | Queue quota / GPU quota / node 側 GPU 状態を確認する |
Podのkubectl から API サーバーに到達できない |
クラスタ内の DNS / ネットワーク設定を確認する |
| Cannot find context... | システム起動直後の DB 書き込み遅延。数分待つか再起動で解決 |
| CreateContainerConfigError | Argo の executor が root 権限で動こうとしている。上記パッチを当てる |
| sh: python3: command not found | ベースイメージに Python がない。python:3.x-slim を使用する |
| Forbidden: ... cannot get pytorchjobs | pipeline-runner の権限不足。ClusterRoleBinding を作成する |
確認コマンド:
kubectl get pytorchjobs -n ml-jobs kubectl get workloads -n ml-jobs kubectl get pods -n ml-jobs kubectl describe workload -n ml-jobs
動作確認が済んだら、次を実行してJobを片づけてください。
kubectl delete pytorchjobs -n ml-jobs --all