GKE 中的 TPU 简介

Google Kubernetes Engine (GKE) 客户现在可以创建包含 TPU v4 和 v5e slice 的 Kubernetes 节点池。如需详细了解 TPU,请参阅系统架构

使用 GKE 时,您首先必须创建一个 GKE 集群。然后,向集群添加节点池。GKE 节点池是一组具有相同属性的虚拟机。对于 TPU 工作负载,节点池由 TPU 虚拟机组成。

节点池类型

GKE 支持两种类型的 TPU 节点池:

多主机 TPU 切片节点池

多主机 TPU 切片节点池是包含两个或更多互连 TPU 虚拟机的节点池。每个虚拟机都有一个与之连接的 TPU 设备。多主机切片中的 TPU 通过高速互连 (ICI) 连接。创建多主机切片节点池后,您无法向其添加节点。例如,您无法创建 v4-32 节点池,然后再向该节点池添加其他 Kubernetes 节点(TPU 虚拟机)。如需向 GKE 集群添加额外的 TPU slice,您必须创建新的节点池。

多主机 TPU 切片节点池中的主机被视为单个原子单元。如果 GKE 无法在切片中部署一个节点,则不会在该切片中部署任何节点。

如果多主机 TPU 切片中的节点需要维修,GKE 会关闭该切片中的所有 TPU 虚拟机,从而强制将工作负载中的所有 Kubernetes Pod 都逐出。当切片中的所有 TPU 虚拟机都已启动并运行后,Kubernetes Pod 便可以在新切片中的 TPU 虚拟机上进行调度。

下图显示了 v5litepod-16 (v5e) 多主机 TPU 切片示例。此切片包含四个 TPU 虚拟机。每个 TPU 虚拟机都具有四个通过高速互连 (ICI) 连接的 TPU v5e 芯片,每个 TPU v5e 芯片都具有一个 TensorCore。

多主机 TPU 切片示意图

下图显示了一个 GKE 集群,其中包含一个 TPU v5litepod-16 (v5e) 切片(拓扑:4x4)和一个 TPU v5litepod-8 (v5e) 切片(拓扑:2x4):

TPU v5e pod 示意图

如需查看在多主机 TPU 切片上运行工作负载的示例,请参阅在 TPU 上运行工作负载

单主机 TPU 切片节点池

单主机切片节点池是包含一个或多个独立 TPU 虚拟机的节点池。每个虚拟机都有一个与之连接的 TPU 设备。虽然单主机切片节点池中的虚拟机可以通过数据中心网络 (DCN) 进行通信,但连接到虚拟机的 TPU 不会互连。

下图显示了一个包含 7 台 v4-8 机器的单主机 TPU 切片示例:

单主机切片节点池图

如需查看在单主机 TPU 切片上运行工作负载的示例,请参阅在 TPU 上运行工作负载

GKE 节点池的 TPU 机器类型

在创建节点池之前,您需要选择工作负载所需的 TPU 切片的 TPU 版本和大小。GKE Standard 版本 1.26.1-gke.1500 及更高版本支持 TPU v4,GKE Standard 版本 1.27.2-gke.2100 及更高版本支持 v5e,GKE Standard 版本 1.28.3-gke.1024000 及更高版本支持 v5p。

GKE Autopilot 1.29.2-gke.1521000 及更高版本支持 TPU v4、v5e 和 v5p。

如需详细了解不同 TPU 版本的硬件规格,请参阅系统架构。创建 TPU 节点池时,请根据模型的大小及其所需的内存量选择 TPU 切片大小(TPU 拓扑)。您在创建节点池时指定的机器类型取决于 slice 的版本和大小。

v5e

以下是适用于训练和推理用例的 TPU v5e 机器类型和拓扑:

机器类型 拓扑 TPU 芯片数量 虚拟机数量 推荐的使用场景
ct5lp-hightpu-1t 1x1 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-aus-east5-bus-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
被抢占的可能性

为了为芯片数量更多的虚拟机腾出空间,GKE 调度程序可能会抢占并重新调度芯片数量较少的虚拟机。因此,8 芯片虚拟机更有可能抢占 1 芯片和 4 芯片虚拟机。

v4 和 v5p

以下是 TPU v4 和 v5p 机器类型:

机器类型 vCPU 的数量 内存 (GB) NUMA 节点的数量
ct4p-hightpu-4t 240 407 2
ct5p-hightpu-4t 208 448 2

创建 TPU v4 切片时,请使用具有一个主机且包含 4 个芯片的 ct4p-hightpu-4t 机器类型。如需了解详情,请参阅 v4 拓扑TPU 系统架构us-central2-b 中提供 TPU v4 切片机器类型。您的 GKE Standard 集群必须运行控制平面版本 1.26.1-gke.1500 或更高版本。GKE Autopilot 集群必须运行控制平面版本 1.29.2-gke.1521000 或更高版本。

创建 TPU v5p 切片时,请使用具有一个主机且包含 4 个芯片的 ct5p-hightpu-4t 机器类型。TPU v5p 切片机器类型在 us-west4-aus-east5-a 中可用。GKE Standard 集群必须运行控制平面版本 1.28.3-gke.1024000 或更高版本。GKE Autopilot 必须运行 1.29.2-gke.1521000 或更高版本。如需详细了解 v5p,请参阅 v5p 训练简介

已知问题和限制

  • Kubernetes Pod 的数量上限:您可以在单个 TPU 虚拟机中最多运行 256 个 Kubernetes Pod。
  • 仅限特定预留:在 GKE 中使用 TPU 时,SPECIFICgcloud container node-pools create 命令的 --reservation-affinity 标志唯一支持的值。
  • 仅支持抢占式 TPU 的 Spot 虚拟机变体Spot 虚拟机与抢占式虚拟机类似,受相同的可用性限制,但没有 24 小时的最长时长限制。
  • 不支持费用分配GKE 费用分配用量计量不包含有关 TPU 用量或费用的任何数据。
  • 自动扩缩器可能会计算容量:在包含 TPU 虚拟机的新节点可用之前,集群自动扩缩器可能会错误地计算这些节点的容量。然后,集群自动扩缩器可能会执行额外的扩容,因此创建超出需要的节点。集群自动扩缩器会在常规缩容操作后缩容额外的节点(如果不需要)。
  • 自动伸缩器取消扩容:集群自动伸缩器会取消等待状态超过 10 小时的 TPU 节点池的扩容。集群自动扩缩器稍后将重试此类扩容操作。对于不使用预留的客户,此行为可能会降低 TPU 可获取性。
  • 污点可能会阻止缩容:容忍 TPU 污点的非 TPU 工作负载可能会阻止缩容在排空 TPU 节点池期间重新创建的节点池。

确保有足够的 TPU 和 GKE 配额

您可能需要在创建资源的区域中增加某些与 GKE 相关的配额。

以下配额的默认值可能需要增加:

  • Persistent Disk SSD (GB) 配额:默认情况下,每个 Kubernetes 节点的启动磁盘需要 100GB。因此,此配额应至少设置为(您预计要创建的 GKE 节点的最大数量)* 100GB。
  • 使用中 IP 地址配额:每个 Kubernetes 节点使用一个 IP 地址。因此,此配额应至少设置为您预计要创建的 GKE 节点数上限。

如需申请增加配额,请参阅申请更高配额。如需详细了解 TPU 配额类型,请参阅 TPU 配额

您的配额增加请求可能需要几天才能获得批准。如果您在几天内无法获得配额增加请求的批准,请与您的 Google 客户支持团队联系。

迁移 TPU 预留

如果您不打算将现有 TPU 预留与 GKE 中的 TPU 搭配使用,请跳过本部分,然后前往创建 Google Kubernetes Engine 集群

如需将预留的 TPU 与 GKE 搭配使用,您必须先将 TPU 预留迁移到新的基于 Compute Engine 的预留系统。

关于此次迁移,您需要了解以下几点:

  • 迁移到新的基于 Compute Engine 的预留系统的 TPU 容量无法与 Cloud TPU 排队资源 API 搭配使用。如果打算将 TPU 排队资源用于您的预留,则需要将 TPU 预留的一部分迁移到新的基于 Compute Engine 的预留系统。
  • 在 TPU 迁移到新的基于 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 清单中为每个工作负载指定两个选择器:

  • cloud.google.com/gke-accelerator-type 设置为 tpu-v5-lite-podslicetpu-v5p-slicetpu-v4-podslice
  • cloud.google.com/gke-tpu-topology 设置为节点的 TPU 拓扑。

训练工作负载推理工作负载部分包含示例清单,说明了如何使用这些节点选择器。

工作负载安排注意事项

TPU 具有独特的特性,需要在 Kubernetes 中进行特殊的工作负载调度和管理。如需了解详情,请参阅 GKE 文档中的工作负载调度注意事项

节点修复

如果多主机 TPU 切片节点池中的节点健康状况不佳,GKE 会重新创建整个节点池。如需了解详情,请参阅 GKE 文档中的节点自动修复

多切片 - 超越单个切片

您可以在多切片中汇总较小的时间片,以处理较大训练工作负载。如需了解详情,请参阅 Cloud TPU 多切片

训练工作负载教程

这些教程重点介绍如何在多主机 TPU 切片(例如 4 台 v5e 机器)上训练工作负载。它们涵盖以下型号:

  • Hugging Face FLAX 模型:使用《精灵宝可梦》训练扩散模型
  • PyTorch/XLA:WikiText 上的 GPT2

下载教程资源

使用以下命令下载每个预训练模型的教程 Python 脚本和 YAML 规范:

git clone https://github.com/GoogleCloudPlatform/ai-on-gke.git

创建集群并连接到集群

创建区域性 GKE 集群,以便将 Kubernetes 控制平面复制到三个可用区,从而提高可用性。在 us-west4us-east1us-central2 中创建集群,具体取决于您使用的 TPU 版本。如需详细了解 TPU 和可用区,请参阅 Cloud TPU 区域和可用区

以下命令会创建一个订阅了快速发布渠道的新 GKE 区域级集群,其中节点池最初包含每个可用区一个节点。该命令还会在集群上启用适用于 GKE 的工作负载身份联合和 Cloud Storage FUSE CSI 驱动程序功能,因为本指南中的示例推理工作负载使用 Cloud Storage 存储分区存储预训练模型。

gcloud container clusters create cluster-name \
  --region your-region \
  --release-channel rapid \
  --num-nodes=1 \
  --workload-pool=project-id.svc.id.goog \
  --addons GcsFuseCsiDriver

如需为现有集群启用适用于 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

示例工作负载的配置基于以下假设:

  • 节点池使用 tpu-topology=4x4,其中包含 4 个节点
  • 节点池使用的是 machine-type ct5lp-hightpu-4t

运行以下命令以连接到新创建的集群:

gcloud container clusters get-credentials cluster-name \
--location=cluster-region

Hugging Face FLAX 模型:使用《精灵宝可梦》训练扩散模型

此示例使用 Pokémon 数据集训练 HuggingFace 中的 Stable Diffusion 模型。

Stable Diffusion 模型是一种潜在的文本到图像模型,可根据任何文本输入生成逼真的图片。如需详细了解稳定扩散,请参阅:

创建 Docker 映像

Dockerfile 位于 ai-on-gke/tutorials-and-examples/tpu-examples/training/diffusion/ 文件夹下。

在运行以下命令之前,请确保您的账号具有适当的权限,以便 Docker 推送到代码库。

构建并推送 Docker 映像:

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。使用您刚刚创建的图片填充“image”字段。

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

本教程介绍了如何使用 HuggingFace 在 PyTorch/XLA 上使用 wikitext 数据集在 v5e TPU 上运行 GPT2。

创建 Docker 映像

Dockerfile 位于 ai-on-gke/tutorials-and-examples/tpu-examples/training/gpt/ 文件夹下。

在运行以下命令之前,请确保您的账号具有适当的权限,以便 Docker 推送到代码库。

构建并推送 Docker 映像:

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 集群上执行四个单独的步骤:

  1. 创建一个 Cloud Storage 存储桶并设置对该存储桶的访问权限。您可以使用 Cloud Storage 存储桶来存储预训练模型。

  2. 下载并将预训练模型转换为与 TPU 兼容的模型。应用 Kubernetes Pod,用于下载预训练模型、使用 Cloud TPU 转换器,并使用 Cloud Storage FUSE CSI 驱动程序将转换后的模型存储到 Cloud Storage 存储桶中。Cloud TPU 转换器不需要专用硬件。本教程介绍了如何下载模型并在 CPU 节点池中运行 Cloud TPU 转换器。

  3. 启动转换后的模型的服务器。应用部署,使用由存储在 ReadOnlyMany (ROX) 持久卷中的卷支持的服务器框架来提供模型。部署副本必须在 v5e slice 节点池中运行,且每个节点有一个 Kubernetes Pod。

  4. 部署负载均衡器以测试模型服务器。服务器使用 LoadBalancer 服务公开给外部请求。我们提供了一个 Python 脚本,其中包含用于测试模型服务器的示例请求。

下图展示了负载平衡器如何路由请求。

显示负载均衡器路由的图表

服务器部署示例

这些示例工作负载的配置基于以下假设:

  • 集群正在运行,其中包含 3 个节点的 TPU v5 节点池
  • 节点池使用机器类型 ct5lp-hightpu-1t,其中:
    • 拓扑为 1x1
    • TPU 芯片数量为 1

以下 GKE 清单定义了一个单主机服务器部署。

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 或更高版本。请务必先为每个教程安装 requirements.txt,然后再运行 Python 测试脚本。

如果您的本地环境中未正确设置 Python,您可以使用 Cloud Shell 下载并运行本教程中的 Python 脚本。

设置集群

  1. 使用 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
  2. 创建单主机 TPU 节点池

示例工作负载假定以下情况:

  • 您的集群正在运行,其中包含一个包含 3 个节点的 TPU v5e 节点池。
  • TPU 节点池使用机器类型 ct5lp-hightpu-1t

如果您使用的集群配置与前面所述的不同,则需要修改服务器部署清单

对于 JAX Stable Diffusion 演示,您需要一个 CPU 节点池,其机器类型的可用内存应至少为 16 Gi(例如 e2-standard-4)。您可以在 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 上运行,我们将使用由 Persistent Disk 支持且具有 ReadWriteMany (RWX) 访问权限的虚拟私有云。
  • 如需在多个单主机 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 服务账号。如需了解详情,请参阅使用适用于 GKE 的 GKE Workload Identity 联合配置对 Cloud Storage 存储分区的访问权限

  1. 为您的应用创建 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 存储桶位于同一项目中。
  2. 确保您的 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。
  3. 通过在两个服务账号之间添加 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 集群可以位于同一项目中,也可以位于不同的项目中。
  4. 使用 IAM 服务账号的电子邮件地址为 Kubernetes 服务账号添加注解。

    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。
  5. 运行以下命令,在本演示的 YAML 文件中填充您的存储桶名称:

    find . -type f -name "*.yaml" | xargs sed -i "s/BUCKET_NAME/your-bucket-name/g"

    your-bucket-name 替换为 Cloud Storage 存储分区的名称。

  6. 使用以下命令创建永久性卷和永久性卷声明:

    kubectl apply -f pvc-pv.yaml

JAX 模型推理和服务

安装 Python 依赖项,以运行向 JAX 模型服务发送请求的教程 Python 脚本。

pip install -r jax/requirements.txt

运行 JAX BERT E2E 服务演示:

此演示使用 Hugging Face 中的预训练 BERT 模型

Kubernetes Pod 会执行以下步骤:

  1. 下载并使用示例资源中的 Python 脚本 export_bert_model.py,将预训练的 BERT 模型下载到临时目录。
  2. 使用 Cloud TPU 转换器映像将预训练模型从 CPU 转换为 TPU,并将模型存储在您在设置期间创建的 Cloud Storage 存储桶中。

此 Kubernetes Pod 配置为在默认节点池 CPU 上运行。使用以下命令运行 Pod:

kubectl apply -f jax/bert/install-bert.yaml

使用以下命令验证模型是否已正确安装:

kubectl get pods install-bert

STATUS 读取 Completed 可能需要几分钟时间。

为模型启动 TF 模型服务器

本教程中的示例工作负载假定以下情况:

  • 集群运行时使用包含三个节点的 TPU v5 节点池
  • 节点池使用的是包含一个 TPU 芯片的 ct5lp-hightpu-1t 机器类型。

如果您使用的集群配置与前面所述的不同,则需要修改服务器部署清单

应用部署
kubectl apply -f jax/bert/serve-bert.yaml

使用以下命令验证服务器是否正在运行:

kubectl get deployment bert-deployment

AVAILABLE 读取 3 可能需要一分钟。

应用负载均衡器服务
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 中的预训练稳定型扩散模型

从 Flax Stable Diffusion 模型导出与 TPU 兼容的 TF2 保存的模型

如需导出稳定的扩散模型,集群必须有一个 CPU 节点池,且机器类型的可用内存必须大于或等于 16 GiB,如设置集群中所述。

Kubernetes Pod 会执行以下步骤:

  1. 下载并使用示例资源中的 Python 脚本 export_stable_diffusion_model.py,将预训练的稳定扩散模型下载到临时目录。
  2. 使用 Cloud TPU 转换器映像将预训练模型从 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 模型服务器容器

示例工作负载的配置基于以下假设:

  • 集群运行时使用包含三个节点的 TPU v5 节点池
  • 节点池使用 ct5lp-hightpu-1t 机器类型,其中:
    • 拓扑为 1x1
    • TPU 芯片数量为 1

如果您使用的集群配置与前面所述的不同,则需要修改服务器部署清单

应用部署:

kubectl apply -f jax/stable-diffusion/serve-stable-diffusion.yaml

验证服务器是否按预期运行:

kubectl get deployment stable-diffusion-deployment

AVAILABLE 读取 3 可能需要一分钟。

应用负载均衡器服务:

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 服务演示:

安装 Python 依赖项,以运行向 TF 模型服务发送请求的教程 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 Serving 提供模型

应用模型服务部署:

kubectl apply -f tf/resnet50/deployment.yml

使用以下命令验证服务器是否按预期运行:

kubectl get deployment resnet-deployment

AVAILABLE 读取 3 可能需要一分钟。

应用负载均衡器服务:

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 模型推理和服务

安装 Python 依赖项,以运行向 PyTorch 模型服务发送请求的教程 Python 脚本:

pip install -r pt/densenet161/requirements.txt

运行 TorchServe Densenet161 E2E 服务演示:

  1. 生成模型归档文件。

    1. 应用模型归档:
    kubectl apply -f pt/densenet161/model-archive.yml
    1. 使用以下命令验证模型是否已正确安装:
    kubectl get pods densenet161-model-archive

    STATUS 读取 Completed 可能需要几分钟时间。

  2. 使用 TorchServe 提供模型:

    1. 应用模型服务部署:

      kubectl apply -f pt/densenet161/deployment.yml
    2. 使用以下命令验证服务器是否按预期运行:

      kubectl get deployment densenet161-deployment

      AVAILABLE 读取 3 可能需要一分钟。

    3. 应用负载均衡器服务:

      kubectl apply -f pt/densenet161/loadbalancer.yml

      使用以下命令验证负载均衡器是否已准备好处理外部流量:

      kubectl get svc densenet161-service

      EXTERNAL_IP 可能需要几分钟时间才能列出 IP 地址。

  3. 向模型服务器发送测试请求:

    1. 从负载均衡器获取外部 IP:

      EXTERNAL_IP=$(kubectl get services densenet161-service --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
    2. 运行测试请求脚本,将请求 (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
  4. 通过运行以下 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 问题排查信息,请参阅排查 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

调度出现死锁

假设您有两个作业(作业 A 和作业 B),并且这两个作业都将在具有给定 TPU 拓扑(例如 v4-32)的 TPU 切片上调度。此外,假设您在 GKE 集群中拥有两个 v4-32 TPU 切片;我们将它们称为切片 X 和切片 Y。由于您的集群具有充足的容量来调度这两个作业,因此理论上这两个作业都应快速调度完毕,每个 TPU v4-32 切片上各有一个作业。

不过,如果不仔细规划,就可能会陷入调度死锁。假设 Kubernetes 调度器将作业 A 中的一个 Kubernetes Pod 调度到 slice X,然后将作业 B 中的一个 Kubernetes Pod 调度到 slice X。在这种情况下,鉴于作业 A 的 Kubernetes Pod 亲和性规则,调度器会尝试将作业 A 的所有剩余 Kubernetes Pod 调度到 slice X 上。作业 B 也是如此。因此,作业 A 和作业 B 都无法在单个 slice 上完全调度。结果将是调度死锁。

为避免出现调度死锁的风险,您可以使用 Kubernetes Pod 反亲和性,并将 cloud.google.com/gke-nodepool 作为 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:您运行工作负载的可用区。