Spot Pod でフォールト トレラントなワークロードを低コストで実行する


このページでは、Google Kubernetes Engine(GKE)の Autopilot クラスタで Spot Pod を使用して、フォールト トレラントなワークロードをより低コストで実行する方法について説明します。

概要

GKE Autopilot クラスタの Spot Pod は、Compute Engine の Spot VM を基盤とするノードで実行される Pod です。Spot Pod の料金は標準の Autopilot Pod より低く設定されていますが、標準の Pod の実行でコンピューティング リソースが必要になると、GKE によって強制排除される場合があります。

Spot Pod は、ワークロードを標準の Pod として実行するよりも低コストで、ステートレス、バッチ、フォールト トレラントなワークロードを実行したい場合に適しています。Autopilot クラスタで Spot Pod を使用するには、Pod 仕様でマニフェストを変更して、Spot Pod をリクエストします。

Spot Pod は、デフォルトの汎用 Autopilot コンピューティング クラスと、特定のハードウェア要件を満たす特殊なコンピューティング クラスで実行できます。これらのコンピューティング クラスの詳細については、Autopilot のコンピューティング クラスをご覧ください。

Autopilot クラスタの Spot Pod の料金の詳細については、Google Kubernetes Engine の料金をご覧ください。

Spot Pod は、Autopilot サービスレベル契約からは除外されています。

利点

Autopilot クラスタで Spot Pod を使用すると、次の利点があります。

  • 標準の Autopilot Pod で同じワークロードを実行する場合よりもコストを抑えることができます。
  • GKE が自動スケーリングとスケジュールを自動的に管理します。
  • GKE は、Spot Pod を実行するノードを自動的に taint して、それらのノードで重要なワークロードなどの標準 Pod がスケジュールされないようにします。Spot Pod を使用するデプロイは、対応する容認値で自動的に更新されます。

始める前に

作業を始める前に、次のことを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。すでに gcloud CLI をインストールしている場合は、gcloud components update を実行して最新のバージョンを取得する。

Autopilot ワークロードの Spot Pod をリクエストする

Pod を Spot Pod として実行するようリクエストするには、Pod 仕様の nodeSelector またはノード アフィニティcloud.google.com/gke-spot=true ラベルを使用します。GKE は、Spot Pod を実行できるノードを自動的にプロビジョニングします。

コンピューティング リソースが Google Cloud の別の場所で必要になる場合など、Spot Pod はいつでも強制排除され、終了する可能性があります。terminationGracePeriodSeconds フィールドを指定すると、終了する場合に、終了するノード上の Spot Pod は最大 15 秒の猶予期間をリクエストできます。この期間はベスト エフォート ベースで付与されます。

プリエンプションでの Spot Pod の最大猶予期間は 15 秒です。terminationGracePeriodSeconds で 15 秒を超える時間をリクエストしても、プリエンプションは 15 秒を超えて猶予されることはありません。強制排除の場合、Pod に SIGTERM シグナルが送信されます。猶予期間中にシャットダウンの手順を行う必要があります。

Autopilot では、GKE が Spot Pod の実行用に作成されたノードを自動的に taint し、対応する容認値でワークロードを変更します。taint により、Spot Pod を実行するノードで標準 Pod がスケジュールされなくなります。

nodeSelector を使用して Spot Pod を必須にする

nodeSelector を使用すると、Deployment で Spot Pod を必須にできます。次の例のように、Deployment に cloud.google.com/gke-spot=true ラベルを追加します。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      labels:
        app: pi
    spec:
      nodeSelector:
        cloud.google.com/gke-spot: "true"
      terminationGracePeriodSeconds: 15
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

ノード アフィニティを使用して Spot Pod をリクエストする

また、ノード アフィニティを使用して Spot Pod をリクエストすることもできます。ノード アフィニティを使用すると、ワークロードを実行するノードをより柔軟に選択できます。たとえば、複数の選択基準を組み合わせると、Pod の実行場所をきめ細かく制御できます。ノード アフィニティを使用して Spot Pod をリクエストする場合は、使用するノード アフィニティの種類を次のように指定できます。

  • requiredDuringSchedulingIgnoredDuringExecution: Spot Pod の使用を必須にします。
  • preferredDuringSchedulingIgnoredDuringExecution: ベスト エフォート ベースで Spot Pod を使用します。

ノード アフィニティを使用して Deployment の Spot Pod を必須にするには、次の nodeAffinity ルールを Deployment マニフェストに追加します。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      labels:
        app: pi
    spec:
      terminationGracePeriodSeconds: 15
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: cloud.google.com/gke-spot
                operator: In
                values:
                - "true"
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

ベストエフォート ベースで Spot Pod をリクエストする

ノード アフィニティを使用してベスト エフォート ベースで Spot Pod をリクエストするには、preferredDuringSchedulingIgnoredDuringExecution を使用します。Spot Pod を優先してリクエストすると、GKE は次の順序で Pod をスケジュールします。

  1. 割り当て可能な容量がある Spot Pod を実行できる既存のノード。
  2. 割り当て可能な容量がある既存の標準ノード。
  3. Spot Pod を実行できる新しいノード(コンピューティング リソースが利用可能である場合)。
  4. 新しい標準ノード。

GKEは、Spot Pod の新しいノードを作成するよりも、割り当て可能な容量を持つ既存の標準ノードを優先するため、Spot Pod よりも標準 Pod として実行される Pod の数が多くなり、Spot Pod の低価格を最大限に活用できなくなる可能性があります。

プリエンプティブル Pod のリクエスト

Autopilot クラスタは、cloud.google.com/gke-preemptible セレクタを使用して、プリエンプティブル Pod のリクエストをサポートします。このセレクタを使用する Pod は、自動的に Spot Pod に移行され、セレクタが cloud.google.com/gke-spot に変更されます。

終了した Pod を探して削除する

Pod が正常に終了している間、kubelet が Failed ステータスと Shutdown 理由を終了する Pod に割り当てます。終了した Pod の数がしきい値 1,000 に達すると、ガベージ コレクションによって Pod がクリーンアップされます。次のコマンドを使用して、シャットダウンされる Pod を手動で削除することもできます。

kubectl get pods --all-namespaces | grep -i shutdown | awk '{print $1, $2}' | xargs -n2 kubectl delete pod -n

ワークロードが Spot Pod を使用しないようにする

既存の Spot Pod を更新して標準 Pod として実行する場合は、次のいずれかの方法を使用できます。

  • ワークロードを再作成する: Deployment を削除し、マニフェストで Spot Pod を選択する行を削除してから、更新された Deployment マニフェストをクラスタに適用します。
  • ワークロードを編集する: Pod がクラスタ内で実行されているときに Deployment 仕様を編集します。

どちらの方法でも、ワークロードが軽微に中断される可能性があります。

ワークロードを再作成する

次の手順では、既存の Deployment を削除し、更新されたマニフェストをクラスタに適用する方法について説明します。この手順は、Jobs などの他のタイプの Kubernetes ワークロードにも使用できます。

GKE が更新された Pod を正しいタイプのノードに配置するようにするには、ワークロードの既存の状態を Kubernetes API サーバーからファイルにエクスポートし、そのファイルを編集する必要があります。

  1. ワークロード仕様を YAML ファイルに書き込みます。

    kubectl get deployment DEPLOYMENT_NAME -o yaml > DEPLOYMENT_NAME-on-demand.yaml
    

    DEPLOYMENT_NAME は、デプロイの名前に置き換えます。Jobs や Pod などの他のタイプのワークロードの場合は、kubectl get コマンドで対応するリソース名(kubectl get pod など)を使用します。

  2. テキスト エディタで YAML ファイルを開きます。

    vi DEPLOYMENT_NAME-on-demand.yaml
    
  3. Spot Pod の nodeSelector と、GKE が Spot Pod に追加した toleration をファイルから削除します。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      annotations:
      # lines omitted for clarity
    spec:
      progressDeadlineSeconds: 600
      replicas: 6
      revisionHistoryLimit: 10
      selector:
        matchLabels:
          pod: nginx-pod
      strategy:
        rollingUpdate:
          maxSurge: 25%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
        # lines omitted for clarity
        spec:
          containers:
          - image: nginx
            imagePullPolicy: Always
            name: web-server
            resources:
              limits:
                ephemeral-storage: 1Gi
              requests:
                cpu: 500m
                ephemeral-storage: 1Gi
                memory: 2Gi
            securityContext:
              capabilities:
                drop:
                - NET_RAW
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
          dnsPolicy: ClusterFirst
          nodeSelector:
            cloud.google.com/gke-spot: "true"
          restartPolicy: Always
          schedulerName: default-scheduler
          securityContext:
            seccompProfile:
              type: RuntimeDefault
          terminationGracePeriodSeconds: 15
          tolerations:
          - effect: NoSchedule
            key: kubernetes.io/arch
            operator: Equal
            value: amd64
          - effect: NoSchedule
            key: cloud.google.com/gke-spot
            operator: Equal
            value: "true"
    status:
      #lines omitted for clarity
    

    Pod を Spot ノードではなくオンデマンド ノードで実行する必要があることを GKE に示すには、toleration と nodeSelector の両方を削除する必要があります。

  4. 更新したマニフェストを保存します。

  5. Deployment マニフェストを削除してクラスタに再適用します。

    kubectl replace -f DEPLOYMENT_NAME-on-demand.yaml
    

    このオペレーションの所要時間は、GKE が終了してクリーンアップする必要がある Pod の数によって異なります。

ワークロードをインプレースで編集する

次の手順では、実行中の Deployment をその場で編集して、Pod をオンデマンド ノードで実行する必要があることを GKE に示す方法について説明します。Jobs などの他のタイプの Kubernetes ワークロードにも、これらの手順を使用できます。

GKE はワークロードのアドミッション中に Spot Pod の toleration をワークロード仕様に自動的に追加するため、Kubernetes API でワークロード オブジェクトを編集する必要があります。

  1. ワークロード マニフェストを開いてテキスト エディタで編集します。

    kubectl edit deployment/DEPLOYMENT_NAME
    

    DEPLOYMENT_NAME は、Deployment の名前に置き換えます。Jobs や Pod などの他のタイプのワークロードの場合は、kubectl edit コマンドで対応するリソース名(kubectl edit pod/POD_NAME など)を使用します。

  2. テキスト エディタで、Spot Pod のノードセレクタまたはノード アフィニティ ルールと、GKE がマニフェストに追加した toleration を削除します。次の例をご覧ください。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: example-deployment
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          type: dev
      template:
        metadata:
          labels:
            type: dev
        spec:
          nodeSelector:
            cloud.google.com/gke-spot: "true"
          tolerations:
          - effect: NoSchedule
            key: cloud.google.com/gke-spot
            operator: Equal
            value: "true"
          containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80
    
  3. 更新したマニフェストを保存して、テキスト エディタを閉じます。更新されたオブジェクト構成は、Pod をオンデマンド ノードで実行する必要があることを GKE に示します。GKE は Pod を再作成して、新しいオンデマンド ノードに配置します。

ワークロードがオンデマンド ノードで実行されていることを確認する

更新されたワークロードが Spot Pod で実行されなくなったことを確認するには、ワークロードを調べて、Spot Pod の toleration を探します。

  • ワークロードを検査します。

    kubectl describe deployment DEPLOYMENT_NAME
    

出力には、spec.tolerations フィールドに cloud.google.com/gke-spot のエントリが表示されません。

次のステップ