Skip to content
Published on

[AWS] KarpenterでGPUノード管理:AI/MLワークロードの最適化

Authors

目次

1. KarpenterによるGPUノードプロビジョニング

GPUワークロードの特殊性

AI/MLワークロードは一般的なコンピューティングとは異なる固有の要件があります:

+---------------------------------------------------------------+
|                GPUワークロードの特性                           |
+---------------------------------------------------------------+
| - 高額なGPUインスタンス(1時間あたり数〜数十ドル)            |
| - 長時間の学習ジョブ(数時間〜数日)                          |
| - 推論作業の低レイテンシ要件                                  |
| - GPUメモリ(VRAM)ベースのリソース制約                       |
| - インスタンスタイプ別のGPU性能差が大きい                     |
| - Spot中断時の学習進捗損失リスク                              |
+---------------------------------------------------------------+

KarpenterがGPU管理に適している理由

+------------------------------------------+
|      従来方式(Cluster Autoscaler)      |
|                                           |
|  GPU Node Group A: p3.2xlarge             |
|  GPU Node Group B: g5.xlarge              |
|  GPU Node Group C: g5.2xlarge             |
|  GPU Node Group D: p4d.24xlarge           |
|  ...                                      |
|  各Node Groupを個別管理(非効率)         |
+------------------------------------------+

+------------------------------------------+
|         Karpenter方式                    |
|                                           |
|  単一のGPU NodePool:                     |
|  - Pod要件の分析                          |
|  - 最適なGPUインスタンスを自動選択        |
|  - Spot/On-Demandの自動切替              |
|  - コストベースのインスタンス最適化       |
+------------------------------------------+

2. GPU NodePool設定

汎用GPU NodePool

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-general
spec:
  template:
    metadata:
      labels:
        node-type: gpu
        workload: ai-ml
    spec:
      requirements:
        # GPUインスタンスのみ選択
        - key: karpenter.k8s.aws/instance-gpu-count
          operator: Gt
          values: ['0']

        # GPUインスタンスファミリー
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ['g', 'p']

        # 容量タイプ
        - key: karpenter.sh/capacity-type
          operator: In
          values: ['on-demand', 'spot']

        # アベイラビリティゾーン
        - key: topology.kubernetes.io/zone
          operator: In
          values: ['ap-northeast-1a', 'ap-northeast-1c', 'ap-northeast-1d']

        # x86アーキテクチャのみ
        - key: kubernetes.io/arch
          operator: In
          values: ['amd64']

      # GPU専用taint
      taints:
        - key: nvidia.com/gpu
          value: 'true'
          effect: NoSchedule

      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-optimized

      # GPUノードは長い有効期限
      expireAfter: 336h # 14日

  limits:
    cpu: '500'
    memory: 2000Gi
    nvidia.com/gpu: '100'

  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 5m
    budgets:
      - nodes: '1'

  weight: 80

AWS GPUインスタンスタイプガイド

+------------------+----------+----------+------------------+---------------------+
| インスタンス     | GPU      | 数量     | GPUメモリ        | 主な用途            |
+------------------+----------+----------+------------------+---------------------+
| g4dn.xlarge      | T4       | 1        | 16 GB            | 推論、軽量学習      |
| g4dn.12xlarge    | T4       | 4        | 64 GB            | マルチ推論          |
| g5.xlarge        | A10G     | 1        | 24 GB            | 推論、微調整        |
| g5.12xlarge      | A10G     | 4        | 96 GB            | 中型学習            |
| g5.48xlarge      | A10G     | 8        | 192 GB           | 大型学習            |
| g6.xlarge        | L4       | 1        | 24 GB            | 推論最適化          |
| g6.12xlarge      | L4       | 4        | 96 GB            | マルチモーダル推論  |
| p3.2xlarge       | V100     | 1        | 16 GB            | 汎用学習            |
| p3.8xlarge       | V100     | 4        | 64 GB            | 大規模学習          |
| p4d.24xlarge     | A100     | 8        | 320 GB (40GB x8) | 超大規模学習        |
| p5.48xlarge      | H100     | 8        | 640 GB (80GB x8) | 最高性能学習        |
+------------------+----------+----------+------------------+---------------------+

推論専用NodePool

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-inference
spec:
  template:
    metadata:
      labels:
        node-type: gpu-inference
        workload: inference
    spec:
      requirements:
        # 推論に適したインスタンス
        - key: karpenter.k8s.aws/instance-gpu-name
          operator: In
          values: ['t4', 'a10g', 'l4']

        # Spotインスタンス優先(推論はstateless)
        - key: karpenter.sh/capacity-type
          operator: In
          values: ['spot', 'on-demand']

        # インスタンスサイズ制限
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ['xlarge', '2xlarge', '4xlarge']

      taints:
        - key: nvidia.com/gpu
          value: 'true'
          effect: NoSchedule

      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-optimized

  limits:
    nvidia.com/gpu: '50'

  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 2m

  weight: 60

学習専用NodePool

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-training
spec:
  template:
    metadata:
      labels:
        node-type: gpu-training
        workload: training
    spec:
      requirements:
        # 学習に適した高性能GPU
        - key: karpenter.k8s.aws/instance-gpu-name
          operator: In
          values: ['a100', 'h100', 'a10g']

        # On-Demand専用(学習の中断コストが高い)
        - key: karpenter.sh/capacity-type
          operator: In
          values: ['on-demand']

        # 大型インスタンス
        - key: karpenter.k8s.aws/instance-gpu-count
          operator: Gt
          values: ['0']

      taints:
        - key: nvidia.com/gpu
          value: 'true'
          effect: NoSchedule

      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-training

      # 学習用ノードは有効期限なし
      expireAfter: 720h # 30日

  limits:
    nvidia.com/gpu: '32'

  disruption:
    # 学習中のConsolidation無効化
    consolidationPolicy: WhenEmpty
    consolidateAfter: 30m
    budgets:
      - nodes: '0'

  weight: 90

3. GPU専用EC2NodeClass

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: gpu-optimized
spec:
  # GPUドライバ対応AMI
  amiSelectorTerms:
    - alias: al2023@latest

  role: KarpenterNodeRole-my-cluster

  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster
        network-type: private

  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster

  # GPUワークロード用大容量ディスク
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 200Gi
        volumeType: gp3
        iops: 6000
        throughput: 250
        encrypted: true
        deleteOnTermination: true

  metadataOptions:
    httpEndpoint: enabled
    httpPutResponseHopLimit: 2
    httpTokens: required

  tags:
    Environment: production
    NodeType: gpu
    ManagedBy: karpenter

  # GPUドライバインストール用ユーザーデータ
  userData: |
    #!/bin/bash
    echo "GPU node bootstrap"
    # NVIDIAドライバはGPU Operatorが処理

学習専用EC2NodeClass(大容量ストレージ)

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: gpu-training
spec:
  amiSelectorTerms:
    - alias: al2023@latest

  role: KarpenterNodeRole-my-cluster

  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster

  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster

  # 学習データ用大容量・高性能ストレージ
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 500Gi
        volumeType: gp3
        iops: 16000
        throughput: 1000
        encrypted: true
        deleteOnTermination: true

  tags:
    Environment: production
    NodeType: gpu-training
    ManagedBy: karpenter

4. Spot GPUインスタンス戦略

Spot GPUのコスト削減効果

+------------------+-------------------+-------------------+---------+
| インスタンス     | On-Demand (時間)  | Spot予想 (時間)   | 削減率  |
+------------------+-------------------+-------------------+---------+
| g4dn.xlarge      | ~0.526            | ~0.158            | ~70%    |
| g5.xlarge        | ~1.006            | ~0.302            | ~70%    |
| g5.2xlarge       | ~1.212            | ~0.364            | ~70%    |
| g5.12xlarge      | ~5.672            | ~1.702            | ~70%    |
| p3.2xlarge       | ~3.060            | ~0.918            | ~70%    |
+------------------+-------------------+-------------------+---------+
 (価格はリージョンと時期により変動します)

Spot GPU NodePool - 推論用

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-spot-inference
spec:
  template:
    metadata:
      labels:
        node-type: gpu-spot
        workload: inference
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ['spot']

        # 推論に適した多様なGPUタイプ
        - key: karpenter.k8s.aws/instance-gpu-name
          operator: In
          values: ['t4', 'a10g', 'l4']

        # 多様なサイズでSpot可用性確保
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ['xlarge', '2xlarge', '4xlarge', '8xlarge', '12xlarge']

        # 複数AZ活用
        - key: topology.kubernetes.io/zone
          operator: In
          values: ['ap-northeast-1a', 'ap-northeast-1c', 'ap-northeast-1d']

      taints:
        - key: nvidia.com/gpu
          value: 'true'
          effect: NoSchedule

      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-optimized

  limits:
    nvidia.com/gpu: '40'

  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m

  weight: 70

Spot中断対策

# 長時間学習ジョブにdo-not-disruptアノテーションを適用
apiVersion: v1
kind: Pod
metadata:
  name: training-job
  annotations:
    karpenter.sh/do-not-disrupt: 'true'
spec:
  containers:
    - name: training
      image: my-training-image:latest
      resources:
        requests:
          nvidia.com/gpu: '1'
          cpu: '4'
          memory: 16Gi
        limits:
          nvidia.com/gpu: '1'
  tolerations:
    - key: nvidia.com/gpu
      operator: Exists
      effect: NoSchedule
  terminationGracePeriodSeconds: 120

5. NVIDIA GPU Operator連携

GPU Operatorの概要

+----------------------------------------------------------------+
|                    NVIDIA GPU Operator                          |
|                                                                |
|  +------------------+  +-------------------+  +--------------+ |
|  | NVIDIAドライバ   |  | Container Toolkit |  | Device Plugin| |
|  | (自動インストール)|  | (自動設定)        |  | (自動デプロイ)| |
|  +------------------+  +-------------------+  +--------------+ |
|                                                                |
|  +------------------+  +-------------------+  +--------------+ |
|  | GPU Feature      |  | DCGM Exporter    |  | MIG Manager  | |
|  | Discovery        |  | (メトリクス収集)  |  | (MIG管理)    | |
|  +------------------+  +-------------------+  +--------------+ |
+----------------------------------------------------------------+

GPU Operatorのインストール

# NVIDIA GPU Operator Helmリポジトリ追加
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

# GPU Operatorインストール
helm install gpu-operator nvidia/gpu-operator \
  --namespace gpu-operator \
  --create-namespace \
  --set driver.enabled=true \
  --set toolkit.enabled=true \
  --set devicePlugin.enabled=true \
  --set dcgmExporter.enabled=true \
  --set migManager.enabled=false \
  --set gfd.enabled=true

GPU OperatorとKarpenterの連携確認

# GPUノードのラベル確認
kubectl get nodes -l node-type=gpu -o json | \
  jq '.items[].metadata.labels | with_entries(select(.key | startswith("nvidia")))'

# GPUリソース確認
kubectl describe node gpu-node-name | grep -A 5 "nvidia.com/gpu"

# DCGM Exporter Pod確認
kubectl get pods -n gpu-operator -l app=nvidia-dcgm-exporter

GPUワークロードデプロイ例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gpu-inference-server
  namespace: ml-serving
spec:
  replicas: 3
  selector:
    matchLabels:
      app: inference-server
  template:
    metadata:
      labels:
        app: inference-server
    spec:
      containers:
        - name: inference
          image: nvcr.io/nvidia/tritonserver:24.01-py3
          ports:
            - containerPort: 8000
              name: http
            - containerPort: 8001
              name: grpc
            - containerPort: 8002
              name: metrics
          resources:
            requests:
              cpu: '4'
              memory: 16Gi
              nvidia.com/gpu: '1'
            limits:
              nvidia.com/gpu: '1'
          volumeMounts:
            - name: model-store
              mountPath: /models
      tolerations:
        - key: nvidia.com/gpu
          operator: Exists
          effect: NoSchedule
      nodeSelector:
        node-type: gpu-inference
      volumes:
        - name: model-store
          persistentVolumeClaim:
            claimName: model-store-pvc

6. マルチアーキテクチャサポート(x86 + ARM/Graviton)

マルチアーキテクチャNodePool

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: multi-arch
spec:
  template:
    spec:
      requirements:
        # x86とARMの両方を許可
        - key: kubernetes.io/arch
          operator: In
          values: ['amd64', 'arm64']

        # Gravitonインスタンスを含む
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ['c', 'm', 'r']

        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ['5']

        - key: karpenter.sh/capacity-type
          operator: In
          values: ['on-demand', 'spot']

      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default

  limits:
    cpu: '1000'
    memory: 2000Gi

  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m

GravitonのGPU代替:Inferentia/Trainium

# AWS Inferentia推論専用NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: inferentia
spec:
  template:
    metadata:
      labels:
        accelerator: inferentia
    spec:
      requirements:
        - key: node.kubernetes.io/instance-type
          operator: In
          values: ['inf2.xlarge', 'inf2.8xlarge', 'inf2.24xlarge', 'inf2.48xlarge']

        - key: karpenter.sh/capacity-type
          operator: In
          values: ['on-demand']

      taints:
        - key: aws.amazon.com/neuron
          value: 'true'
          effect: NoSchedule

      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: inferentia-nodes

  limits:
    aws.amazon.com/neuron: '32'

  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 5m

7. コスト最適化戦略

SpotとOn-Demandの混合戦略

+-------------------------------------------------------------+
|           コスト最適化の意思決定ツリー                        |
+-------------------------------------------------------------+
|                                                             |
|  ワークロードタイプの確認                                   |
|      |                                                      |
|      +-- 推論 (Stateless) --> Spot優先 + On-Demand代替      |
|      |                                                      |
|      +-- 微調整 (短期) --> Spot + チェックポイント戦略      |
|      |                                                      |
|      +-- 大規模学習 (長期) --> On-Demand + リザーブド       |
|      |                                                      |
|      +-- バッチ処理 --> Spot専用                            |
|                                                             |
+-------------------------------------------------------------+

重み付き優先順位によるインスタンスファミリー戦略

# 第1優先:G5 Spot(最もコスト効率的な推論)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-tier1-g5-spot
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ['spot']
        - key: karpenter.k8s.aws/instance-gpu-name
          operator: In
          values: ['a10g']
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ['g']
      taints:
        - key: nvidia.com/gpu
          value: 'true'
          effect: NoSchedule
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-optimized
  weight: 100
  limits:
    nvidia.com/gpu: '20'
---
# 第2優先:G4dn Spot(フォールバック)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-tier2-g4dn-spot
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ['spot']
        - key: karpenter.k8s.aws/instance-gpu-name
          operator: In
          values: ['t4']
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ['g']
      taints:
        - key: nvidia.com/gpu
          value: 'true'
          effect: NoSchedule
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-optimized
  weight: 50
  limits:
    nvidia.com/gpu: '20'
---
# 第3優先:G5 On-Demand(最終手段)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-tier3-g5-ondemand
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ['on-demand']
        - key: karpenter.k8s.aws/instance-gpu-name
          operator: In
          values: ['a10g']
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ['g']
      taints:
        - key: nvidia.com/gpu
          value: 'true'
          effect: NoSchedule
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-optimized
  weight: 10
  limits:
    nvidia.com/gpu: '10'

Consolidationポリシーの最適化

# GPUノードのConsolidation設定
disruption:
  # GPUノードはWhenEmptyのみ使用(実行中のGPUジョブを保護)
  consolidationPolicy: WhenEmpty
  # 空ノード検知後5分待機(一時的な非アクティブを考慮)
  consolidateAfter: 5m
  budgets:
    # 同時に最大1ノードのみ中断
    - nodes: '1'
    # 業務時間中は中断をブロック
    - nodes: '0'
      schedule: '0 9 * * MON-FRI'
      duration: 10h

8. ノード中断バジェット(Node Disruption Budgets)

GPUワークロード用Disruption Budget

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-training-protected
spec:
  template:
    spec:
      requirements:
        - key: karpenter.k8s.aws/instance-gpu-count
          operator: Gt
          values: ['0']
        - key: karpenter.sh/capacity-type
          operator: In
          values: ['on-demand']
      taints:
        - key: nvidia.com/gpu
          value: 'true'
          effect: NoSchedule
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-training

  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 30m
    budgets:
      # 学習時間中は中断を完全にブロック
      - nodes: '0'
        schedule: '0 0 * * *'
        duration: 23h

      # メンテナンスウィンドウ(毎日1時間)
      - nodes: '1'
        schedule: '0 23 * * *'
        duration: 1h

      # ドリフトによる中断は別途管理
      - nodes: '1'
        reasons:
          - 'Drifted'

Podレベルの保護

# 長時間学習Pod:Karpenterの中断を防止
apiVersion: v1
kind: Pod
metadata:
  name: long-training-job
  annotations:
    # このアノテーションでKarpenterの自発的中断を防止
    karpenter.sh/do-not-disrupt: 'true'
spec:
  containers:
    - name: trainer
      image: my-training-image:v1
      resources:
        requests:
          nvidia.com/gpu: '4'
          cpu: '16'
          memory: 64Gi
        limits:
          nvidia.com/gpu: '4'
  tolerations:
    - key: nvidia.com/gpu
      operator: Exists
      effect: NoSchedule
  # 十分な終了猶予時間(チェックポイント保存)
  terminationGracePeriodSeconds: 300

PDB(Pod Disruption Budget)設定

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: inference-server-pdb
  namespace: ml-serving
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: inference-server

9. Prometheus/Grafanaによるモニタリング

Karpenterメトリクス収集設定

# Karpenter ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: karpenter
  namespace: karpenter
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: karpenter
  endpoints:
    - port: http-metrics
      interval: 15s
      path: /metrics

主要なKarpenterメトリクス

+-----------------------------------------------+------------------------------------------+
| メトリクス                                    | 説明                                     |
+-----------------------------------------------+------------------------------------------+
| karpenter_nodeclaims_launched_total           | 起動されたNodeClaim総数                  |
| karpenter_nodeclaims_registered_total         | 登録されたNodeClaim総数                  |
| karpenter_nodeclaims_terminated_total         | 終了されたNodeClaim総数                  |
| karpenter_pods_state                          | Pod状態(ノード、ネームスペース等)      |
| karpenter_nodepool_usage                      | NodePool別リソース使用量                 |
| karpenter_nodepool_limit                      | NodePool別リソース制限                   |
| karpenter_voluntary_disruption_eligible_nodes | 自発的中断対象ノード数                   |
| karpenter_disruption_actions_performed_total  | 実行された中断アクション数               |
| karpenter_nodes_allocatable                   | ノード別割当可能リソース                 |
| karpenter_nodes_total_daemon_requests         | DaemonSetリソース要求合計                |
+-----------------------------------------------+------------------------------------------+

GPU専用Grafanaダッシュボードクエリ

# GPUノード数の追跡
count(karpenter_nodes_allocatable{resource_type="nvidia.com/gpu"} > 0)

# GPU使用率(DCGM Exporter必要)
DCGM_FI_DEV_GPU_UTIL

# NodePool別GPU使用量 vs 制限
karpenter_nodepool_usage{resource_type="nvidia.com/gpu"}
  /
karpenter_nodepool_limit{resource_type="nvidia.com/gpu"}

# プロビジョニングレイテンシ
histogram_quantile(0.99,
  rate(karpenter_provisioner_scheduling_duration_seconds_bucket[5m])
)

DCGM Exporterメトリクス

# DCGM Exporter ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: dcgm-exporter
  namespace: gpu-operator
spec:
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter
  endpoints:
    - port: metrics
      interval: 15s

アラートルール例

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: karpenter-gpu-alerts
  namespace: monitoring
spec:
  groups:
    - name: karpenter-gpu
      rules:
        # GPU NodePoolが制限の90%に到達
        - alert: GPUNodePoolNearLimit
          expr: |
            karpenter_nodepool_usage{nodepool="gpu-general", resource_type="nvidia.com/gpu"}
            /
            karpenter_nodepool_limit{nodepool="gpu-general", resource_type="nvidia.com/gpu"}
            > 0.9
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: 'GPU NodePoolがリソース制限に近づいています'

        # GPU使用率が低いノードの検知
        - alert: LowGPUUtilization
          expr: |
            avg_over_time(DCGM_FI_DEV_GPU_UTIL[30m]) < 10
          for: 1h
          labels:
            severity: info
          annotations:
            summary: 'GPU使用率が1時間にわたり10%未満です'

        # Karpenterプロビジョニング失敗
        - alert: KarpenterProvisioningFailed
          expr: |
            increase(karpenter_nodeclaims_terminated_total{reason="ProvisioningFailed"}[15m]) > 0
          labels:
            severity: critical
          annotations:
            summary: 'KarpenterがGPUノードのプロビジョニングに失敗しました'

10. 実践例:学習クラスタ

分散学習クラスタの構成

# PyTorch分散学習Job
apiVersion: batch/v1
kind: Job
metadata:
  name: distributed-training
  namespace: ml-training
spec:
  parallelism: 4
  completions: 4
  template:
    metadata:
      labels:
        app: distributed-training
      annotations:
        karpenter.sh/do-not-disrupt: 'true'
    spec:
      containers:
        - name: pytorch-trainer
          image: my-pytorch-training:v1
          command: ['torchrun']
          args:
            - '--nproc_per_node=1'
            - '--nnodes=4'
            - '--node_rank=$(JOB_COMPLETION_INDEX)'
            - '--master_addr=training-master'
            - '--master_port=29500'
            - 'train.py'
          env:
            - name: JOB_COMPLETION_INDEX
              valueFrom:
                fieldRef:
                  fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
          resources:
            requests:
              cpu: '8'
              memory: 32Gi
              nvidia.com/gpu: '1'
            limits:
              nvidia.com/gpu: '1'
          volumeMounts:
            - name: shared-data
              mountPath: /data
            - name: checkpoints
              mountPath: /checkpoints
      tolerations:
        - key: nvidia.com/gpu
          operator: Exists
          effect: NoSchedule
      nodeSelector:
        node-type: gpu-training
      restartPolicy: OnFailure
      volumes:
        - name: shared-data
          persistentVolumeClaim:
            claimName: training-data-pvc
        - name: checkpoints
          persistentVolumeClaim:
            claimName: checkpoint-pvc

11. 実践例:推論クラスタ

オートスケーリング推論サービス

# 推論Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: llm-inference
  namespace: ml-serving
spec:
  replicas: 2
  selector:
    matchLabels:
      app: llm-inference
  template:
    metadata:
      labels:
        app: llm-inference
    spec:
      containers:
        - name: vllm-server
          image: vllm/vllm-openai:latest
          args:
            - '--model'
            - 'meta-llama/Llama-3-8B'
            - '--tensor-parallel-size'
            - '1'
            - '--gpu-memory-utilization'
            - '0.9'
          ports:
            - containerPort: 8000
              name: http
          resources:
            requests:
              cpu: '4'
              memory: 16Gi
              nvidia.com/gpu: '1'
            limits:
              nvidia.com/gpu: '1'
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 60
            periodSeconds: 10
      tolerations:
        - key: nvidia.com/gpu
          operator: Exists
          effect: NoSchedule
      nodeSelector:
        node-type: gpu-inference
---
# HPA設定
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: llm-inference-hpa
  namespace: ml-serving
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: llm-inference
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Pods
      pods:
        metric:
          name: gpu_utilization
        target:
          type: AverageValue
          averageValue: '70'
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Pods
          value: 2
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Pods
          value: 1
          periodSeconds: 120

12. トラブルシューティングガイド

一般的なGPUノードの問題

# 1. GPUリソースが表示されない場合
kubectl describe node gpu-node | grep -A 10 "Allocatable"
# nvidia.com/gpuがない場合、GPU Operatorを確認

# 2. GPU Operator Podの状態確認
kubectl get pods -n gpu-operator
kubectl logs -n gpu-operator -l app=nvidia-driver-daemonset

# 3. Karpenterプロビジョニングログの確認
kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter \
  | grep -i "gpu\|nvidia\|instance-type"

# 4. NodeClaimの状態確認
kubectl get nodeclaims -o wide

# 5. Pending Podの原因分析
kubectl describe pod gpu-pod-name | grep -A 20 "Events"

よくある問題と解決方法

+---------------------------------------------+------------------------------------------+
| 問題                                        | 解決方法                                 |
+---------------------------------------------+------------------------------------------+
| GPUリソースがノードに表示されない           | GPU Operatorの再インストールまたは       |
|                                             | ドライバの確認                           |
| Spot GPUインスタンスが見つからない          | より多くのGPUインスタンスタイプとAZを追加|
| GPUノードプロビジョニングがタイムアウト     | EC2NodeClassのサブネット/SGタグを確認    |
| 学習中にノードが中断された                  | do-not-disruptアノテーションを追加       |
| GPUメモリ不足(OOM)                        | より大きなGPUインスタンスタイプを許可    |
| 不要なGPUノードが維持されている             | Consolidationポリシーと                  |
|                                             | consolidateAfter値を確認                 |
| 特定のGPUタイプのみプロビジョニングされる   | NodePool requirements範囲を拡張          |
+---------------------------------------------+------------------------------------------+

GPUメモリデバッグ

# ノード上で直接GPU状態を確認(デバッグPod使用)
kubectl run gpu-debug --rm -it \
  --image=nvidia/cuda:12.0.0-base-ubuntu22.04 \
  --overrides='{"spec":{"tolerations":[{"key":"nvidia.com/gpu","operator":"Exists","effect":"NoSchedule"}],"nodeSelector":{"node-type":"gpu"}}}' \
  --restart=Never \
  -- nvidia-smi

13. ベストプラクティスまとめ

GPUノード管理チェックリスト

+---+------------------------------------------------------------+
| # | ベストプラクティス                                         |
+---+------------------------------------------------------------+
| 1 | 推論と学習ワークロードのNodePoolを分離                     |
| 2 | GPU taintを設定して非GPUワークロードのスケジューリングを防止|
| 3 | 推論はSpot、学習はOn-Demandを使用                          |
| 4 | 長時間学習にdo-not-disruptアノテーションを適用             |
| 5 | チェックポイント戦略で学習進捗を保護                       |
| 6 | GPU Operatorでドライバ管理を自動化                         |
| 7 | DCGM ExporterでGPUメトリクスを収集                        |
| 8 | NodePool limitsでGPUコスト上限を設定                       |
| 9 | 複数のGPUインスタンスタイプを許可して可用性を確保          |
| 10| PDBで推論サービスの最小可用性を保証                        |
| 11| Disruption Budgetで学習時間中の中断をブロック              |
| 12| HPAとKarpenterを連携して自動スケーリングを実現            |
+---+------------------------------------------------------------+

コスト最適化戦略まとめ

戦略1:階層型NodePool
  - Spot GPU(高い重み) -> On-Demand GPU(低い重み)
  - 推論ワークロードに最適

戦略2:インスタンス多角化
  - 複数のGPUファミリー(g4dn、g5、g6)を許可
  - 複数のインスタンスサイズを許可
  - Spot可用性を最大化

戦略3:自動スケールダウン
  - WhenEmpty consolidationで空のGPUノードを即座に削除
  - consolidateAfterを短く設定(推論)
  - 学習ノードはより長い待機時間を設定

戦略4:適切なリソース制限
  - NodePool limitsで最大GPU数を制限
  - 予期しないコスト暴走を防止
  - チーム/プロジェクト別の割当量を管理

Karpenter + GPU最終アーキテクチャ図

+---------------------------------------------------------------------+
|                        EKS Cluster                                  |
|                                                                     |
|  +-------------------+  +-------------------+  +-----------------+  |
|  | NodePool:         |  | NodePool:         |  | NodePool:       |  |
|  | gpu-inference     |  | gpu-training      |  | multi-arch      |  |
|  | (Spot, weight:60) |  | (OD, weight:90)   |  | (Mixed, w:50)   |  |
|  +--------+----------+  +--------+----------+  +--------+--------+  |
|           |                      |                       |           |
|  +--------v----------+  +--------v----------+  +--------v--------+  |
|  | EC2NodeClass:     |  | EC2NodeClass:     |  | EC2NodeClass:   |  |
|  | gpu-optimized     |  | gpu-training      |  | default         |  |
|  | (200GB, gp3)      |  | (500GB, gp3)      |  | (100GB, gp3)    |  |
|  +-------------------+  +-------------------+  +-----------------+  |
|                                                                     |
|  +-------------------+  +-------------------+                       |
|  | GPU Operator      |  | Prometheus +      |                       |
|  | (NVIDIAドライバ、 |  | Grafana           |                       |
|  |  Device Plugin、  |  | (Karpenter +      |                       |
|  |  DCGM Exporter)   |  |  DCGMメトリクス)  |                       |
|  +-------------------+  +-------------------+                       |
+---------------------------------------------------------------------+