GKE の TPU の概要
Google Kubernetes Engine(GKE)をご利用のお客様は、TPU v4 スライスと v5e スライスを含む Kubernetes ノードプールを作成できるようになりました。TPU の詳細については、システム アーキテクチャをご覧ください。
GKE を使用する場合は、まず GKE クラスタを作成する必要があります。 次に、クラスタにノードプールを追加します。GKE ノードプールは、同じ属性を共有する VM のコレクションです。TPU ワークロードの場合、ノードプールは TPU VM で構成されます。
ノードプールの種類
GKE は、次の 2 種類の TPU ノードプールをサポートしています。
マルチホスト TPU スライス ノードプール
マルチホスト TPU スライスのノードプールは、相互接続された 2 つ以上の TPU VM を含むノードプールです。各 VM には TPU デバイスが接続されています。マルチホスト スライスの TPU は、高速相互接続(ICI)を介して接続されます。マルチホスト スライスのノードプールを作成した後に、ノードを追加することはできません。たとえば、v4-32
ノードプールを作成してから、後でこのノードプールに Kubernetes ノード(TPU VM)を追加することはできません。GKE クラスタに TPU スライスを追加するには、新しいノードプールを作成する必要があります。
マルチホスト TPU スライスのノードプール内のホストは、単一のアトミック単位として扱われます。GKE がスライスに 1 つのノードをデプロイできない場合、スライスにノードはデプロイされません。
マルチホスト TPU スライス内のノードを修復する必要がある場合、GKE はスライス内のすべての TPU VM をシャットダウンし、ワークロード内のすべての Kubernetes Pod を強制的に排除します。スライスのすべての TPU VM が稼働したら、新しいスライスの TPU VM で Kubernetes Pod をスケジュールできます。
次の図は、v5litepod-16(v5e)マルチホスト TPU スライスの例を示しています。このスライスには 4 つの TPU VM があります。各 TPU VM には 4 つの TPU v5e チップがあり、高速相互接続(ICI)で接続されています。各 TPU v5e チップには 1 つの TensorCore があります。
次の図は、1 つの TPU v5litepod-16
(v5e)スライス(トポロジ: 4x4)と 1 つの TPU v5litepod-8
(v5e)スライス(トポロジ: 2x4)を含む GKE クラスタを示しています。
マルチホスト TPU スライスでワークロードを実行する例については、TPU でワークロードを実行するをご覧ください。
単一ホスト TPU スライス ノードプール
単一ホストスライスのノードプールは、1 つ以上の独立した TPU VM を含むノードプールです。各 VM には TPU デバイスが接続されています。単一ホストスライスのノードプール内の VM はデータセンター ネットワーク(DCN)を介して通信できますが、VM に接続された TPU は相互接続されません。
次の図は、7 つの v4-8
マシンを含む単一ホストの TPU スライスの例を示しています。
単一ホストの TPU スライスでワークロードを実行する例については、TPU でワークロードを実行するをご覧ください。
GKE ノードプールの TPU マシンタイプ
ノードプールを作成する前に、ワークロードに必要な TPU バージョンと TPU スライスのサイズを選択する必要があります。TPU v4 は GKE Standard バージョン 1.26.1-gke.1500
以降で、v5e は GKE Standard バージョン 1.27.2-gke.2100
以降で、v5p は GKE Standard バージョン 1.28.3-gke.1024000
以降でサポートされています。
TPU v4、v5e、v5p は、GKE Autopilot バージョン 1.29.2-gke.1521000
以降でサポートされています。
TPU バージョン別のハードウェア仕様については、システム アーキテクチャをご覧ください。TPU ノードプールを作成するときは、モデルのサイズと必要なメモリ量に基づいて TPU スライスのサイズ(TPU トポロジ)を選択します。ノードプールの作成時に指定するマシンタイプは、スライスのバージョンとサイズによって異なります。
v5e
トレーニングと推論のユースケースでサポートされている TPU v5e マシンタイプとトポロジは次のとおりです。
マシンタイプ | トポロジ | TPU チップの数 | VM 数 | おすすめの使用例 |
---|---|---|---|---|
ct5lp-hightpu-1t |
1×1 | 1 | 1 | トレーニング、単一ホストの推論 |
ct5lp-hightpu-4t |
2x2 | 4 | 1 | トレーニング、単一ホストの推論 |
ct5lp-hightpu-8t |
2x4 | 8 | 1 | トレーニング、単一ホストの推論 |
ct5lp-hightpu-4t |
2x4 | 8 | 2 | トレーニング、マルチホストの推論 |
ct5lp-hightpu-4t |
4x4 | 16 | 4 | 大規模なトレーニング、マルチホスト推論 |
ct5lp-hightpu-4t |
4x8 | 32 | 8 | 大規模なトレーニング、マルチホスト推論 |
ct5lp-hightpu-4t |
8x8 | 64 | 16 | 大規模なトレーニング、マルチホスト推論 |
ct5lp-hightpu-4t |
8x16 | 128 | 32 | 大規模なトレーニング、マルチホスト推論 |
ct5lp-hightpu-4t |
16x16 | 256 | 64 | 大規模なトレーニング、マルチホスト推論 |
Cloud TPU v5e は、トレーニングと推論を組み合わせたプロダクトです。 トレーニング ジョブはスループットと可用性に対して最適化されていますが、推論ジョブはレイテンシに対して最適化されています。詳細については、v5e トレーニング アクセラレータのタイプと v5e 推論アクセラレータのタイプをご覧ください。
TPU v5e マシンは、us-west4-a
、us-east5-b
、us-east1-c
で利用できます。
GKE Standard クラスタは、コントロール プレーン バージョン 1.27.2-gke.2100 以降を実行する必要があります。GKE Autopilot は、コントロール プレーン バージョン 1.29.2-gke.1521000 以降を実行する必要があります。v5e の詳細については、Cloud TPU v5e トレーニングをご覧ください。
マシンタイプの比較
マシンタイプ | ct5lp-hightpu-1t | ct5lp-hightpu-4t | ct5lp-hightpu-8t |
---|---|---|---|
v5e チップの数 | 1 | 4 | 8 |
vCPU 数 | 24 | 112 | 224 |
RAM(GB) | 48 | 192 | 384 |
NUMA ノードの数 | 1 | 1 | 2 |
プリエンプションの可能性 | 高 | 中 | 低 |
チップ数の多い VM 用にスペースを確保するため、GKE スケジューラは、チップ数が少ない VM をプリエンプトして再スケジュールすることがあります。このため、8 チップの VM により 1 チップの VM と 4 チップの VM がプリエンプトされる可能性が高くなります。
v4 と v5p
TPU v4 と v5p のマシンタイプは次のとおりです。
マシンタイプ | vCPU 数 | メモリ(GB) | NUMA ノードの数 |
---|---|---|---|
ct4p-hightpu-4t |
240 | 407 | 2 |
ct5p-hightpu-4t |
208 | 448 | 2 |
TPU v4 スライスを作成する場合は、1 つのホストと 4 つのチップを含む ct4p-hightpu-4t
マシンタイプを使用します。詳細については、v4 トポロジと TPU システム アーキテクチャをご覧ください。TPU v4 スライス マシンタイプは us-central2-b
で使用できます。GKE Standard クラスタは、コントロール プレーン バージョン 1.26.1-gke.1500
以降を実行する必要があります。GKE Autopilot クラスタは、コントロール プレーン バージョン 1.29.2-gke.1521000
以降を実行する必要があります。
TPU v5p スライスを作成する場合は、1 つのホストと 4 つのチップを含む ct5p-hightpu-4t
マシンタイプを使用します。TPU v5p スライス マシンタイプは、us-west4-a
と us-east5-a
で利用できます。GKE Standard クラスタは、コントロール プレーン バージョン 1.28.3-gke.1024000
以降を実行する必要があります。GKE Autopilot は 1.29.2-gke.1521000
以降を実行する必要があります。v5p の詳細については、v5p トレーニングの概要をご覧ください。
既知の問題と制限事項
- Kubernetes Pod の最大数: 1 つの TPU VM で実行できる Kubernetes Pod は最大 256 個です。
- 特殊な予約のみ: GKE で TPU を使用する場合、
gcloud container node-pools create
コマンドの--reservation-affinity
フラグでサポートされている値はSPECIFIC
のみです。 - プリエンプティブル TPU の Spot VM バリアントのみをサポート: Spot VM はプリエンプティブル VM に似ており、同じ可用性制限が適用されますが、最大時間 24 時間の制限はありません。
- 費用割り当てサポートなし: GKE の費用割り当てと使用状況測定には、TPU の使用量と費用に関するデータは含まれません。
- オートスケーラーが容量を計算する: クラスタ オートスケーラーは、TPU VM を含む新しいノードが利用可能になる前に、新しいノードの容量を正しく計算しない場合があります。これにより、クラスタ オートスケーラーが追加のスケールアップを実行して、結果として必要以上にノードを作成する場合があります。クラスタ オートスケーラーは、通常のスケールダウン オペレーションの後、不要なノードをスケールダウンします。
- オートスケーラーがスケールアップをキャンセルする: クラスタ オートスケーラーは、10 時間以上待機状態になっている TPU ノードプールのスケールアップをキャンセルしますクラスタ オートスケーラーは、このようなスケールアップ オペレーションを後で再試行します。この動作により、予約を使用しないお客様の TPU の入手可能性が低下する可能性があります。
- taint でスケールダウンが妨げられる: TPU taint の容認機能がある TPU 以外のワークロードは、TPU ノードプールのドレイン中に再作成されると、ノードプールのスケールダウンを妨げることがあります。
十分な TPU と GKE の割り当てを確保する
リソースが作成されるリージョンで、特定の GKE 関連の割り当てを増やす必要がある場合があります。
次の割り当てには、増やす必要性が高いデフォルト値があります。
- Persistent Disk SSD(GB)割り当て: 各 Kubernetes ノードのブートディスクにはデフォルトで 100 GB 必要です。そのため、この割り当ては、少なくとも、(作成する予定の GKE ノードの最大数)x 100GB に設定する必要があります。
- 使用中の IP アドレスの割り当て: 各 Kubernetes ノードは 1 つの IP アドレスを消費します。そのため、この割り当ては、少なくとも、作成する予定の GKE ノードの最大数と同じ値に設定する必要があります。
割り当ての増加をリクエストするには、割り当ての増加をリクエストするをご覧ください。 TPU 割り当てのタイプの詳細については、TPU 割り当てをご覧ください。
割り当て増加のリクエストが承認されるまで数日かかることがあります。割り当て増加リクエストが数日以内に承認されない場合は、Google アカウント チームにお問い合わせください。
TPU 予約を移行する
GKE の TPU で既存の TPU 予約を使用する予定がない場合は、このセクションをスキップして、Google Kubernetes Engine クラスタを作成するに進みます。
GKE で予約済み TPU を使用するには、まず TPU の予約を新しい Compute Engine ベースの予約システムに移行する必要があります。
この移行について知っておくべき重要な点は次のとおりです。
- 新しい Compute Engine ベースの予約システムに移行された TPU 容量は、Cloud TPU Queued Resource API では使用できません。 TPU キュー内のリソースを予約で使用する場合は、TPU の予約の一部を新しい Compute Engine ベースの予約システムに移行する必要があります。
- 新しい Compute Engine ベースの予約システムに移行されている場合、TPU でアクティブに実行できるワークロードはありません。
- 移行を実行する時間を選択し、Google Cloud アカウント チームと協力して移行をスケジュールします。移行の時間枠は、営業時間中(月曜日~金曜日の太平洋時間午前 9 時~午後 5 時)にする必要があります。
Google Kubernetes Engine クラスタを作成する
Google Kubernetes Engine のドキュメントのクラスタを作成するをご覧ください。
TPU ノードプールを作成する
Google Kubernetes Engine のドキュメントのノードプールを作成するをご覧ください。
特権モードを使用せずに実行する
コンテナの権限スコープを縮小する場合は、TPU 権限モードをご覧ください。
TPU ノードプールでワークロードを実行する
Google Kubernetes Engine のドキュメントの TPU で GKE ワークロードを実行するをご覧ください。
ノードセレクタ
Kubernetes が TPU VM を含むノードでワークロードをスケジュールするには、Google Kubernetes Engine マニフェストのワークロードごとに 2 つのセレクタを指定する必要があります。
cloud.google.com/gke-accelerator-type
をtpu-v5-lite-podslice
、tpu-v5p-slice
またはtpu-v4-podslice
に設定します。cloud.google.com/gke-tpu-topology
をノードの TPU トポロジに設定します。
[トレーニング ワークロード] セクションと [推論ワークロード] セクションには、これらのノードセレクタの使用方法を示すマニフェストの例が含まれています。
ワークロードのスケジューリングに関する考慮事項
TPU には、Kubernetes で特別なワークロードのスケジューリングと管理が必要になる独自の特性があります。詳細については、GKE ドキュメントのワークロードのスケジューリングに関する考慮事項をご覧ください。
ノードの修復
マルチホスト TPU スライス ノードプール内のノードが異常な場合、GKE はノードプール全体を再作成します。詳細については、GKE ドキュメントのノードの自動修復をご覧ください。
マルチスライス - 単一スライス以外
小さなスライスをマルチスライスに集約して、より大きなトレーニング ワークロードを処理できます。詳細については、Cloud TPU マルチスライスをご覧ください。
トレーニング ワークロードのチュートリアル
このチュートリアルでは、マルチホスト TPU スライス(たとえば 4 つの v5e マシン)でワークロードをトレーニングする方法について説明します。次のモデルが対象です。
- Hugging Face FLAX Models: Pokémon 上で Diffusion をトレーニングする
- PyTorch/XLA: WikiText 上の GPT2
チュートリアルのリソースをダウンロードする
次のコマンドを使用して、各事前トレーニング済みモデルのチュートリアル Python スクリプトと YAML 仕様をダウンロードします。
git clone https://github.com/GoogleCloudPlatform/ai-on-gke.git
クラスタの作成とクラスタへの接続
リージョン GKE クラスタを作成することにより、Kubernetes コントロール プレーンが 3 つのゾーンに複製され、高可用性を実現します。使用している TPU のバージョンに応じて、us-west4
、us-east1
、または us-central2
にクラスタを作成します。TPU とゾーンの詳細については、Cloud TPU のリージョンとゾーンをご覧ください。
次のコマンドは、最初にゾーンごとに 1 つのノードを含むノードプールを持つ、ラピッド リリース チャンネルに登録される新しい GKE リージョン クラスタを作成します。このガイドの推論ワークロードの例では、Cloud Storage バケットを使用して事前トレーニング済みモデルを保存しているため、このコマンドは、Workload Identity Federation for GKE と Cloud Storage FUSE CSI ドライバの機能もクラスタで有効にします。
gcloud container clusters create cluster-name \ --region your-region \ --release-channel rapid \ --num-nodes=1 \ --workload-pool=project-id.svc.id.goog \ --addons GcsFuseCsiDriver
既存のクラスタで Workload Identity Federation for GKE と Cloud Storage FUSE CSI ドライバ機能を有効にするには、次のコマンドを実行します。
gcloud container clusters update cluster-name \ --region your-region \ --update-addons GcsFuseCsiDriver=ENABLED \ --workload-pool=project-id.svc.id.goog
このサンプル ワークロードは、次のことを前提として構成されています。
- ノードプールは 4 つのノードを持つ
tpu-topology=4x4
を使用している - ノードプールは
machine-type
ct5lp-hightpu-4t
を使用している
次のコマンドを実行して、新しく作成したクラスタに接続します。
gcloud container clusters get-credentials cluster-name \ --location=cluster-region
Hugging Face FLAX Models: Pokémon 上で Diffusion をトレーニングする
この例では、Pokémon データセットを使用して HuggingFace からの Stable Diffusion モデルをトレーニングします。
Stable Diffusion モデルは、テキスト入力からフォトリアリスティックな画像を生成する、潜在的 text-to-image モデルです。Stable Diffusion の詳細については、以下をご覧ください。
Docker イメージを作成する
Dockerfile は ai-on-gke/tutorials-and-examples/tpu-examples/training/diffusion/
フォルダにあります。
次のコマンドを実行する前に、Docker がリポジトリに push するための適切な権限がアカウントに付与されていることを確認します。
Docker イメージをビルドして push します。
cd ai-on-gke/tutorials-and-examples/tpu-examples/training/diffusion/ docker build -t gcr.io/project-id/diffusion:latest . docker push gcr.io/project-id/diffusion:latest
ワークロードをデプロイする
次のコンテンツを含むファイルを作成し、tpu_job_diffusion.yaml
という名前を付けます。
作成した画像を画像フィールドに入力します。
apiVersion: v1
kind: Service
metadata:
name: headless-svc
spec:
clusterIP: None
selector:
job-name: tpu-job-diffusion
---
apiVersion: batch/v1
kind: Job
metadata:
name: tpu-job-diffusion
spec:
backoffLimit: 0
# Completions and parallelism should be the number of chips divided by 4.
# (e.g. 4 for a v5litepod-16)
completions: 4
parallelism: 4
completionMode: Indexed
template:
spec:
subdomain: headless-svc
restartPolicy: Never
nodeSelector:
cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
cloud.google.com/gke-tpu-topology: 4x4
containers:
- name: tpu-job-diffusion
image: gcr.io/${project-id}/diffusion:latest
ports:
- containerPort: 8471 # Default port using which TPU VMs communicate
- containerPort: 8431 # Port to export TPU usage metrics, if supported
command:
- bash
- -c
- |
cd examples/text_to_image
python3 train_text_to_image_flax.py --pretrained_model_name_or_path=duongna/stable-diffusion-v1-4-flax --dataset_name=lambdalabs/pokemon-blip-captions --resolution=128 --center_crop --random_flip --train_batch_size=4 --mixed_precision=fp16 --max_train_steps=1500 --learning_rate=1e-05 --max_grad_norm=1 --output_dir=sd-pokemon-model
resources:
requests:
google.com/tpu: 4
limits:
google.com/tpu: 4
次に、次のコマンドを使用してデプロイします。
kubectl apply -f tpu_job_diffusion.yaml
クリーンアップ
ジョブの実行が完了したら、次のコマンドを使用して削除できます。
kubectl delete -f tpu_job_diffusion.yaml
PyTorch/XLA: WikiText 上の GPT2
このチュートリアルでは、wikitext データセットを使用して PyTorch/XLA で HuggingFace を使用して、v5e TPU で GPT2 を実行する方法を説明します。
Docker イメージを作成する
Dockerfile は ai-on-gke/tutorials-and-examples/tpu-examples/training/gpt/
フォルダにあります。
次のコマンドを実行する前に、Docker がリポジトリに push するための適切な権限がアカウントに付与されていることを確認します。
Docker イメージをビルドして push します。
cd ai-on-gke/tutorials-and-examples/tpu-examples/training/gpt/ docker build -t gcr.io/project-id/gpt:latest . docker push gcr.io/project-id/gpt:latest
ワークロードをデプロイする
次の YAML をコピーして、tpu_job_gpt.yaml
というファイルに保存します。作成した画像を画像フィールドに入力します。
apiVersion: v1
kind: Service
metadata:
name: headless-svc
spec:
clusterIP: None
selector:
job-name: tpu-job-gpt
---
apiVersion: batch/v1
kind: Job
metadata:
name: tpu-job-gpt
spec:
backoffLimit: 0
# Completions and parallelism should be the number of chips divided by 4.
# (for example, 4 for a v5litepod-16)
completions: 4
parallelism: 4
completionMode: Indexed
template:
spec:
subdomain: headless-svc
restartPolicy: Never
volumes:
# Increase size of tmpfs /dev/shm to avoid OOM.
- name: shm
emptyDir:
medium: Memory
# consider adding `sizeLimit: XGi` depending on needs
nodeSelector:
cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
cloud.google.com/gke-tpu-topology: 4x4
containers:
- name: tpu-job-gpt
image: gcr.io/$(project-id)/gpt:latest
ports:
- containerPort: 8479
- containerPort: 8478
- containerPort: 8477
- containerPort: 8476
- containerPort: 8431 # Port to export TPU usage metrics, if supported.
env:
- name: PJRT_DEVICE
value: 'TPU'
- name: XLA_USE_BF16
value: '1'
command:
- bash
- -c
- |
numactl --cpunodebind=0 python3 -u examples/pytorch/xla_spawn.py --num_cores 4 examples/pytorch/language-modeling/run_clm.py --num_train_epochs 3 --dataset_name wikitext --dataset_config_name wikitext-2-raw-v1 --per_device_train_batch_size 16 --per_device_eval_batch_size 16 --do_train --do_eval --output_dir /tmp/test-clm --overwrite_output_dir --config_name my_config_2.json --cache_dir /tmp --tokenizer_name gpt2 --block_size 1024 --optim adafactor --adafactor true --save_strategy no --logging_strategy no --fsdp "full_shard" --fsdp_config fsdp_config.json
volumeMounts:
- mountPath: /dev/shm
name: shm
resources:
requests:
google.com/tpu: 4
limits:
google.com/tpu: 4
以下のコマンドを使用してデプロイします。
kubectl apply -f tpu_job_gpt.yaml
クリーンアップ
ジョブの実行が完了したら、次のコマンドを使用して削除できます。
kubectl delete -f tpu_job_gpt.yaml
チュートリアル: 単一ホストの推論ワークロード
このチュートリアルでは、JAX、TensorFlow、PyTorch を使用して、GKE v5e TPU 上で事前にトレーニングされたモデルの単一ホストの推論ワークロードを実行する方法について説明します。GKE クラスタでは、大きく分けて、実行する次の 4 つのステップがあります。
Cloud Storage バケットを作成し、バケットへのアクセスを設定します。Cloud Storage バケットを使用して、事前トレーニング済みモデルを保存します。
事前トレーニング済みモデルをダウンロードして TPU 互換モデルに変換します。事前トレーニング済みモデルをダウンロードし、Cloud TPU Converter を使用し、変換されたモデルを Cloud Storage FUSE CSI ドライバを使用して Cloud Storage バケットに保存する Kubernetes Pod を適用します。Cloud TPU コンバータには、専用のハードウェアは必要ありません。このチュートリアルでは、モデルをダウンロードして CPU ノードプールで Cloud TPU Converter を実行する方法について説明します。
変換されたモデルのサーバーを起動します。ReadOnlyMany(ROX)Persistent ボリュームに保存されているボリュームを基盤とするサーバー フレームワークを使用して、モデルを提供する Deployment を適用します。Deployment レプリカは、ノードごとに 1 つの Kubernetes Pod を持つ v5e スライス ノードプールで実行する必要があります。
ロードバランサをデプロイしてモデルサーバーをテストします。サーバーは、LoadBalancer Service を使用して外部リクエストに公開されます。モデル サーバーのテスト用に、サンプル リクエスト付きの Python スクリプトが用意されています。
次の図は、ロードバランサによってリクエストがルーティングされる方法を示しています。
サーバーの Deployment の例
このサンプル ワークロードは、次のことを前提として構成されています。
- クラスタは 3 つのノードを持つ TPU v5 ノードプールで実行されている
- ノードプールはマシンタイプ
ct5lp-hightpu-1t
を使用している。ここで- トポロジが 1x1
- TPU チップの数が 1 個
次の GKE マニフェストは、単一ホストサーバー Deployment を定義します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: bert-deployment
spec:
selector:
matchLabels:
app: tf-bert-server
replicas: 3 # number of nodes in node pool
template:
metadata:
annotations:
gke-gcsfuse/volumes: "true"
labels:
app: tf-bert-server
spec:
nodeSelector:
cloud.google.com/gke-tpu-topology: 1x1 # target topology
cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice # target version
containers:
- name: serve-bert
image: us-docker.pkg.dev/cloud-tpu-images/inference/tf-serving-tpu:2.13.0
env:
- name: MODEL_NAME
value: "bert"
volumeMounts:
- mountPath: "/models/"
name: bert-external-storage
ports:
- containerPort: 8500
- containerPort: 8501
- containerPort: 8431 # Port to export TPU usage metrics, if supported.
resources:
requests:
google.com/tpu: 1 # TPU chip request
limits:
google.com/tpu: 1 # TPU chip request
volumes:
- name: bert-external-storage
persistentVolumeClaim:
claimName: external-storage-pvc
TPU ノードプールで異なる数のノードを使用している場合は、replicas
フィールドをノードの数に変更します。
Standard クラスタで GKE バージョン 1.27 以前が実行されている場合は、次のフィールドをマニフェストに追加します。
spec:
securityContext:
privileged: true
GKE バージョン 1.28 以降では、Kubernetes Pod を特権モードで実行する必要はありません。詳細については、特権モードなしでコンテナを実行するをご覧ください。
別のマシンタイプを使用している場合は、以下のようにします。
cloud.google.com/gke-tpu-topology
を、使用しているマシンタイプのトポロジに設定します。resources
の下の両方のgoogle.com/tpu
フィールドを、対応するマシンタイプのチップ数と一致するように設定します。
設定
次のコマンドを使用して、チュートリアルの Python スクリプトと YAML マニフェストをダウンロードします。
git clone https://github.com/GoogleCloudPlatform/ai-on-gke.git
single-host-inference
ディレクトリに移動します。
cd ai-on-gke/gke-tpu-examples/single-host-inference/
Python 環境を設定する
このチュートリアルで使用する Python スクリプトには、Python バージョン 3.9 以降が必要です。
Python テスト スクリプトを実行する前に、各チュートリアル用に requirements.txt
をインストールしてください。
ローカル環境に適切な Python が設定されていない場合は、Cloud Shell を使用して、このチュートリアルの Python スクリプトをダウンロードして実行できます。
クラスタを設定する
e2-standard-4
マシンタイプを使用してクラスタを作成します。gcloud container clusters create cluster-name \ --region your-region \ --release-channel rapid \ --num-nodes=1 \ --machine-type=e2-standard-4 \ --workload-pool=project-id.svc.id.goog \ --addons GcsFuseCsiDriver
サンプル ワークロードは、次のことを前提としています。
- クラスタは 3 ノードの TPU v5e ノードプールで実行されている。
- TPU ノードプールはマシンタイプ
ct5lp-hightpu-1t
を使用している。
前述のものとは異なるクラスタ構成を使用している場合は、サーバー Deployment マニフェストを編集する必要があります。
JAX Stable Diffusion のデモでは、16 GB 以上の使用可能なメモリを備えたマシンタイプ(たとえば、e2-standard-4
)の CPU ノードプールが必要です。これは、gcloud container clusters create
コマンド、または次のコマンドを使用して、既存のクラスタにノードプールを追加することによって構成されます。
gcloud beta container node-pools create your-pool-name \ --zone=your-cluster-zone \ --cluster=your-cluster-name \ --machine-type=e2-standard-4 \ --num-nodes=1
以下を置き換えます。
your-pool-name
: 作成するノードプールの名前。your-cluster-zone
: クラスタが作成されたゾーン。your-cluster-name
: ノードプールを追加するクラスタの名前。your-machine-type
: ノードプールに作成するノードのマシンタイプ。
モデル ストレージを設定する
提供するモデルの保存方法はいくつかあります。このチュートリアルでは、次のアプローチを使用します。
- TPU で動作するように事前トレーニング済みモデルを変換するために、
ReadWriteMany
(RWX)アクセス権を持つ Persistent Disk を基盤とする Virtual Private Cloud を使用します。 - 複数の単一ホスト TPU でモデルを提供する場合は、Cloud Storage バケットを基盤とする同じ VPC を使用します。
次のコマンドを実行して Cloud Storage バケットを作成します。
gcloud storage buckets create gs://your-bucket-name \ --project=your-bucket-project-id \ --location=your-bucket-location
以下を置き換えます。
your-bucket-name
: Cloud Storage バケットの名前。your-bucket-project-id
: Cloud Storage バケットを作成したプロジェクト ID。your-bucket-location
: Cloud Storage バケットのロケーション。パフォーマンスを改善するには、GKE クラスタが実行されているロケーションを指定します。
GKE クラスタにバケットへのアクセス権を付与するには、次の操作を行います。設定を簡素化するために、次の例ではデフォルトの名前空間とデフォルトの Kubernetes サービス アカウントを使用します。詳細については、Workload Identity Federation for GKE を使用して Cloud Storage バケットへのアクセスを構成するを参照してください。
アプリケーションに IAM サービス アカウントを作成するか、既存の IAM サービス アカウントを使用します。Cloud Storage バケットのプロジェクトでは、任意の IAM サービス アカウントを使用できます。
gcloud iam service-accounts create your-iam-service-acct \ --project=your-bucket-project-id
以下を置き換えます。
your-iam-service-acct
: 新しい IAM サービス アカウントの名前。your-bucket-project-id
: IAM サービス アカウントを作成したプロジェクトの ID。IAM サービス アカウントは、Cloud Storage バケットと同じプロジェクト内に存在する必要があります。
必要なストレージ ロールが IAM サービス アカウントにあることを確認します。
gcloud storage buckets add-iam-policy-binding gs://your-bucket-name \ --member "serviceAccount:your-iam-service-acct@your-bucket-project-id.iam.gserviceaccount.com" \ --role "roles/storage.objectAdmin"
以下を置き換えます。
your-bucket-name
: Cloud Storage バケットの名前。your-iam-service-acct
: 新しい IAM サービス アカウントの名前。your-bucket-project-id
: IAM サービス アカウントを作成したプロジェクトの ID。
2 つのサービス アカウントの間に IAM ポリシー バインディングを追加して、Kubernetes サービス アカウントが IAM サービス アカウントの権限を借用できるようにします。このバインドでは、Kubernetes サービス アカウントが IAM サービス アカウントとして機能します。
gcloud iam service-accounts add-iam-policy-binding your-iam-service-acct@your-bucket-project-id.iam.gserviceaccount.com \ --role roles/iam.workloadIdentityUser \ --member "serviceAccount:your-project-id.svc.id.goog[default/default]"
以下を置き換えます。
your-iam-service-acct
: 新しい IAM サービス アカウントの名前。your-bucket-project-id
: IAM サービス アカウントを作成したプロジェクトの ID。your-project-id
: GKE クラスタを作成したプロジェクトの ID。Cloud Storage バケットと GKE クラスタは、同じプロジェクトまたは異なるプロジェクトに配置できます。
Kubernetes サービス アカウントに IAM サービス アカウントのメールアドレスでアノテーションを付けます。
kubectl annotate serviceaccount default \ --namespace default \ iam.gke.io/gcp-service-account=your-iam-service-acct@your-bucket-project-id.iam.gserviceaccount.com
以下を置き換えます。
your-iam-service-acct
: 新しい IAM サービス アカウントの名前。your-bucket-project-id
: IAM サービス アカウントを作成したプロジェクトの ID。
次のコマンドを実行して、このデモの YAML ファイルにバケット名を入力します。
find . -type f -name "*.yaml" | xargs sed -i "s/BUCKET_NAME/your-bucket-name/g"
your-bucket-name
は、Cloud Storage バケットの名前に置き換えます。次のコマンドを使用して、Persisten Volume と Persistent Volume Claim を作成します。
kubectl apply -f pvc-pv.yaml
JAX モデルの推論とサービス提供
JAX モデルサービスにリクエストを送信するチュートリアルの Python スクリプトを実行するための Python 依存関係をインストールします。
pip install -r jax/requirements.txt
JAX BERT E2E サービス提供デモを実行します。
このデモでは、Hugging Face からの事前トレーニング済みの BERT モデルを使用します。
Kubernetes Pod は次の手順を実行します。
- サンプル リソースから Python スクリプト
export_bert_model.py
をダウンロードして使用し、事前トレーニング済みの bert モデルを一時ディレクトリにダウンロードします。 - Cloud TPU Converter イメージを使用して、事前トレーニング済みモデルを CPU から TPU に変換し、設定時に作成した Cloud Storage バケットにモデルを保存します。
この Kubernetes Pod は、デフォルトのノードプール CPU で実行するように構成されています。次のコマンドで Pod を実行します。
kubectl apply -f jax/bert/install-bert.yaml
以下のコマンドを使用して、モデルが正しくインストールされたことを確認します。
kubectl get pods install-bert
STATUS
が Completed
を読み取るまでに数分かかることがあります。
モデルの TF モデルサーバーを起動する
このチュートリアルのサンプル ワークロードでは、次のことを前提としています。
- クラスタは 3 つのノードを持つ TPU v5 ノードプールで実行されている
- ノードプールは 1 つの TPU チップを含む
ct5lp-hightpu-1t
マシンタイプを使用している
前述のものとは異なるクラスタ構成を使用している場合は、サーバー Deployment マニフェストを編集する必要があります。
Deployment を適用する
kubectl apply -f jax/bert/serve-bert.yaml
次のコマンドを使用して、サーバーが稼働していることを確認します。
kubectl get deployment bert-deployment
AVAILABLE
が 3
を読み取るまでに 1 分ほどかかることがあります。
ロードバランサ サービスを適用する
kubectl apply -f jax/bert/loadbalancer.yaml
以下のコマンドを使用して、ロードバランサが外部トラフィックに対して準備ができていることを確認します。
kubectl get svc tf-bert-service
EXTERNAL_IP
に IP が表示されるまでに数分かかる場合があります。
リクエストをモデルサーバーに送信する
ロードバランサ サービスから外部 IP を取得します。
EXTERNAL_IP=$(kubectl get services tf-bert-service --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
サーバーにリクエストを送信するためのスクリプトを実行する
python3 jax/bert/bert_request.py $EXTERNAL_IP
予想される出力:
For input "The capital of France is [MASK].", the result is ". the capital of france is paris.."
For input "Hello my name [MASK] Jhon, how can I [MASK] you?", the result is ". hello my name is jhon, how can i help you?."
クリーンアップ
リソースをクリーンアップするには、kubectl delete
を逆の順序で実行します。
kubectl delete -f jax/bert/loadbalancer.yaml kubectl delete -f jax/bert/serve-bert.yaml kubectl delete -f jax/bert/install-bert.yaml
JAX Stable Diffusion E2E サービス提供デモを実行する
このデモでは、Hugging Face からの事前トレーニング済みのStable Diffusion モデルを使用します。
Flax Stable Diffusion モデルから TPU 互換の TF2 保存モデルをエクスポートする
Stable Diffusion モデルをエクスポートするには、クラスタの設定で説明されているように、クラスタに 16 GB 以上の利用可能なメモリを備えたマシンタイプの CPU ノードプールが必要です。
Kubernetes Pod は次の手順を実行します。
- サンプル リソースから Python スクリプト
export_stable_diffusion_model.py
をダウンロードして使用し、トレーニング済みの Stable Diffusion モデルを一時ディレクトリにダウンロードします。 - Cloud TPU Converter イメージを使用して、事前トレーニング済みモデルを CPU から TPU に変換し、ストレージの設定時に作成した Cloud Storage バケットにモデルを保存します。
この Kubernetes Pod は、デフォルトの CPU ノードプールで実行するように構成されています。次のコマンドで Pod を実行します。
kubectl apply -f jax/stable-diffusion/install-stable-diffusion.yaml
以下のコマンドを使用して、モデルが正しくインストールされたことを確認します。
kubectl get pods install-stable-diffusion
STATUS
が Completed
を読み取るまでに数分かかることがあります。
モデル用の TF モデルサーバー コンテナを起動します。
サンプル ワークロードは、以下を前提として構成されています。
- クラスタは 3 つのノードを持つ TPU v5 ノードプールで実行されている
- ノードプールは
ct5lp-hightpu-1t
マシンタイプを使用している。ここで- トポロジが 1x1
- TPU チップの数が 1 個
前述のものとは異なるクラスタ構成を使用している場合は、サーバー Deployment マニフェストを編集する必要があります。
Deployment を適用します。
kubectl apply -f jax/stable-diffusion/serve-stable-diffusion.yaml
サーバーが想定どおりに実行されていることを確認します。
kubectl get deployment stable-diffusion-deployment
AVAILABLE
が 3
を読み取るまでに 1 分ほどかかることがあります。
ロードバランサ サービスを適用します。
kubectl apply -f jax/stable-diffusion/loadbalancer.yaml
以下のコマンドを使用して、ロードバランサが外部トラフィックに対して準備ができていることを確認します。
kubectl get svc tf-stable-diffusion-service
EXTERNAL_IP
に IP が表示されるまでに数分かかる場合があります。
リクエストをモデルサーバーに送信する
ロードバランサから外部 IP を取得します。
EXTERNAL_IP=$(kubectl get services tf-stable-diffusion-service --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
サーバーにリクエストを送信するためのスクリプトを実行する
python3 jax/stable-diffusion/stable_diffusion_request.py $EXTERNAL_IP
予想される出力:
プロンプトは Painting of a squirrel skating in New York
であり、出力画像は現在のディレクトリに stable_diffusion_images.jpg
として保存されます。
クリーンアップ
リソースをクリーンアップするには、kubectl delete
を逆の順序で実行します。
kubectl delete -f jax/stable-diffusion/loadbalancer.yaml kubectl delete -f jax/stable-diffusion/serve-stable-diffusion.yaml kubectl delete -f jax/stable-diffusion/install-stable-diffusion.yaml
TensorFlow ResNet 50 E2E サービス提供デモを実行します。
TF モデルサービスにリクエストを送信するチュートリアルの Python スクリプトを実行するための Python 依存関係をインストールします。
pip install -r tf/resnet50/requirements.txt
ステップ 1: モデルを変換する
モデル変換を適用します。
kubectl apply -f tf/resnet50/model-conversion.yml
以下のコマンドを使用して、モデルが正しくインストールされたことを確認します。
kubectl get pods resnet-model-conversion
STATUS
が Completed
を読み取るまでに数分かかることがあります。
ステップ 2: TensorFlow サービス提供でモデルを提供する
モデルサービス提供の Deployment を適用します。
kubectl apply -f tf/resnet50/deployment.yml
次のコマンドを使用して、サーバーが想定どおりに実行されていることを確認します。
kubectl get deployment resnet-deployment
AVAILABLE
が 3
を読み取るまでに 1 分ほどかかることがあります。
ロードバランサ サービスを適用します。
kubectl apply -f tf/resnet50/loadbalancer.yml
以下のコマンドを使用して、ロードバランサが外部トラフィックに対して準備ができていることを確認します。
kubectl get svc resnet-service
EXTERNAL_IP
に IP が表示されるまでに数分かかる場合があります。
ステップ 3: モデルサーバーにテスト リクエストを送信する
ロードバランサから外部 IP を取得します。
EXTERNAL_IP=$(kubectl get services resnet-service --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
テスト リクエスト(HTTP)スクリプトを実行して、モデルサーバーにリクエストを送信します。
python3 tf/resnet50/request.py --host $EXTERNAL_IP
レスポンスは次のようになります。
Predict result: ['ImageNet ID: n07753592, Label: banana, Confidence: 0.94921875', 'ImageNet ID: n03532672, Label: hook, Confidence: 0.0223388672', 'ImageNet ID: n07749582, Label: lemon, Confidence: 0.00512695312
ステップ 4: クリーンアップする
リソースをクリーンアップするには、次の kubectl delete
コマンドを実行します。
kubectl delete -f tf/resnet50/loadbalancer.yml kubectl delete -f tf/resnet50/deployment.yml kubectl delete -f tf/resnet50/model-conversion.yml
使用が完了したら、GKE のノードプールとクラスタを削除してください。
PyTorch モデルの推論とサービス提供
PyTorch モデルサービスにリクエストを送信するチュートリアルの Python スクリプトを実行するための Python 依存関係をインストールします。
pip install -r pt/densenet161/requirements.txt
TorchServe Densenet161 E2E サービス提供デモを実行します。
モデル アーカイブを生成します。
- モデル アーカイブを適用します。
kubectl apply -f pt/densenet161/model-archive.yml
- 以下のコマンドを使用して、モデルが正しくインストールされたことを確認します。
kubectl get pods densenet161-model-archive
STATUS
がCompleted
を読み取るまでに数分かかることがあります。TorchServe でモデルを提供します。
モデルサービス提供の Deployment を適用します。
kubectl apply -f pt/densenet161/deployment.yml
次のコマンドを使用して、サーバーが想定どおりに実行されていることを確認します。
kubectl get deployment densenet161-deployment
AVAILABLE
が3
を読み取るまでに 1 分ほどかかることがあります。ロードバランサ サービスを適用します。
kubectl apply -f pt/densenet161/loadbalancer.yml
以下のコマンドを使用して、ロードバランサが外部トラフィックに対して準備ができていることを確認します。
kubectl get svc densenet161-service
EXTERNAL_IP
に IP が表示されるまでに数分かかる場合があります。
モデルサーバーにテスト リクエストを送信します。
ロードバランサから外部 IP を取得します。
EXTERNAL_IP=$(kubectl get services densenet161-service --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
テスト リクエスト スクリプトを実行して、モデルサーバーにリクエスト(HTTP)を送信します。
python3 pt/densenet161/request.py --host $EXTERNAL_IP
次のようなレスポンスが表示されます。
Request successful. Response: {'tabby': 0.47878125309944153, 'lynx': 0.20393909513950348, 'tiger_cat': 0.16572578251361847, 'tiger': 0.061157409101724625, 'Egyptian_cat': 0.04997897148132324
次の
kubectl delete
コマンドを実行して、リソースをクリーンアップします。kubectl delete -f pt/densenet161/loadbalancer.yml kubectl delete -f pt/densenet161/deployment.yml kubectl delete -f pt/densenet161/model-archive.yml
一般的な問題のトラブルシューティング
GKE のトラブルシューティングについては、GKE で TPU のトラブルシューティングを行うをご覧ください。
TPU の初期化に失敗した
次のエラーが発生した場合は、TPU コンテナを特権モードで実行しているか、コンテナ内で ulimit
を増やしていることを確認してください。詳細については、特権モードなしで実行するをご覧ください。
TPU platform initialization failed: FAILED_PRECONDITION: Couldn't mmap: Resource
temporarily unavailable.; Unable to create Node RegisterInterface for node 0,
config: device_path: "/dev/accel0" mode: KERNEL debug_data_directory: ""
dump_anomalies_only: true crash_in_debug_dump: false allow_core_dump: true;
could not create driver instance
スケジューリングのデッドロック
2 つのジョブ(ジョブ A とジョブ B)があり、両方が特定の TPU トポロジ(たとえば、v4-32
)を持つ TPU スライスでスケジューリングされるとします。また、GKE クラスタ内に 2 つの v4-32
TPU スライスがあり、これをスライス X とスライス Y と呼ぶことにします。クラスタには、両方のジョブをスケジュールするのに十分な容量があるため、理論上は両方のジョブが迅速にスケジュールされるはずです(2 つの TPU v4-32
スライスのそれぞれに 1 つのジョブ)。
ただし、慎重に計画しないと、スケジューリングのデッドロックに陥る可能性があります。Kubernetes スケジューラがジョブ A から 1 つの Kubernetes Pod をスライス X でスケジュールし、その後、ジョブ B から 1 つの Kubernetes Pod をスライス X でスケジュールするとします。この場合、ジョブ A の Kubernetes Pod アフィニティ ルールに基づいて、スケジューラはジョブ A の残りのすべての Kubernetes Pod をスライス X にスケジュールしようとします。ジョブ B も同じです。したがって、ジョブ A とジョブ B のどちらも、単一のスライスで完全にスケジュールすることはできません。その結果、スケジューリングのデッドロックが発生します。
スケジューリングのデッドロックのリスクを回避するために、次の例のように、cloud.google.com/gke-nodepool
の Kubernetes Pod 反アフィニティを topologyKey
として使用できます。
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
parallelism: 2
template:
metadata:
labels:
job: pi
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: job
operator: In
values:
- pi
topologyKey: cloud.google.com/gke-nodepool
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: job
operator: NotIn
values:
- pi
topologyKey: cloud.google.com/gke-nodepool
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values:
- kube-system
containers:
- name: pi
image: perl:5.34.0
command: ["sleep", "60"]
restartPolicy: Never
backoffLimit: 4
Terraform を使用した TPU ノードプール リソースの作成
Terraform を使用してクラスタとノードプール リソースを管理することもできます。
既存の GKE クラスタにマルチホスト TPU スライス ノードプールを作成する
マルチホスト TPU ノードプールを作成する既存のクラスタがある場合は、次の Terraform スニペットを使用できます。
resource "google_container_cluster" "cluster_multi_host" {
…
release_channel {
channel = "RAPID"
}
workload_identity_config {
workload_pool = "my-gke-project.svc.id.goog"
}
addons_config {
gcs_fuse_csi_driver_config {
enabled = true
}
}
}
resource "google_container_node_pool" "multi_host_tpu" {
provider = google-beta
project = "${project-id}"
name = "${node-pool-name}"
location = "${location}"
node_locations = ["${node-locations}"]
cluster = google_container_cluster.cluster_multi_host.name
initial_node_count = 2
node_config {
machine_type = "ct4p-hightpu-4t"
reservation_affinity {
consume_reservation_type = "SPECIFIC_RESERVATION"
key = "compute.googleapis.com/reservation-name"
values = ["${reservation-name}"]
}
workload_metadata_config {
mode = "GKE_METADATA"
}
}
placement_policy {
type = "COMPACT"
tpu_topology = "2x2x2"
}
}
次の値を置き換えます。
your-project
: ワークロードを実行している Google Cloud プロジェクト。your-node-pool
: 作成するノードプールの名前。us-central2
: ワークロードを実行しているリージョン。us-central2-b
: ワークロードを実行しているゾーン。your-reservation-name
: 予約の名前。
既存の GKE クラスタに単一ホスト TPU スライス ノードプールを作成する
次の Terraform スニペットを使用します。
resource "google_container_cluster" "cluster_single_host" {
…
cluster_autoscaling {
autoscaling_profile = "OPTIMIZE_UTILIZATION"
}
release_channel {
channel = "RAPID"
}
workload_identity_config {
workload_pool = "${project-id}.svc.id.goog"
}
addons_config {
gcs_fuse_csi_driver_config {
enabled = true
}
}
}
resource "google_container_node_pool" "single_host_tpu" {
provider = google-beta
project = "${project-id}"
name = "${node-pool-name}"
location = "${location}"
node_locations = ["${node-locations}"]
cluster = google_container_cluster.cluster_single_host.name
initial_node_count = 0
autoscaling {
total_min_node_count = 2
total_max_node_count = 22
location_policy = "ANY"
}
node_config {
machine_type = "ct4p-hightpu-4t"
workload_metadata_config {
mode = "GKE_METADATA"
}
}
}
次の値を置き換えます。
your-project
: ワークロードを実行している Google Cloud プロジェクト。your-node-pool
: 作成するノードプールの名前。us-central2
: ワークロードを実行しているリージョン。us-central2-b
: ワークロードを実行しているゾーン。