raw ブロック ストレージとエフェメラル ストレージに混合ローカル SSD をプロビジョニングして使用する

このドキュメントでは、Google Kubernetes Engine(GKE)でローカル SSD をプロビジョニングしてパーティショニングし、同じノードで標準のエフェメラル ストレージ ファイル システムとフォーマットされていない raw ブロック ストレージの両方を必要とするワークロードをサポートする方法について説明します。

この構成は、ステートフル アプリケーションと高度に最適化されたデータベースに使用します。これらのワークロードでは、パフォーマンスを向上させるためにディスクへの直接アクセスが必要になることがよくありますが、サイドカー、ログ、コンテナ キャッシュ用の標準のエフェメラル ファイル ストレージも必要です。ローカル SSD の混合容量を使用すると、クラスタ管理のオーバーヘッドを削減し、ハードウェアの使用率を最大化できます。これは、生のブロック インフラストラクチャに専用のノードやディスク全体を割り当てる必要がないためです。

ローカル SSD の容量が混在するクラスタまたはノードプールを作成すると、GKE は物理的に接続されたすべてのローカル SSD を次のようにパーティショニングします。

  • エフェメラル ストレージ: リクエストされたエフェメラル容量は、ローカル SSD 間で均等に分割され、ノードの標準エフェメラル ファイル ストレージを提供するために結合されます。エフェメラル容量が複数のローカル SSD にパーティション分割されている場合、ストレージは RAID-0 アレイに結合されます。エフェメラル ストレージには、標準の emptyDir ボリュームを使用してアクセスできます。
  • 未加工のブロック ストレージ: 各ディスクの物理容量の残りは、未フォーマットの未加工のブロック デバイスとしてノードに提示されます。

このドキュメントは、特殊なワークロード向けに GKE のハードウェア使用率とストレージ パフォーマンスを最適化するプラットフォーム管理者、オペレーター、DevOps を対象としています。

考慮事項

Compute Engine のローカル SSD の制限に加えて、混合ローカル SSD モードを構成する前に、次の GKE 固有の考慮事項を確認してください。

  • Autopilot の互換性: GKE Autopilot クラスタは、未加工ブロックとエフェメラル ストレージの混在をサポートしていません。Autopilot で標準エフェメラル ストレージにローカル SSD を使用する方法については、Autopilot クラスタのローカル SSD をご覧ください。
  • カスタム ComputeClass: カスタム ComputeClass は、混合ローカル SSD 構成をサポートしていません。
  • オペレーティング システム: 混合ローカル SSD モードは、Linux ノードイメージ(Container-Optimized OS と Ubuntu)でのみサポートされています。Windows Server ノードプールはサポートされていません。
  • メンテナンス イベント: ストレージ最適化 Z3 または Z4D VM の raw ブロック ストレージとしてローカル SSD を使用することはおすすめしません。これらの VM のデータは、メンテナンス イベントで失われる可能性があります。詳細については、ローカル SSD を使用する raw ブロック ストレージをプロビジョニングして使用するをご覧ください。
  • 共有 I/O: エフェメラル RAID-0 アレイとターゲットの未加工ブロック パーティションの両方が、基盤となる同じ物理 SSD に依存しているため、1 つの論理パーティションで集中的なオペレーションを行うと、もう一方のスループットに影響する可能性があります。最大スループットの上限については、ローカル SSD のパフォーマンスをご覧ください。
  • 料金: 混合モードを使用してローカル SSD 容量をパーティショニングする場合、未加工のブロック ストレージと比較してエフェメラル ストレージに割り当てられる容量に関係なく、ノードにアタッチされた基盤となる物理ローカル SSD の合計容量に対して料金が発生します。詳細については、ローカル SSD の料金をご覧ください。
  • 手動構成: ローカル SSD を使用する raw ブロック ストレージを使用するには、デバイスを手動で構成する必要があります。GKE は raw ブロック パーティションをフォーマットまたはマウントしないためです。また、ノードの容量を管理し、同じ場所に配置されたワークロードによる中断を回避する必要があります。ローカル PersistentVolume オブジェクトを使用すると、統合された Pod スケジューリングが提供されます。ワークロードで Pod のライフサイクル全体でデータを保持する必要がない場合、または特別なパフォーマンス要件がない場合は、代わりに完全エフェメラル ローカル SSD 構成を使用します。完全なエフェメラル構成は、ほとんどのユースケースに適しています。
  • ノードのメモリスワップ: ブートディスク プロファイルまたはエフェメラル ローカル SSD プロファイルを使用して、混合ローカル SSD 容量をノードのメモリスワップと組み合わせることができます。専用のローカル SSD プロファイルは、ローカル SSD の容量が混在している場合はサポートされません。詳細については、ローカル SSD の容量が混在するノードのメモリスワップをご覧ください。

始める前に

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

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API を有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化します。gcloud CLI をインストール済みの場合は、gcloud components update コマンドを実行して最新のバージョンを取得します。以前のバージョンの gcloud CLI では、このドキュメントのコマンドを実行できない場合があります。

この機能を使用するには、GKE ノードでバージョン 1.37.0-gke.3570000 以降を実行する必要があります。

必要なロール

ローカル SSD 容量が混在するクラスタとノードプールを作成するために必要な権限を取得するには、プロジェクトに対する Kubernetes Engine 管理者 (roles/container.admin)IAM ロールを付与するよう管理者に依頼してください。ロールの付与については、プロジェクト、フォルダ、組織に対するアクセス権の管理をご覧ください。

必要な権限は、カスタムロールや他の事前定義ロールから取得することもできます。

混合ローカル SSD ストレージを使用するクラスタまたはノードプールを作成する

エフェメラル容量を割り当て、未加工のブロック パーティションを予約するには、--ephemeral-storage-local-ssd フラグの capacity プロパティを使用します。

--ephemeral-storage-local-ssd フラグを使用して capacity サブフィールドを指定して、クラスタまたはノードプールを作成します。

マシンタイプで構成可能な数のローカル SSD が許可されている場合は、count サブフィールドも指定する必要があります。マシンタイプに固定数のローカル SSD(-lssd タイプなど)がある場合は、GKE が VM シェイプに基づいてディスク数を自動的に計算するため、count サブフィールドを省略できます。

ローカル SSD ストレージが混在するクラスタを作成する

新しいクラスタを作成するには、gcloud container clusters create コマンドを使用します。これらの設定は、デフォルトのノードプールにのみ適用されます。後続のノードプールに混合ローカル SSD ストレージが必要な場合は、ノードプールの作成時に --ephemeral-storage-local-ssd フラグを指定します。アタッチできるディスクの最大数は、マシンタイプとリージョンによって異なります。

クラスタのバージョンが 1.37.0-gke.3570000 より前の場合は、次のクラスタ作成コマンドに次のいずれかのフラグを追加します。

  • --cluster-version=<var>VERSION</var> フラグ。<var>VERSION</var> が 1.37.0-gke.3570000 以降に設定されている。
  • --release-channel=rapid フラグ。

ローカル SSD の数を構成できるマシンタイプの場合は、次のコマンドを実行します。

gcloud container clusters create CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --ephemeral-storage-local-ssd count=NUMBER_OF_DISKS,capacity=CAPACITY

ローカル SSD の数が固定されているマシンタイプの場合は、次のコマンドを実行します。

gcloud container clusters create CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --ephemeral-storage-local-ssd capacity=CAPACITY

次のように置き換えます。

  • CLUSTER_NAME: 作成するクラスタの名前。
  • MACHINE_TYPE: 使用するマシンタイプ。サポートされているマシンタイプの一覧については、マシンシリーズのサポートをご覧ください。共有コア マシンタイプはローカル SSD をサポートしていないため、デフォルトの e2-medium マシンタイプではローカル SSD を使用できません。
  • NUMBER_OF_DISKS: 構成可能なローカル SSD 数を持つマシンタイプの場合、各ノードでプロビジョニングするローカル SSD ディスクの数。
  • CAPACITY: エフェメラル ストレージ用にフォーマットするローカル SSD の合計容量。この値は 0 より大きく、物理ローカル SSD の合計容量(ディスク数 × ディスクあたりのサイズ、例: 200GB)より小さくする必要があります。次の点にご注意ください。

    • この値をクラスタの作成後に変更することはできません。
    • GKE は、この容量の一部をシステム用に予約するため、割り当て可能なエフェメラル ストレージは CAPACITY 未満になります。詳細については、ローカル エフェメラル ストレージの予約をご覧ください。

ローカル SSD ストレージが混在するノードプールを作成する

既存のクラスタにノードプールを追加するには、gcloud container node-pools create コマンドを使用します。

ノードプールのバージョンが 1.37.0-gke.3570000 より前の場合は、次の操作を行う必要があります。

  • 次のノードプール作成コマンドに --node-version=<var>VERSION</var> フラグを追加し、<var>VERSION</var> を 1.37.0-gke.3570000 以降に設定します。
  • クラスタ コントロール プレーンのバージョンが 1.37.0-gke.3570000 以降であることを確認します。

ローカル SSD の数を構成できるマシンタイプの場合は、次のコマンドを実行します。

gcloud container node-pools create POOL_NAME \
    --cluster=CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --ephemeral-storage-local-ssd count=NUMBER_OF_DISKS,capacity=CAPACITY

ローカル SSD の数が固定されているマシンタイプの場合は、次のコマンドを実行します。

gcloud container node-pools create POOL_NAME \
    --cluster=CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --ephemeral-storage-local-ssd capacity=CAPACITY

次のように置き換えます。

  • POOL_NAME: 作成する新しいノードプールの名前。
  • CLUSTER_NAME: クラスタの名前。
  • MACHINE_TYPE: 使用するマシンタイプ。サポートされているマシンタイプの一覧については、マシンシリーズのサポートをご覧ください。共有コア マシンタイプはローカル SSD をサポートしていないため、デフォルトの e2-medium マシンタイプではローカル SSD を使用できません。
  • NUMBER_OF_DISKS: 構成可能なローカル SSD 数を持つマシンタイプの場合、各ノードでプロビジョニングするローカル SSD ディスクの数。
  • CAPACITY: エフェメラル ストレージ用にフォーマットするローカル SSD の合計容量。この値は 0 より大きく、物理ローカル SSD の合計容量(ディスク数 × ディスクあたりのサイズ、例: 200GB)より小さくする必要があります。次の点にご注意ください。

    • この値は、ノードプールの作成後に変更することはできません。
    • GKE は、この容量の一部をシステム用に予約するため、割り当て可能なエフェメラル ストレージは CAPACITY 未満になります。詳細については、ローカル エフェメラル ストレージの予約をご覧ください。

ノードラベルを確認する

プロビジョニング後に、ノードにエフェメラル ブロック ストレージと未加工ブロック ストレージの両方に正しいラベルが付いていることを確認するには、次のコマンドを実行します。

kubectl describe node NODE_NAME

出力には、次の両方のノードラベルが含まれている必要があります。

  • cloud.google.com/gke-ephemeral-storage-local-ssd: "true"
  • cloud.google.com/gke-local-nvme-ssd-partition: "true"

エフェメラル容量にアクセスする

他の GKE エフェメラル ボリュームと同様に、emptyDir ボリュームを介して予約済みのエフェメラル ストレージにアクセスします。

未加工のブロック容量にアクセスする

raw ブロック容量にアクセスするには、StorageClass オブジェクトを作成し、ローカル PersistentVolume オブジェクトをプロビジョニングして、PersistentVolumeClaim オブジェクトを使用して要求します。

StorageClass を作成する

コミュニティの静的プロビジョナーを使用せずに PersistentVolume オブジェクトをプロビジョニングするため、volumeBindingMode: WaitForFirstConsumer フィールドを含む StorageClass オブジェクトを作成することをおすすめします。これにより、Pod のスケジューリング時点までバインディングが遅延され、Pod を実行できる適切なノードから Kubernetes がローカル SSD を選択するようになります。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: "local-storage"
provisioner: "kubernetes.io/no-provisioner"
volumeBindingMode: "WaitForFirstConsumer"

PersistentVolume を作成する

ローカル PersistentVolume オブジェクトには、ノードのアップグレード、スケーリング、クラスタの自動スケーリングに関する重要な制限があります。ボリュームを作成する前に、ローカル PersistentVolume の制限事項を確認して、ライフサイクル イベントがデータにどのように影響するかを理解してください。

PersistentVolume オブジェクトを作成するには、次の手順を完了して、ローカル SSD パーティションへのシンボリック リンクパスを見つけ、正確な残りの容量を特定して、マニフェストを構成します。

パーティション容量を確認する

ターゲット ノードで次のコマンドを実行して、パーティションに残っている正確な未加工ブロック容量を確認します。

kubectl debug node/NODE_NAME -it --image=ubuntu -- \
    sh -c 'ls -l /host/dev/disk/by-id/ | grep partition; lsblk -o NAME,SIZE,TYPE'

NODE_NAME は、ローカル SSD がアタッチされているノードの名前に置き換えます。

出力には、デバイス名とパーティション シンボリック リンクのマッピングが表示されます。これにより、どのパーティションがどのデバイスに対応しているかを確認できます。また、ノードにアタッチされているブロック デバイスとそのサイズも一覧表示されます。

たとえば、2 つのローカル SSD をノードにアタッチし、200 GB をエフェメラル ストレージ(capacity=200GB)に割り当てると、出力は次のようになります。

google-local-ssd-partition0 -> /dev/nvme0n1p2
google-local-ssd-partition1 -> /dev/nvme0n2p2
NAME          SIZE TYPE
...
nvme0n1       375G disk
|-nvme0n1p1   100G part
| `-md0     199.9G raid0
`-nvme0n1p2   275G part
nvme0n2       375G disk
|-nvme0n2p1   100G part
| `-md0     199.9G raid0
`-nvme0n2p2   275G part

google-local-ssd-partition シンボリック リンクと基盤となるデバイス名のマッピング、およびそのパーティションのサイズをメモします。PersistentVolume マニフェストで、サイズを PARTITION_SIZE として使用し、シンボリック リンクパスを LOCAL_SSD_SYMLINK_PATH として使用します。

たとえば、上記の出力では、/dev/disk/by-id/google-local-ssd-partition0 シンボリック リンクは nvme0n1p2 パーティションを指しています。このパーティションのサイズは 275G です。

kubectl debug コマンドは、デフォルトの Namespace で一時的な Pod を実行したままにします。リソースを消費しないようにするには、次のコマンドを実行してこの Pod を削除します。

kubectl delete pod DEBUG_POD_NAME

DEBUG_POD_NAME は、kubectl debug コマンドによって作成された Pod の名前に置き換えます。この名前は、コマンドの実行時に出力されます。

PersistentVolume マニフェストを構成する

確認したパーティション サイズでボリューム マニフェストを構成します。ローカル ボリュームには nodeAffinity フィールドが必要です。これにより、Kubernetes は、このボリュームを要求する Pod を、ローカル SSD が物理的にアタッチされている特定のノードにスケジュールします。次の例は、raw ブロックのローカル SSD パーティションのマニフェストを示しています。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: "example-local-pv"
spec:
  capacity:
    storage: PARTITION_SIZE
  accessModes:
  - "ReadWriteOnce"
  volumeMode: "Block"
  persistentVolumeReclaimPolicy: "Retain"
  storageClassName: "local-storage"
  local:
    path: "LOCAL_SSD_SYMLINK_PATH"
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: "kubernetes.io/hostname"
          operator: "In"
          values:
          - "NODE_NAME"

次のように置き換えます。

  • PARTITION_SIZE: 最初のステップで確認した、残りの実際の未加工ブロック容量。値がパーティションの容量と正確に一致するように、Gi 接尾辞(275Gi など)を使用します。
  • NODE_NAME: ローカル SSD がアタッチされているノードの名前。
  • LOCAL_SSD_SYMLINK_PATH: 最初のステップで見つけた、フォーマットされていないローカル SSD パーティションへのシンボリック リンクパス。ノードプールにアタッチされたローカル SSD ごとに、ホスト OS は、残りの raw ブロック容量にアクセスするためのシンボリック リンクを作成します。パスはゼロ インデックスのパターンに従います。たとえば、3 つのローカル SSD をアタッチする場合、パーティションの raw ブロックパスは次のようになります。
    • /dev/disk/by-id/google-local-ssd-partition0
    • /dev/disk/by-id/google-local-ssd-partition1
    • /dev/disk/by-id/google-local-ssd-partition2

PersistentVolumeClaim を作成する

PersistentVolumeClaim オブジェクトを作成して、ボリュームを要求します。対応する仕様は次のようになります。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: "example-local-pvc"
spec:
  accessModes:
  - "ReadWriteOnce"
  volumeMode: "Block"
  storageClassName: "local-storage"
  resources:
    requests:
      storage: PARTITION_SIZE

Pod でボリュームを使用する

volumeDevices 仕様で PersistentVolumeClaim オブジェクトを参照することで、Pod で PersistentVolume オブジェクトを使用できます。example-local-pvc クレームを使用する Pod マニフェストのサンプルを次に示します。

apiVersion: v1
kind: Pod
metadata:
  name: "example-pod"
spec:
  containers:
  - name: "app"
    image: "ubuntu:22.04"
    command: ["/bin/sh", "-c"]
    args: ["tail -f /dev/null"]
    volumeDevices:
    - name: "raw"
      devicePath: "/dev/xvda"
  volumes:
  - name: "raw"
    persistentVolumeClaim:
      claimName: "example-local-pvc"

kubernetes.io/no-provisioner プロビジョナーを使用しているため、GKE はローカル ボリュームを自動的にクリーンアップしません。PersistentVolume オブジェクトを削除する場合は、ディスクを安全に再利用する前に、基盤となるディスクからデータを手動で消去する必要があります。

トラブルシューティング

トラブルシューティングの手順については、GKE のストレージのトラブルシューティングをご覧ください。

次のステップ