Hyperdisk ML で AI / ML データの読み込みを高速化する

このガイドでは、Hyperdisk ML を使用して Google Kubernetes Engine(GKE)で AI/ML モデルの重みを簡単に読み込み、高速化する方法について説明します。Compute Engine Persistent Disk の CSI ドライバは、GKE クラスタの Hyperdisk ML ストレージにアクセスするための基本的な方法です。

概要

Hyperdisk ML は、アプリケーションのスケールアウトに使用できる高パフォーマンスのストレージ ソリューションです。多くの仮想マシンに同時に高い集計スループットを提供するため、大量のデータへのアクセスが必要な AI/ML ワークロードを実行する場合に最適です。

読み取り専用(複数回)モードで有効にすると、Hyperdisk ML を使用して、モデル レジストリから直接読み込む場合と比較して、モデル重みの読み込みを最大で 11.9 倍に高速化できます。この高速化は、Google Cloud Hyperdisk アーキテクチャによって達成されます。このアーキテクチャでは、1.2 TiB/秒で 2,500 個の同時実行ノードにスケーリングできます。これにより、読み込み時間が短縮され、AI/ML 推論ワークロードの Pod のオーバープロビジョニングが削減されます。

Hyperdisk ML を作成して使用する手順の概要は次のとおりです。

  1. Persistent Disk ディスク イメージにデータを事前にキャッシュに保存またはハイドレートする: サービングに使用できる外部データソース(Cloud Storage から読み込まれた Gemma 重みなど)のデータを使用して Hyperdisk ML ボリュームを読み込みます。ディスク イメージの Persistent Disk には、Google Cloud Hyperdisk との互換性が必要です。
  2. 既存の Google Cloud Hyperdisk を使用して Hyperdisk ML ボリュームを作成する: データが読み込まれた Hyperdisk ML ボリュームを参照する Kubernetes ボリュームを作成します。必要に応じて、マルチゾーン ストレージ クラスを作成して、Pod が実行されるすべてのゾーンでデータを使用できるようにします。
  3. Hyperdisk ML ボリュームを消費する Kubernetes Deployment を作成する: アプリケーションが使用できるよう、高速化されたデータ読み込みで Hyperdisk ML ボリュームを参照します。

マルチゾーン Hyperdisk ML ボリューム

Hyperdisk ML ディスクは単一ゾーンでのみ使用できます。必要に応じて、Hyperdisk ML のマルチゾーン機能を使用して、同じコンテンツを含む複数のゾーンディスクを単一の論理 PersistentVolumeClaim と PersistentVolume に動的にリンクできます。マルチゾーン機能で参照されるゾーンディスクは、同じリージョンに配置する必要があります。たとえば、リージョン クラスタが us-central1 に作成されている場合、マルチゾーン ディスクは同じリージョン(us-central1-aus-central1-b など)に配置する必要があります。

AI/ML 推論の一般的なユースケースは、Spot VM を使用してアクセラレータの可用性と費用対効果が高まるよう、ゾーン間で Pod を実行することです。Hyperdisk ML はゾーンベースであるため、推論サーバーがゾーン間で多くの Pod を実行している場合、GKE はゾーン間でディスクを自動的にクローニングして、データがアプリケーションに従うようにします。

外部データソースからの Hyperdisk ML のハイドレーションと、ゾーン間でデータにアクセスするためのマルチゾーン PV の作成。

マルチゾーン Hyperdisk ML ボリュームには次の制限があります。

  • ボリュームのサイズ変更とボリューム スナップショット オペレーションはサポートされていません。
  • マルチゾーン Hyperdisk ML ボリュームは、読み取り専用モードでのみサポートされます。
  • マルチゾーン Hyperdisk ML ボリュームで既存のディスクを使用する場合、GKE はゾーン間のディスク コンテンツが同じであることを検証するチェックを実行しません。いずれかのディスクに異なるコンテンツが含まれている場合は、アプリケーションでゾーン間の不整合が生じる可能性を考慮してください。

詳細については、VolumeSnapshot からマルチゾーン ReadOnlyMany Hyperdisk ML ボリュームを作成するをご覧ください。

始める前に

作業を始める前に、次のタスクが完了していることを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。すでに gcloud CLI をインストールしている場合は、gcloud components update を実行して最新のバージョンを取得する。
  • デフォルトのリージョンとゾーンをサポートされている値のいずれかに設定します。
  • このガイドで必要なノードを作成できるだけの十分な割り当てが Google Cloud プロジェクトにあることを確認します。GKE クラスタと Kubernetes リソースの作成のコードサンプルでは、選択したリージョンに最小割り当てとして 88 個の C3 CPU と 8 個の NVIDIA L4 GPU が必要です。

要件

GKE で Hyperdisk ML ボリュームを使用するには、クラスタが次の要件を満たしている必要があります。

  • GKE バージョン 1.30.2-gke.1394000 以降を実行している Linux クラスタを使用する。リリース チャンネルを使用する場合は、このドライバに必要な GKE バージョン以上がチャンネルにあることを確認してください。
  • Compute Engine Persistent Disk の CSI ドライバが有効になっていることを確認します。Compute Engine Persistent Disk ドライバは新しい Autopilot クラスタと Standard クラスタでデフォルトで有効になっており、Autopilot の使用時には無効にすることや編集することはできません。使用しているクラスタで Compute Engine Persistent Disk の CSI ドライバを有効にする必要がある場合は、既存のクラスタで Compute Engine Persistent Disk の CSI ドライバを有効にするをご覧ください。
  • 先読み量を調整する場合は、GKE バージョン 1.29.2-gke.1217000 以降を使用します。
  • マルチゾーン動的プロビジョニング機能を使用する場合は、GKE バージョン 1.30.2-gke.1394000 以降を使用します。
  • Hyperdisk ML は、特定のノードタイプとゾーンでのみサポートされます。詳細については、Compute Engine ドキュメントの Hyperdisk ML についてをご覧ください。

モデルへのアクセス権を取得する

GKE にデプロイするために Gemma モデルへのアクセス権を取得するには、まずライセンス同意契約に署名してから、Hugging Face のアクセス トークンを生成する必要があります。

Gemma を使用するには同意契約に署名する必要があります。手順は次のとおりです。

  1. Kaggle.com のモデルの同意ページにアクセスします。
  2. Hugging Face アカウントを使用して同意を確認します。
  3. モデルの規約に同意します。

アクセス トークンを生成する

Hugging Face からモデルにアクセスするには、Hugging Face トークンが必要です。

トークンをまだ生成していない場合は、次の手順に沿って生成します。

  1. [Your Profile] > [Settings] > [Access Tokens] の順にクリックします。
  2. [New Token] を選択します。
  3. 任意の名前と、少なくとも Read ロールを指定します。
  4. [Generate a token] を選択します。
  5. トークンをクリップボードにコピーします。

GKE クラスタを作成する

GKE Autopilot クラスタまたは GKE Standard クラスタの GPU で LLM を提供できます。フルマネージドの Kubernetes エクスペリエンスを実現するには、Autopilot クラスタを使用することをおすすめします。ワークロードに最適な GKE の運用モードを選択するには、GKE の運用モードを選択するをご覧ください。

Autopilot

  1. Cloud Shell で、次のコマンドを実行します。

    gcloud container clusters create-auto hdml-gpu-l4 \
      --project=PROJECT \
      --location=CONTROL_PLANE_LOCATION \
      --release-channel=rapid \
      --cluster-version=1.30.2-gke.1394000
    

    次の値を置き換えます。

    • PROJECT: Google Cloud プロジェクト ID。
    • CONTROL_PLANE_LOCATION: クラスタのコントロール プレーンの Compute Engine のリージョン。使用するアクセラレータ タイプをサポートするリージョンを指定します(たとえば、L4 GPU の場合は us-east4)。

    GKE は、デプロイされたワークロードからのリクエストに応じた CPU ノードと GPU ノードを持つ Autopilot クラスタを作成します。

  2. クラスタと通信を行うように kubectl を構成します。

    gcloud container clusters get-credentials hdml-gpu-l4 \
      --location=CONTROL_PLANE_LOCATION
    

Standard

  1. Cloud Shell で次のコマンドを実行して、Standard クラスタとノードプールを作成します。

    gcloud container clusters create hdml-gpu-l4 \
        --location=CONTROL_PLANE_LOCATION \
        --num-nodes=1 \
        --machine-type=c3-standard-44 \
        --release-channel=rapid \
        --cluster-version=CLUSTER_VERSION \
        --node-locations=ZONES \
        --project=PROJECT
    
    gcloud container node-pools create gpupool \
        --accelerator type=nvidia-l4,count=2,gpu-driver-version=latest \
        --location=CONTROL_PLANE_LOCATION \
        --project=PROJECT \
        --node-locations=ZONES \
        --cluster=hdml-gpu-l4 \
        --machine-type=g2-standard-24 \
        --num-nodes=2
    

    次の値を置き換えます。

    • CLUSTER_VERSION: GKE クラスタのバージョン(1.30.2-gke.1394000 など)。
    • CONTROL_PLANE_LOCATION: クラスタのコントロール プレーンの Compute Engine のロケーション。リージョン クラスタの場合は、使用するアクセラレータをサポートするゾーンを含むリージョンを指定します。ゾーンクラスタの場合は、使用するアクセラレータをサポートするゾーンを指定します。特定のアクセラレータが利用可能なロケーションを確認するには、各リージョンとゾーンで利用可能な GPU をご覧ください。
    • ZONES: ノードが作成されるゾーン。クラスタに必要な数だけゾーンを指定できます。すべてのゾーンは、--location フラグで指定された、クラスタのコントロール プレーンと同じリージョンに存在する必要があります。ゾーンクラスタの場合、--node-locations にはクラスタのプライマリ ゾーンが含まれている必要があります。
    • PROJECT: Google Cloud プロジェクト ID。

    クラスタの作成には数分かかることもあります。

  2. クラスタと通信を行うように kubectl を構成します。

    gcloud container clusters get-credentials hdml-gpu-l4
    

Persistent Disk ディスク イメージにデータを事前キャッシュ保存する

Hyperdisk ML を使用するには、ディスク イメージにデータを事前にキャッシュに保存し、GKE 内のワークロードによる読み取りアクセス用に Hyperdisk ML ボリュームを作成します。このアプローチ(データ ハイドレーションとも呼ばれます)により、ワークロードで必要なときにデータを使用できます。

次の手順では、Kubernetes Job を使用して、Hugging Face リポジトリなどのソースから Hyperdisk ML ボリュームにデータを手動でコピーする方法について説明します。

データがすでに Cloud Storage バケットにある場合は、Hyperdisk ML を使用して、Cloud Storage から Hyperdisk ML へのデータ転送を自動化できます。これにより、以降のセクションで説明する手動のジョブ作成手順が不要になります。

Hyperdisk ML をサポートする StorageClass を作成する

  1. 次の StorageClass マニフェストを hyperdisk-ml.yaml という名前のファイルに保存します。

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
        name: hyperdisk-ml
    parameters:
        type: hyperdisk-ml
        provisioned-throughput-on-create: "2400Mi"
    provisioner: pd.csi.storage.gke.io
    allowVolumeExpansion: false
    reclaimPolicy: Delete
    volumeBindingMode: WaitForFirstConsumer
    mountOptions:
      - read_ahead_kb=4096
    
  2. 次のコマンドを実行して StorageClass を作成します。

    kubectl create -f hyperdisk-ml.yaml
    

ReadWriteOnce(RWO)PersistentVolumeClaim を作成する

  1. 次の PersistentVolumeClaim マニフェストを producer-pvc.yaml という名前のファイルに保存します。先ほど作成した StorageClass を使用します。この PVC は、Kubernetes Job がモデルデータを永続ディスクにダウンロードするために使用されるため、ReadWriteOnce アクセスモードを使用します。これには書き込みアクセスが必要です。ReadWriteMany アクセスモードは Google Cloud Hyperdisk でサポートされていません。データを保存できるだけの十分な容量がディスクにあることを確認します。

    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: producer-pvc
    spec:
      storageClassName: hyperdisk-ml
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 300Gi
    
  2. 次のコマンドを実行して PersistentVolumeClaim を作成します。

    kubectl create -f producer-pvc.yaml
    

マウントされた Google Cloud Hyperdisk ボリュームにデータを入力する Kubernetes Job を作成する

このセクションでは、ディスクをプロビジョニングし、Gemma 7B 指示チューニング型モデルを Hugging Face からマウントされた Google Cloud Hyperdisk ボリュームにダウンロードする Kubernetes Job を作成する例を示します。

  1. このガイドのサンプルで使用する Gemma LLM にアクセスするには、Hugging Face トークンを含む Kubernetes Secret を作成します。

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=HF_TOKEN\
        --dry-run=client -o yaml | kubectl apply -f -
    

    HF_TOKEN は、先ほど生成した Hugging Face トークンに置き換えます。

  2. 次のマニフェストの例を producer-job.yaml として保存します。

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: producer-job
    spec:
      template:
        spec:
          affinity:
            # Node affinity ensures that Pods are scheduled on the nodes that support Hyperdisk ML.
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  # Specifies the Performance compute class. For more information,
                  # see https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-compute-classes#when-to-use.
                  - key: cloud.google.com/compute-class
                    operator: In
                    values:
                    - "Performance"
                - matchExpressions:
                  - key: cloud.google.com/machine-family
                    operator: In
                    values:
                    - "c3"
                - matchExpressions:
                  # Restricts Pod scheduling to a specific zone because Hyperdisk ML disks
                  # are a zonal resource.
                  - key: topology.kubernetes.io/zone
                    operator: In
                    values:
                    - "ZONE"
          containers:
          - name: copy
            resources:
              requests:
                cpu: "32"
              limits:
                cpu: "32"
             # The image used to download models from Hugging Face.
            image: huggingface/downloader:0.17.3
            command: [ "huggingface-cli" ]
            args:
            - download
            # The Hugging Face model to download.
            - google/gemma-1.1-7b-it
            # Destination directory within the container.
            - --local-dir=/data/gemma-7b
            - --local-dir-use-symlinks=False
            env:
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
            volumeMounts:
              # Mount path for the PersistentVolume.
              - mountPath: "/data"
                name: volume
          # Prevents Pod restarts on scheduling failures. The Job will create new Pods
          # for retries, up to the specified "backoffLimit".
          restartPolicy: Never
          volumes:
            - name: volume
              persistentVolumeClaim:
                # References the Hyperdisk ML PVC created earlier.
                claimName: producer-pvc
      # Runs only one Pod at any given time.
      parallelism: 1
      # After the Pod runs successfully, the Job is complete.
      completions: 1
      # Max retries on Pod failure.
      backoffLimit: 4
    

    ZONE は、Hyperdisk を作成するコンピューティング ゾーンに置き換えます。Deployment の例で使用する場合、G2 マシンの容量があるゾーンであることを確認してください。

  3. 次のコマンドを実行して Job を作成します。

    kubectl apply -f producer-job.yaml
    

    Job が Persistent Disk ボリュームへのデータのコピーを完了するまでに数分かかることがあります。Job のプロビジョニングが完了すると、ステータスは [完了] になります。

  4. Job のステータスの進行状況を確認するには、次のコマンドを実行します。

    kubectl get job producer-job
    
  5. Job が完了したら、次のコマンドを実行して Job をクリーンアップできます。

    kubectl delete job producer-job
    

既存の Google Cloud Hyperdisk から ReadOnlyMany Hyperdisk ML ボリュームを作成する

このセクションでは、既存の Google Cloud Hyperdisk ボリュームから ReadOnlyMany(ROM)PersistentVolume と PersistentVolumeClaim のペアを作成する手順について説明します。詳細については、既存の永続ディスクを PersistentVolume として使用するをご覧ください。

  1. GKE バージョン 1.30.2-gke.1394000 以降では、GKE は READ_WRITE_SINGLE Google Cloud Hyperdisk ボリュームのアクセスモードを READ_ONLY_MANY に自動的に変換します。

    以前のバージョンの GKE で既存の Google Cloud Hyperdisk ボリュームを使用している場合は、次のコマンドを実行してアクセスモードを手動で変更する必要があります。

    gcloud compute disks update HDML_DISK_NAME \
        --zone=ZONE \
        --access-mode=READ_ONLY_MANY
    

    次の値を置き換えます。

  2. 先ほど入力したディスクを参照して、PersistentVolume と PersistentVolumeClaim のペアを作成します。

    1. 次のマニフェストを hdml-static-pv.yaml として保存します。

      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: hdml-static-pv
      spec:
        storageClassName: "hyperdisk-ml"
        capacity:
          storage: 300Gi
        # The "ReadOnlyMany" access mode allows the volume to be mounted by multiple
        # nodes for read-only access.
        accessModes:
          - ReadOnlyMany
        # ClaimRef links this PersistentVolume to a PersistentVolumeClaim.
        claimRef:
          namespace: default
          name: hdml-static-pvc
        csi:
          driver: pd.csi.storage.gke.io
          # The unique identifier of the Compute Engine disk resource backing
          # this volume.
          volumeHandle: projects/PROJECT/zones/ZONE/disks/DISK_NAME
          fsType: ext4
          readOnly: true
        # Node affinity ensures that Pod is scheduled in a zone where the volume
        # is replicated.
        nodeAffinity:
          required:
            nodeSelectorTerms:
            - matchExpressions:
              - key: topology.gke.io/zone
                operator: In
                values:
                - ZONE
      ---
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        namespace: default
        name: hdml-static-pvc
      spec:
        storageClassName: "hyperdisk-ml"
        volumeName: hdml-static-pv
        accessModes:
        - ReadOnlyMany
        resources:
          requests:
            storage: 300Gi
      

      次の値を置き換えます。

      • PROJECT: GKE クラスタが作成されるプロジェクト。
      • ZONE: 既存の Google Cloud Hyperdisk ボリュームが作成されるゾーン。
      • DISK_NAME: 既存の Google Cloud Hyperdisk ボリュームの名前。
    2. 次のコマンドを実行して、PersistentVolume リソースと PersistentVolumeClaim リソースを作成します。

      kubectl apply -f hdml-static-pv.yaml
      

VolumeSnapshot からマルチゾーン ReadOnlyMany Hyperdisk ML ボリュームを作成する

このセクションでは、ReadOnlyMany アクセスモードでマルチゾーン Hyperdisk ML ボリュームを作成する手順について説明します。VolumeSnapshot は、既存の Persistent Disk ディスク イメージに使用します。詳細については、ボリューム スナップショットを使用して Persistent Disk ストレージをバックアップするをご覧ください。

マルチゾーン Hyperdisk ML ボリュームを作成する手順は次のとおりです。

ディスクの VolumeSnapshot を作成する

  1. 次のマニフェストを disk-image-vsc.yaml という名前のファイルとして保存します。

    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshotClass
    metadata:
      name: disk-image-vsc
    driver: pd.csi.storage.gke.io
    # The snapshot will be deleted when the "VolumeSnapshot" object is deleted.
    deletionPolicy: Delete
    parameters:
      snapshot-type: images
    
  2. 次のコマンドを実行して VolumeSnapshotClass を作成します。

    kubectl apply -f disk-image-vsc.yaml
    
  3. 次のマニフェストを my-snapshot.yaml という名前のファイルとして保存します。ReadWriteOnce(RWO)PersistentVolumeClaim を作成するで先ほど作成した PersistentVolumeClaim を参照します。

    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshot
    metadata:
      name: my-snapshot
    spec:
      volumeSnapshotClassName: disk-image-vsc
      source:
        # The name of the PersistentVolumeClaim to snapshot.
        persistentVolumeClaimName: producer-pvc
    
  4. 次のコマンドを実行して VolumeSnapshot を作成します。

    kubectl apply -f my-snapshot.yaml
    
  5. VolumeSnapshot が「Ready」とマークされたら、次のコマンドを実行して Hyperdisk ML ボリュームを作成します。

    kubectl wait --for=jsonpath='{.status.readyToUse}'=true \
        --timeout=300s volumesnapshot my-snapshot
    

マルチゾーン StorageClass を作成する

データのコピーに複数のゾーンからアクセスできるようにするには、StorageClass に enable-multi-zone-provisioning パラメータを指定します。これにより、allowedTopologies フィールドで指定したゾーンにディスクが作成されます。

StorageClass を作成する手順は次のとおりです。

  1. 次のマニフェストを hyperdisk-ml-multi-zone.yaml という名前のファイルとして保存します。

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: hyperdisk-ml-multi-zone
    parameters:
      type: hyperdisk-ml
      provisioned-throughput-on-create: "4800Mi"
      enable-multi-zone-provisioning: "true"
    provisioner: pd.csi.storage.gke.io
    allowVolumeExpansion: false
    reclaimPolicy: Delete
    volumeBindingMode: Immediate
    allowedTopologies:
    - matchLabelExpressions:
      - key: topology.gke.io/zone
        values:
        - ZONE_1
        - ZONE_2
    mountOptions:
      - read_ahead_kb=8192
    

    ZONE_1ZONE_2、...、ZONE_N は、ストレージにアクセスできるゾーンに置き換えます。

    この例では、volumeBindingMode を Immediate に設定します。これにより、GKE は PersistentVolumeClaim を参照するコンシューマーの前に PersistentVolumeClaim をプロビジョニングできます。

  2. 次のコマンドを実行して StorageClass を作成します。

    kubectl apply -f hyperdisk-ml-multi-zone.yaml
    

マルチゾーン StorageClass を使用する PersistentVolumeClaim を作成する

次のステップでは、StorageClass を参照する PersistentVolumeClaim を作成します。

GKE は、指定されたディスク イメージのコンテンツを使用して、スナップショットで指定された各ゾーンに Hyperdisk ML ボリュームを自動的にプロビジョニングします。

PersistentVolumeClaim を作成する手順は次のとおりです。

  1. 次のマニフェストを hdml-consumer-pvc.yaml という名前のファイルとして保存します。

    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: hdml-consumer-pvc
    spec:
      # Specifies that the new PersistentVolumeClaim should be provisioned from the
      # contents of the volume snapshot named "my-snapshot".
      dataSource:
        name: my-snapshot
        kind: VolumeSnapshot
        apiGroup: snapshot.storage.k8s.io
      accessModes:
      - ReadOnlyMany
      storageClassName: hyperdisk-ml-multi-zone
      resources:
        requests:
          storage: 300Gi
    
  2. 次のコマンドを実行して PersistentVolumeClaim を作成します。

    kubectl apply -f hdml-consumer-pvc.yaml
    

Hyperdisk ML ボリュームを消費する Deployment を作成する

PersistentVolume で Pod を使用する場合は、ワークロード コントローラ(DeploymentStatefulSet など)を使用することをおすすめします。

Deployment で ReadOnlyMany モードの既存の PersistentVolume を使用する場合は、複数のリーダーで永続ディスクを使用するをご覧ください。

Deployment を作成してテストする手順は次のとおりです。

  1. 次のマニフェストの例を vllm-gemma-deployment として保存します。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: vllm-gemma-deployment
    spec:
      replicas: 2
      selector:
        # Labels used to select the Pods managed by this Deployment.
        matchLabels:
          app: gemma-server
      template:
        metadata:
          labels:
            app: gemma-server
            # Labels for AI/GKE integration.
            ai.gke.io/model: gemma-7b
            ai.gke.io/inference-server: vllm
        spec:
          affinity:
            # Node affinity ensures Pods run on nodes with L4 GPUs.
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: cloud.google.com/gke-accelerator
                    operator: In
                    values:
                    - nvidia-l4
            # Pod anti-affinity prefers scheduling Pods in different zones for
            # higher availability.
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - gemma-server
                  topologyKey: topology.kubernetes.io/zone
          containers:
          - name: inference-server
            # The container image for the vLLM inference server.
            image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:latest
            resources:
              requests:
                cpu: "2"
                memory: "25Gi"
                ephemeral-storage: "25Gi"
                nvidia.com/gpu: 2
              limits:
                cpu: "2"
                memory: "25Gi"
                ephemeral-storage: "25Gi"
                nvidia.com/gpu: 2
            # Command to run the vLLM API server.
            command: ["python3", "-m", "vllm.entrypoints.api_server"]
            args:
            # Specifies the model to load, using an environment variable.
            - --model=$(MODEL_ID)
            - --tensor-parallel-size=2
            env:
            # Environment variable to define the model path.
            - name: MODEL_ID
              value: /models/gemma-7b
            volumeMounts:
            - mountPath: /dev/shm
              name: dshm
            # Mount point for the Hyperdisk ML volume containing the model.
            - mountPath: /models
              name: gemma-7b
          volumes:
          - name: dshm
            emptyDir:
                medium: Memory
          - name: gemma-7b
            # References the PersistentVolumeClaim for the Hyperdisk ML volume.
            persistentVolumeClaim:
              claimName: CLAIM_NAME
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: llm-service
    spec:
      # Selects Pods with the label "app: gemma-server".
      selector:
        app: gemma-server
      # The "ClusterIP" field makes the Service reachable only within the cluster.
      type: ClusterIP
      ports:
        - protocol: TCP
          port: 8000
          targetPort: 8000
    

    CLAIM_NAME は次のいずれかの値に置き換えます。

    • hdml-static-pvc: 既存の Google Cloud Hyperdisk の Hyperdisk ML ボリュームを使用している場合。
    • hdml-consumer-pvc: VolumeSnapshot ディスク イメージの Hyperdisk ML ボリュームを使用している場合。
  2. 次のコマンドを実行して、推論サーバーが使用可能になるまで待ちます。

    kubectl wait --for=condition=Available --timeout=700s deployment/vllm-gemma-deployment
    
  3. vLLM サーバーが稼働していることを確認する手順は次のとおりです。

    1. 次のコマンドを実行して、モデルへのポート転送を設定します。

      kubectl port-forward service/llm-service 8000:8000
      
    2. curl コマンドを実行して、モデルにリクエストを送信します。

      USER_PROMPT="I'm new to coding. If you could only recommend one programming language to start with, what would it be and why?"
      
      curl -X POST http://localhost:8000/generate \
      -H "Content-Type: application/json" \
      -d @- <<EOF
      {
          "prompt": "<start_of_turn>user\n${USER_PROMPT}<end_of_turn>\n",
          "temperature": 0.90,
          "top_p": 1.0,
          "max_tokens": 128
      }
      EOF
      

    次の出力には、モデルのレスポンスの例が表示されています。

    {"predictions":["Prompt:\n<start_of_turn>user\nI'm new to coding. If you could only recommend one programming language to start with, what would it be and why?<end_of_turn>\nOutput:\nPython is often recommended for beginners due to its clear, readable syntax, simple data types, and extensive libraries.\n\n**Reasons why Python is a great language for beginners:**\n\n* **Easy to read:** Python's syntax is straightforward and uses natural language conventions, making it easier for beginners to understand the code.\n* **Simple data types:** Python has basic data types like integers, strings, and lists that are easy to grasp and manipulate.\n* **Extensive libraries:** Python has a vast collection of well-documented libraries covering various tasks, allowing beginners to build projects without reinventing the wheel.\n* **Large supportive community:**"]}
    

先読み量をチューニングする

シーケンシャル I/O を実行するワークロードがある場合は、先読み量をチューニングすると効果的です。これは通常、AI/ML モデルの重みをメモリに読み込む必要がある推論ワークロードまたはトレーニング ワークロードに適用されます。通常、シーケンシャル I/O を使用するほとんどのワークロードでは、1,024 KB 以上の先読み量でパフォーマンスが向上します。

新しいボリュームの先読み量を調整する

このオプションを指定するには、StorageClass の mountOptions フィールドに read_ahead_kb を追加します。次の例は、先読み量を 4,096 KB に調整する方法を示しています。これは、hyperdisk-ml StorageClass を使用して作成され、動的にプロビジョニングされた新しい PersistentVolume に適用されます。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: hyperdisk-ml
parameters:
    type: hyperdisk-ml
provisioner: pd.csi.storage.gke.io
allowVolumeExpansion: false
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
mountOptions:
  - read_ahead_kb=4096

既存のボリュームの先読み量を調整する

静的にプロビジョニングされたボリュームまたは既存の PersistentVolume の場合は、spec.mountOptions フィールドに read_ahead_kb を追加して、このオプションを指定できます。次の例は、先読み量を 4,096 KB に調整する方法を示しています。

apiVersion: v1
kind: PersistentVolume
  name: DISK_NAME
spec:
  accessModes:
  - ReadOnlyMany
  capacity:
    storage: 300Gi
  csi:
    driver: pd.csi.storage.gke.io
    fsType: ext4
    readOnly: true
    # The unique identifier of the Compute Engine disk resource backing this volume.
    volumeHandle: projects/PROJECT/zones/ZONE/disks/DISK_NAME
  # Node affinity ensures that Pods are scheduled in the zone where the volume exists.
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.gke.io/zone
          operator: In
          values:
          - ZONE
  storageClassName: hyperdisk-ml
  mountOptions:
  - read_ahead_kb=4096

次の値を置き換えます。

  • DISK_NAME: 既存の Google Cloud Hyperdisk ボリュームの名前。
  • ZONE: 既存の Google Cloud Hyperdisk ボリュームが作成されるゾーン。

Hyperdisk ML ボリュームのパフォーマンスをテストしてベンチマークする

このセクションでは、Flexible I/O Tester(FIO)を使用して、既存のデータを読み取る Hyperdisk ML ボリュームのパフォーマンスをベンチマークする方法について説明します。これらの指標を使用して、特定のワークロードと構成のボリュームのパフォーマンスを評価できます。

  1. 次のマニフェストの例を benchmark-job.yaml として保存します。

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: benchmark-job
    spec:
      template:  # Template for the Pods the Job will create.
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: cloud.google.com/compute-class
                    operator: In
                    values:
                    - "Performance"
                - matchExpressions:
                  - key: cloud.google.com/machine-family
                    operator: In
                    values:
                    - "c3"
    
          containers:
          - name: fio
            resources:
              requests:
                cpu: "32"
            image: litmuschaos/fio
            args:
            - fio
            # Specifies the files to use for the benchmark. Multiple files can be separated by colons.
            - --filename
            - /models/gemma-7b/model-00001-of-00004.safetensors:/models/gemma-7b/model-00002-of-00004.safetensors:/models/gemma-7b/model-00003-of-00004.safetensors:/models/gemma-7b/model-00004-of-00004.safetensors:/models/gemma-7b/model-00004-of-00004.safetensors
            # Use non-buffered I/O.
            - --direct=1
            # Set the I/O pattern to read.
            - --rw=read
            # Open files in read-only mode.
            - --readonly
            # Block size for I/O operations.
            - --bs=4096k
            # I/O engine to use.
            - --ioengine=libaio
            # Number of I/O units to keep in flight against each file.
            - --iodepth=8
            # Duration of the test in seconds.
            - --runtime=60
            # Number of jobs to run.
            - --numjobs=1
            # Name of the job.
            - --name=read_benchmark
            volumeMounts:
            - mountPath: "/models"
              name: volume
          restartPolicy: Never
          volumes:
          - name: volume
            persistentVolumeClaim:
              claimName: hdml-static-pvc
      parallelism: 1
      completions: 1
      backoffLimit: 1
    

    CLAIM_NAME は、PersistentVolumeClaim の名前に置き換えます(hdml-static-pvc など)。

  2. 次のコマンドを実行して、Job をモニタリングします。

    kubectl apply -f benchmark-job.yaml.
    
  3. kubectl ログを使用して、fio ツールの出力を表示します。

    kubectl logs benchmark-job-nrk88 -f
    

    出力は次のようになります。

    read_benchmark: (g=0): rw=read, bs=4M-4M/4M-4M/4M-4M, ioengine=libaio, iodepth=8
    fio-2.2.10
    Starting 1 process
    
    read_benchmark: (groupid=0, jobs=1): err= 0: pid=32: Fri Jul 12 21:29:32 2024
    read : io=18300MB, bw=2407.3MB/s, iops=601, runt=  7602msec
        slat (usec): min=86, max=1614, avg=111.17, stdev=64.46
        clat (msec): min=2, max=33, avg=13.17, stdev= 1.08
        lat (msec): min=2, max=33, avg=13.28, stdev= 1.06
        clat percentiles (usec):
        |  1.00th=[11072],  5.00th=[12352], 10.00th=[12608], 20.00th=[12736],
        | 30.00th=[12992], 40.00th=[13120], 50.00th=[13248], 60.00th=[13376],
        | 70.00th=[13504], 80.00th=[13632], 90.00th=[13888], 95.00th=[14016],
        | 99.00th=[14400], 99.50th=[15296], 99.90th=[22144], 99.95th=[25728],
        | 99.99th=[33024]
        bw (MB  /s): min= 2395, max= 2514, per=100.00%, avg=2409.79, stdev=29.34
        lat (msec) : 4=0.39%, 10=0.31%, 20=99.15%, 50=0.15%
    cpu          : usr=0.28%, sys=8.08%, ctx=4555, majf=0, minf=8203
    IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=99.8%, 16=0.0%, 32=0.0%, >=64=0.0%
        submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
        complete  : 0=0.0%, 4=100.0%, 8=0.1%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
        issued    : total=r=4575/w=0/d=0, short=r=0/w=0/d=0, drop=r=0/w=0/d=0
        latency   : target=0, window=0, percentile=100.00%, depth=8
    
    Run status group 0 (all jobs):
    READ: io=18300MB, aggrb=2407.3MB/s, minb=2407.3MB/s, maxb=2407.3MB/s, mint=7602msec, maxt=7602msec
    
    Disk stats (read/write):
    nvme0n2: ios=71239/0, merge=0/0, ticks=868737/0, in_queue=868737, util=98.72%
    

Hyperdisk ML ボリュームのスループットまたは IOPS をモニタリングする

Hyperdisk ML ボリュームのプロビジョニングされたパフォーマンスをモニタリングするには、Compute Engine ドキュメントのプロビジョニングされた IOPS とスループットを分析するをご覧ください。

既存の Hyperdisk ML ボリュームのプロビジョニングされたスループットまたは IOPS を更新する場合、または StorageClass で指定できる追加の Google Cloud Hyperdisk パラメータについては、Google Cloud Hyperdisk を使用してストレージのパフォーマンスをスケーリングするをご覧ください。

トラブルシューティング

このセクションでは、GKE の Hyperdisk ML ボリュームに関する問題のトラブルシューティングについて説明します。

ディスクのアクセスモードを更新できない

Hyperdisk ML ボリュームがすでにノードで使用され、ReadWriteOnce アクセスモードでノードにアタッチされている場合、次のエラーが発生します。

AttachVolume.Attach failed for volume ... Failed to update access mode:
failed to set access mode for zonal volume ...
'Access mode cannot be updated when the disk is attached to instance(s).'., invalidResourceUsage

GKE は、ReadOnlyMany アクセスモードの PersistentVolume で使用されている場合、Hyperdisk ML ボリュームの accessMode を READ_WRITE_SINGLE から READ_ONLY_MANY に自動的に更新します。この更新は、ディスクが新しいノードにアタッチされたときに行われます。

この問題を解決するには、ReadWriteOnce モードの PersistentVolume を使用してディスクを参照しているすべての Pod を削除します。ディスクが切断されるのを待ってから、ReadOnlyMany モードで PersistentVolume を使用するワークロードを再作成します。

ディスクを READ_WRITE モードでアタッチできない

次のエラーは、GKE が ReadWriteOnce アクセスモードを使用して、READ_ONLY_MANY アクセスモードの Hyperdisk ML ボリュームを GKE ノードにアタッチしようとしたことを示しています。

AttachVolume.Attach failed for volume ...
Failed to Attach: failed cloud service attach disk call ...
The disk cannot be attached with READ_WRITE mode., badRequest

GKE は、ReadOnlyMany アクセスモードの PersistentVolume で使用されている場合、Hyperdisk ML ボリュームの accessMode を READ_WRITE_SINGLE から READ_ONLY_MANY に自動的に更新します。ただし、GKE がアクセスモードを READ_ONLY_MANY から READ_WRITE_SINGLE に自動的に更新することはありません。これは、マルチゾーン ディスク間でコンテンツが分散する可能性があるため、マルチゾーン ディスクに誤って書き込まれないようにするための安全メカニズムです。

この問題を解決するには、更新されたコンテンツが必要な場合は、Persistent Disk ディスク イメージにデータを事前キャッシュに保存するワークフローに沿って操作することをおすすめします。Hyperdisk ML ボリュームのアクセスモードやその他の設定を細かく制御する必要がある場合は、 Google Cloud Hyperdisk ボリュームの設定を変更するをご覧ください。

割り当て超過 - スループット割り当てが不足している

次のエラーは、ディスクのプロビジョニング時に Hyperdisk ML スループット割り当てが不十分だったことを示します。

failed to provision volume with StorageClass ... failed (QUOTA_EXCEEDED): Quota 'HDML_TOTAL_THROUGHPUT' exceeded

この問題を解決するには、ディスク割り当てで、Hyperdisk 割り当ての詳細と、プロジェクトでディスク割り当てを増やす方法について確認してください。

その他のトラブルシューティングのガイダンスについては、Google Cloud Hyperdisk でストレージ パフォーマンスをスケーリングするをご覧ください。

次のステップ