Karpenter

Karpenter 完全ガイド: Kubernetes ノードの自動プロビジョニング

目次

  1. はじめに
  2. Karpenter の概要と背景
  3. アーキテクチャの全体像
  4. コアコンセプト
  5. NodePool の設計と設定
  6. EC2NodeClass の詳細設定
  7. スケジューリングとプロビジョニングの仕組み
  8. Consolidation(統合)と最適化
  9. Drift Detection と自動更新
  10. Disruption(中断)管理
  11. スポットインスタンスの活用
  12. マルチアーキテクチャ対応(ARM/AMD)
  13. セキュリティとIAM設定
  14. モニタリングとオブザーバビリティ
  15. Cluster Autoscaler との比較
  16. 本番環境でのベストプラクティス
  17. トラブルシューティング
  18. 高度な設定パターン
  19. 移行ガイド
  20. まとめと今後の展望

1. はじめに

Kubernetes クラスターの運用において、ノードの管理は最も重要かつ複雑なタスクの一つである。ワークロードの需要に応じてノードを適切にスケーリングし、コストを最適化しながら高い可用性を維持することは、多くの運用チームにとって大きな課題となっている。

従来、AWS 上の Kubernetes クラスターでは Cluster Autoscaler (CA) がノードの自動スケーリングに使用されてきた。しかし、CA は Auto Scaling Group (ASG) に依存するアーキテクチャであるため、インスタンスタイプの柔軟な選択や迅速なスケーリングに制約があった。

Karpenter は、これらの課題を根本的に解決するために AWS が開発したオープンソースのノードプロビジョニングツールである。Kubernetes ネイティブな設計思想に基づき、Pod の要件に応じて最適なノードを直接プロビジョニングすることで、従来のアプローチでは実現できなかったレベルの柔軟性、効率性、および速度を提供する。

本記事では、Karpenter のアーキテクチャから具体的な設定例、本番環境でのベストプラクティスまで、包括的に解説する。


2. Karpenter の概要と背景

2.1 Karpenter とは

Karpenter は、Kubernetes クラスター向けのオープンソースノードプロビジョニングプロジェクトである。2021年に AWS によって開発が開始され、2023年に Cloud Native Computing Foundation (CNCF) のサンドボックスプロジェクトとして採択された。現在はCNCFインキュベーションプロジェクトとして活発に開発が進められている。

Karpenter の主な特徴は以下の通りである:

  • 直接的なノードプロビジョニング: ASG を介さず、EC2 API を直接呼び出してインスタンスを起動する
  • Group-less アーキテクチャ: ノードグループの概念を排除し、Pod の要件に基づいて個別にノードを最適化する
  • 高速スケーリング: ASG の制約を受けないため、秒単位でのスケーリングが可能
  • インテリジェントな統合: 使用率の低いノードを自動的に検出し、ワークロードを統合してコストを削減する
  • Drift Detection: AMI やセキュリティグループなどの設定変更を自動的に検出し、ノードを更新する

2.2 開発の経緯

Karpenter の開発は、以下のような背景から始まった:

Phase 1: 問題認識(2020年以前)

  • Cluster Autoscaler の ASG 依存によるスケーリング遅延
  • ノードグループごとにインスタンスタイプを固定する必要がある柔軟性の欠如
  • ビンパッキング効率の低さによるコスト増大

Phase 2: 設計と開発(2021年)

  • AWS が Karpenter プロジェクトを公開
  • Kubernetes のスケジューラーと連携する新しいアプローチの採用
  • v0.1.0 のリリース

Phase 3: CNCF への移行(2023年)

  • CNCF サンドボックスプロジェクトとして採択
  • マルチクラウド対応の基盤設計
  • API の安定化(v1beta1 → v1)

Phase 4: 成熟期(2024年〜現在)

  • v1.0 のリリースとAPI の GA(General Availability)
  • 本番環境での広範な採用
  • エコシステムの拡大

2.3 プロジェクト構造

Karpenter は以下の2つの主要リポジトリで構成されている:

リポジトリ説明
kubernetes-sigs/karpenterクラウドプロバイダー非依存のコアロジック
aws/karpenter-provider-awsAWS 固有の実装(EC2NodeClass など)

この分離により、将来的に Azure や GCP など他のクラウドプロバイダーへの対応が容易になっている。

2.4 バージョン体系

Karpenter のバージョンは以下の形式で管理されている:

  • Core API: v1(GA)- NodePool, NodeClaim
  • AWS Provider API: v1(GA)- EC2NodeClass
  • Helm Chart: セマンティックバージョニングに従ったリリース(例: 1.1.0

v1 API は後方互換性が保証されており、本番環境での使用に推奨される。


3. アーキテクチャの全体像

3.1 全体アーキテクチャ

Karpenter のアーキテクチャは、以下の主要コンポーネントで構成されている:

┌─────────────────────────────────────────────────────────────────┐
│                    Kubernetes Control Plane                       │
│  ┌───────────────┐  ┌──────────────┐  ┌─────────────────────┐   │
│  │  kube-scheduler│  │  kube-apiserver│  │  etcd              │   │
│  └───────┬───────┘  └──────┬───────┘  └─────────────────────┘   │
│          │                  │                                     │
│  ┌───────┴──────────────────┴───────────────────────────────┐   │
│  │              Karpenter Controller                         │   │
│  │  ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │   │
│  │  │ Provisioner  │ │ Disruption   │ │ Drift Detector    │ │   │
│  │  │ Controller   │ │ Controller   │ │                   │ │   │
│  │  └──────┬──────┘ └──────┬───────┘ └───────┬───────────┘ │   │
│  │         │               │                  │              │   │
│  │  ┌──────┴──────────────┴──────────────────┴───────────┐  │   │
│  │  │              Cloud Provider Interface                │  │   │
│  │  │         (AWS EC2 Fleet API / Launch API)            │  │   │
│  │  └────────────────────────┬────────────────────────────┘  │   │
│  └───────────────────────────┼───────────────────────────────┘   │
└──────────────────────────────┼───────────────────────────────────┘
                               │
                    ┌──────────┴──────────┐
                    │     AWS EC2 API      │
                    │  ┌────────────────┐  │
                    │  │ CreateFleet    │  │
                    │  │ RunInstances   │  │
                    │  │ TerminateInst. │  │
                    │  └────────────────┘  │
                    └──────────┬──────────┘
                               │
              ┌────────────────┼────────────────┐
              │                │                 │
        ┌─────┴─────┐  ┌─────┴─────┐  ┌───────┴───────┐
        │  Worker    │  │  Worker    │  │   Worker      │
        │  Node 1    │  │  Node 2    │  │   Node N      │
        │  (m5.xlarge)│  │ (c6g.2xl) │  │  (r5.large)  │
        └───────────┘  └───────────┘  └───────────────┘

3.2 コントローラーの詳細

Karpenter は、Kubernetes のコントローラーパターンに従って設計されており、複数のコントローラーが協調して動作する:

3.2.1 Provisioning Controller

プロビジョニングコントローラーは、Karpenter の中核を成すコンポーネントである。以下の処理フローで動作する:

  1. Pending Pod の監視: kube-scheduler がスケジュールできなかった Pod を検出
  2. 要件の分析: Pod のリソース要求、nodeSelector、tolerations、affinity などを分析
  3. シミュレーション: 仮想的なノードを作成し、Pod のスケジューリングをシミュレート
  4. インスタンスタイプの選択: 要件を満たす最も効率的なインスタンスタイプを選択
  5. ノードの起動: Cloud Provider API を呼び出してノードを起動
Pending Pod 検出 → 要件分析 → ビンパッキング → インスタンス選択 → ノード起動
     ↑                                                              │
     └──── Pod がスケジュールされるまで繰り返し ──────────────────────┘

3.2.2 Disruption Controller

Disruption Controller は、不要になったノードや最適化が可能なノードを管理する:

  • Consolidation(統合): 使用率の低いノードを検出し、ワークロードを他のノードに移動
  • Expiration(有効期限): 設定された期限を超えたノードを入れ替え
  • Drift(ドリフト): 設定変更を検出し、ノードを最新の設定に更新
  • Emptiness(空ノード): ワークロードが存在しないノードを削除

3.2.3 Cloud Provider Interface

クラウドプロバイダーインターフェースは、Karpenter のコアロジックと特定のクラウドプロバイダーの実装を分離する抽象化レイヤーである:

// Cloud Provider Interface(概念的な構造)
type CloudProvider interface {
    Create(ctx context.Context, nodeClaim *v1.NodeClaim) (*v1.NodeClaim, error)
    Delete(ctx context.Context, nodeClaim *v1.NodeClaim) error
    Get(ctx context.Context, providerID string) (*v1.NodeClaim, error)
    List(ctx context.Context) ([]*v1.NodeClaim, error)
    GetInstanceTypes(ctx context.Context, nodePool *v1.NodePool) ([]*InstanceType, error)
}

3.3 データフロー

Karpenter のデータフローは以下のように進行する:

1. ユーザーが Deployment をデプロイ
        ↓
2. kube-scheduler が Pod をスケジュール試行
        ↓
3. スケジュール不可の場合、Pod は Pending 状態に
        ↓
4. Karpenter が Pending Pod を検出
        ↓
5. NodePool の制約に基づいてインスタンスタイプを決定
        ↓
6. EC2NodeClass の設定に基づいてインスタンスを構成
        ↓
7. EC2 Fleet API でインスタンスを起動
        ↓
8. NodeClaim リソースが作成される
        ↓
9. ノードが Ready になると Pod がスケジュールされる
        ↓
10. 継続的に Consolidation と Drift Detection が実行される

3.4 Kubernetes リソースモデル

Karpenter は以下のカスタムリソース(CRD)を定義する:

リソースAPI グループ説明
NodePoolkarpenter.sh/v1ノードプロビジョニングの制約とポリシー
NodeClaimkarpenter.sh/v1個別のノードリクエストとライフサイクル
EC2NodeClasskarpenter.k8s.aws/v1AWS 固有のノード設定

これらのリソース間の関係は以下の通りである:

NodePool ──参照──→ EC2NodeClass
    │
    └──作成──→ NodeClaim ──対応──→ EC2 Instance ──対応──→ Kubernetes Node

4. コアコンセプト

4.1 NodePool

NodePool は、Karpenter がノードをプロビジョニングする際の制約とポリシーを定義するリソースである。以下の要素を含む:

  • テンプレート: ノードのラベル、taints、要件(インスタンスタイプ、AZ、アーキテクチャなど)
  • 制約: リソース制限、重みによる優先順位
  • Disruption ポリシー: ノードの統合や有効期限の設定

基本的な NodePool の例:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: general-purpose
spec:
  template:
    metadata:
      labels:
        team: platform
        environment: production
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand", "spot"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["4"]
  limits:
    cpu: "1000"
    memory: 1000Gi
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
  weight: 50

4.2 NodeClaim

NodeClaim は、Karpenter が作成する個別のノードリクエストを表すリソースである。NodePool のテンプレートに基づいて自動的に作成され、EC2 インスタンスのライフサイクルを管理する。

NodeClaim のステータスには以下の情報が含まれる:

  • プロバイダーID(EC2 インスタンス ID)
  • 割り当てられたインスタンスタイプ
  • アベイラビリティゾーン
  • キャパシティタイプ(On-Demand / Spot)
  • リソースの割り当て量
# NodeClaim のステータス例(自動生成される)
apiVersion: karpenter.sh/v1
kind: NodeClaim
metadata:
  name: general-purpose-abc123
  labels:
    karpenter.sh/nodepool: general-purpose
spec:
  nodeClassRef:
    group: karpenter.k8s.aws
    kind: EC2NodeClass
    name: default
  requirements:
    - key: kubernetes.io/arch
      operator: In
      values: ["amd64"]
    - key: node.kubernetes.io/instance-type
      operator: In
      values: ["m5.xlarge"]
status:
  providerID: "aws:///us-west-2a/i-0123456789abcdef0"
  instanceType: m5.xlarge
  zone: us-west-2a
  capacity:
    cpu: "4"
    memory: "16Gi"
    ephemeral-storage: "100Gi"
    pods: "58"
  allocatable:
    cpu: "3920m"
    memory: "15168Mi"
    ephemeral-storage: "95Gi"
    pods: "58"
  conditions:
    - type: Ready
      status: "True"

4.3 EC2NodeClass

EC2NodeClass は、AWS EC2 インスタンスの具体的な設定を定義するリソースである。AMI、セキュリティグループ、サブネット、IAM ロールなどの AWS 固有の設定を含む。

4.4 Requirements(要件)

Requirements は、Karpenter がノードを選択する際の条件を定義する仕組みである。Kubernetes の Well-Known Labels と Karpenter 固有のラベルを使用して、詳細な条件を指定できる。

主要な Requirement キー:

キー説明
kubernetes.io/archCPU アーキテクチャamd64, arm64
kubernetes.io/osOSlinux
node.kubernetes.io/instance-typeインスタンスタイプm5.xlarge
topology.kubernetes.io/zoneAZus-west-2a
karpenter.sh/capacity-typeキャパシティタイプon-demand, spot
karpenter.k8s.aws/instance-categoryインスタンスカテゴリc, m, r, t
karpenter.k8s.aws/instance-familyインスタンスファミリーm5, c6g, r6i
karpenter.k8s.aws/instance-sizeインスタンスサイズlarge, xlarge
karpenter.k8s.aws/instance-generationインスタンス世代5, 6, 7
karpenter.k8s.aws/instance-cpuvCPU 数4, 8, 16
karpenter.k8s.aws/instance-memoryメモリ (MiB)8192, 16384
karpenter.k8s.aws/instance-gpu-countGPU 数1, 4
karpenter.k8s.aws/instance-gpu-manufacturerGPU メーカーnvidia

4.5 ビンパッキングアルゴリズム

Karpenter のビンパッキングアルゴリズムは、Pending Pod をできるだけ少ないノードに効率的に配置することを目的としている。以下のステップで動作する:

  1. Pod のグループ化: 同じ要件を持つ Pod をグループ化
  2. 互換インスタンスタイプの列挙: 要件を満たすすべてのインスタンスタイプを列挙
  3. コスト最適化: 最小コストでPod を配置できる組み合わせを計算
  4. 制約の検証: NodePool の limits やその他の制約を検証
  5. 最終選択: 最適なインスタンスタイプとAZの組み合わせを決定
例: 4つの Pod(各 1 CPU, 2 GiB メモリ)が Pending の場合

方法A: 4つの m5.large(各 2 CPU, 8 GiB)→ コスト: $0.384/h
方法B: 1つの m5.xlarge(4 CPU, 16 GiB)→ コスト: $0.192/h  ← Karpenter が選択
方法C: 1つの m5.2xlarge(8 CPU, 32 GiB)→ コスト: $0.384/h

Karpenter は方法B を選択(最小コストで全 Pod を配置可能)

5. NodePool の設計と設定

5.1 NodePool の構造

NodePool は Karpenter の最も重要な設定リソースである。以下に各フィールドの詳細な説明を行う。

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: <名前>
spec:
  # ノードテンプレート
  template:
    metadata:
      labels: {}        # ノードに付与するラベル
      annotations: {}   # ノードに付与するアノテーション
    spec:
      nodeClassRef:     # EC2NodeClass への参照
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: <名前>
      requirements: []  # インスタンス要件
      taints: []        # ノードに付与する Taint
      startupTaints: [] # 起動時のみ付与する Taint
      expireAfter: 720h # ノードの有効期限
      terminationGracePeriod: 24h  # 終了時の猶予期間

  # リソース制限
  limits:
    cpu: "1000"
    memory: 1000Gi

  # Disruption ポリシー
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
    budgets: []         # Disruption バジェット

  # 優先度の重み(0-100)
  weight: 50

5.2 マルチ NodePool 設計

本番環境では、ワークロードの種類に応じて複数の NodePool を設計することが推奨される:

パターン1: ワークロードタイプ別

# 一般ワークロード用 NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: general
spec:
  template:
    metadata:
      labels:
        workload-type: general
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["m"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["5"]
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ["large", "xlarge", "2xlarge", "4xlarge"]
  limits:
    cpu: "500"
    memory: 500Gi
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
  weight: 50

---
# コンピュート集約型ワークロード用 NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: compute-intensive
spec:
  template:
    metadata:
      labels:
        workload-type: compute
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["5"]
      taints:
        - key: workload-type
          value: compute
          effect: NoSchedule
  limits:
    cpu: "200"
    memory: 200Gi
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 5m
  weight: 30

---
# メモリ集約型ワークロード用 NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: memory-intensive
spec:
  template:
    metadata:
      labels:
        workload-type: memory
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: high-memory
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["5"]
      taints:
        - key: workload-type
          value: memory
          effect: NoSchedule
  limits:
    cpu: "200"
    memory: 2000Gi
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 5m
  weight: 30

パターン2: コスト最適化(On-Demand + Spot)

# Spot インスタンス優先 NodePool(高い weight)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: spot-preferred
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["4"]
  limits:
    cpu: "500"
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 30s
  weight: 80  # 高い優先度

---
# On-Demand フォールバック NodePool(低い weight)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: on-demand-fallback
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["4"]
  limits:
    cpu: "200"
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
  weight: 20  # 低い優先度(Spot が使えない場合のフォールバック)

5.3 Weight(重み)によるNodePool の優先順位

NodePool の weight フィールド(0-100)は、複数の NodePool が Pod の要件を満たす場合の優先順位を決定する。値が大きいほど優先度が高い。

Pod の要件を満たす NodePool:
  - spot-preferred (weight: 80)     → 最優先で使用
  - on-demand-fallback (weight: 20) → Spot が不可の場合に使用
  - general (weight: 50)            → 中間の優先度

5.4 Limits(制限)

NodePool の limits は、その NodePool が管理するノードの合計リソースの上限を定義する。これにより、予期しないコスト増大を防止できる。

spec:
  limits:
    cpu: "1000"       # 全ノードの合計 CPU が 1000 コアを超えない
    memory: 1000Gi    # 全ノードの合計メモリが 1000 GiB を超えない

注意事項:

  • limits はノードの allocatable リソースの合計で計算される
  • 制限に達すると、新しいノードはプロビジョニングされない
  • 制限に達した場合、Pod は Pending のままとなる

5.5 StartupTaints

startupTaints は、ノードの起動中にのみ付与される Taint であり、ノードの初期化が完了するまで Pod のスケジューリングを防ぐために使用される。

spec:
  template:
    spec:
      startupTaints:
        - key: node.kubernetes.io/not-ready
          effect: NoSchedule
        - key: custom-init/not-ready
          effect: NoSchedule

典型的なユースケース:

  • カスタム初期化スクリプトの完了待ち
  • セキュリティエージェントのインストール完了待ち
  • ネットワーク設定の完了待ち

DaemonSet やカスタムコントローラーが初期化完了後に Taint を除去する設計にする。


6. EC2NodeClass の詳細設定

6.1 EC2NodeClass の構造

EC2NodeClass は、AWS EC2 インスタンスの具体的な構成を定義する:

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default
spec:
  amiSelectorTerms:
    - alias: al2023@latest
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster
        Type: private
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster
  role: KarpenterNodeRole-my-cluster
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 100Gi
        volumeType: gp3
        iops: 3000
        throughput: 125
        encrypted: true
        deleteOnTermination: true
  metadataOptions:
    httpEndpoint: enabled
    httpProtocolIPv6: disabled
    httpPutResponseHopLimit: 2
    httpTokens: required
  userData: |
    #!/bin/bash
    echo "Custom initialization script"
    yum install -y amazon-ssm-agent
    systemctl enable amazon-ssm-agent
    systemctl start amazon-ssm-agent
  tags:
    Environment: production
    Team: platform
    ManagedBy: karpenter

6.2 AMI の選択と管理

6.2.1 エイリアスによる選択(推奨)

spec:
  amiSelectorTerms:
    - alias: al2023@latest        # Amazon Linux 2023 最新版
    # - alias: al2@latest          # Amazon Linux 2 最新版
    # - alias: bottlerocket@latest # Bottlerocket 最新版
    # - alias: windows2022@latest  # Windows Server 2022 最新版

エイリアスのバージョン指定も可能:

spec:
  amiSelectorTerms:
    - alias: al2023@v20240312    # 特定バージョンを固定

6.2.2 タグによる選択

spec:
  amiSelectorTerms:
    - tags:
        Environment: production
        AMIType: eks-optimized

6.2.3 AMI ID による直接指定

spec:
  amiSelectorTerms:
    - id: ami-0123456789abcdef0
    - id: ami-0987654321fedcba0

6.3 サブネット設定

# プライベートサブネットの選択
spec:
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster
        kubernetes.io/role/internal-elb: "1"

# 特定のサブネット ID を指定
spec:
  subnetSelectorTerms:
    - id: subnet-0123456789abcdef0
    - id: subnet-0987654321fedcba0

6.4 ブロックデバイスマッピング

# 一般的なワークロード
spec:
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 100Gi
        volumeType: gp3
        iops: 3000
        throughput: 125
        encrypted: true
        kmsKeyID: arn:aws:kms:us-west-2:111122223333:key/xxxx
        deleteOnTermination: true

---
# I/O 集約型ワークロード
spec:
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 200Gi
        volumeType: gp3
        iops: 10000
        throughput: 500
        encrypted: true
        deleteOnTermination: true
    - deviceName: /dev/xvdb
      ebs:
        volumeSize: 500Gi
        volumeType: io2
        iops: 20000
        encrypted: true
        deleteOnTermination: true

6.5 メタデータオプション(IMDSv2)

spec:
  metadataOptions:
    httpEndpoint: enabled
    httpProtocolIPv6: disabled
    httpPutResponseHopLimit: 2    # コンテナからのアクセスには 2 が必要
    httpTokens: required          # IMDSv2 を必須に

6.6 ユーザーデータ

Amazon Linux 2023 向け

spec:
  userData: |
    #!/bin/bash
    set -euo pipefail
    yum install -y amazon-ssm-agent
    systemctl enable amazon-ssm-agent
    systemctl start amazon-ssm-agent
    cat <<'SYSCTL' >> /etc/sysctl.d/99-custom.conf
    net.core.somaxconn = 65535
    net.ipv4.tcp_max_syn_backlog = 65535
    vm.max_map_count = 262144
    SYSCTL
    sysctl --system

Bottlerocket 向け

spec:
  amiSelectorTerms:
    - alias: bottlerocket@latest
  userData: |
    [settings.kubernetes]
    max-pods = 110
    [settings.kernel.sysctl]
    "net.core.somaxconn" = "65535"
    "vm.max_map_count" = "262144"

7. スケジューリングとプロビジョニングの仕組み

7.1 プロビジョニングの詳細フロー

┌─────────────────────────────────────────────────────┐
│              プロビジョニングフロー                     │
├─────────────────────────────────────────────────────┤
│  1. Pending Pod の収集                               │
│     └→ kube-apiserver から Pending 状態の Pod を取得  │
│  2. Pod のフィルタリング                              │
│     ├→ DaemonSet Pod を除外                          │
│     └→ 既にノードが割り当てられた Pod を除外           │
│  3. NodePool の評価                                  │
│     ├→ 各 NodePool の requirements と Pod の要件を照合│
│     ├→ limits に余裕があるか確認                      │
│     └→ weight に基づいて NodePool を優先順位付け       │
│  4. インスタンスタイプの選択                          │
│     ├→ requirements を満たすインスタンスタイプを列挙   │
│     ├→ 各インスタンスタイプの価格情報を取得            │
│     └→ ビンパッキングで最適な組み合わせを計算          │
│  5. ノードの作成                                     │
│     ├→ NodeClaim リソースを作成                       │
│     ├→ EC2 CreateFleet API を呼び出し                 │
│     └→ インスタンスの起動を待機                       │
│  6. ノードの初期化                                   │
│     ├→ kubelet がノードを登録                         │
│     ├→ ノードが Ready になるまで待機                   │
│     └→ startupTaints が除去される                     │
│  7. Pod のスケジューリング                            │
│     └→ kube-scheduler が Pod をノードに配置           │
└─────────────────────────────────────────────────────┘

7.2 バッチング

Karpenter はプロビジョニングの効率を上げるため、短時間に発生する複数の Pending Pod をバッチ処理する:

時間軸:
0s    - Pod A が Pending になる → バッチウィンドウ開始
0.5s  - Pod B が Pending になる → バッチに追加
0.8s  - Pod C が Pending になる → バッチに追加
1.0s  - バッチウィンドウ終了 → Pod A, B, C をまとめてプロビジョニング

7.3 Pod Affinity/Anti-Affinity の処理

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values: ["web-app"]
              topologyKey: kubernetes.io/hostname
      containers:
        - name: web
          image: nginx:latest
          resources:
            requests:
              cpu: "500m"
              memory: "512Mi"

この場合、Karpenter は3つの Pod それぞれに別のノードを作成する必要があることを理解し、3つのノードをプロビジョニングする。

7.4 Topology Spread Constraints の処理

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 6
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: web-app
      containers:
        - name: web
          image: nginx:latest
          resources:
            requests:
              cpu: "500m"
              memory: "512Mi"

結果:

us-west-2a: 2 Pod(ノード1つ)
us-west-2b: 2 Pod(ノード1つ)
us-west-2c: 2 Pod(ノード1つ)

7.5 DaemonSet の考慮

ノードに必要なリソース = Pod のリソース要求 + DaemonSet のリソース要求 + kubelet 予約

例:
  Pod 要求: 2 CPU, 4 GiB
  DaemonSet (kube-proxy): 0.1 CPU, 0.1 GiB
  DaemonSet (aws-node): 0.025 CPU, 0.1 GiB
  DaemonSet (datadog-agent): 0.2 CPU, 0.5 GiB
  kubelet 予約: 0.08 CPU, 0.3 GiB
  ─────────────────────────────────
  合計: 2.405 CPU, 5.0 GiB → m5.xlarge (4 CPU, 16 GiB) を選択

7.6 GPU ワークロードのプロビジョニング

# GPU NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-workloads
spec:
  template:
    metadata:
      labels:
        workload-type: gpu
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: gpu-nodeclass
      requirements:
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["g", "p"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["4"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
      taints:
        - key: nvidia.com/gpu
          value: "true"
          effect: NoSchedule
  limits:
    cpu: "100"
    memory: 500Gi
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 5m

8. Consolidation(統合)と最適化

8.1 Consolidation の概要

Consolidation は、Karpenter の最も強力な機能の一つである。クラスターのリソース使用効率を継続的に監視し、コストを削減するためにノードを統合する。

8.2 Consolidation ポリシー

WhenEmpty

spec:
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 30s

WhenEmptyOrUnderutilized(推奨)

spec:
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m

8.3 Consolidation のアルゴリズム

1. 空ノード削除 (Empty Node Deletion)
   └→ ワークロードがないノードを即座に削除

2. マルチノード統合 (Multi-Node Consolidation)
   └→ 複数のノードのワークロードを他の既存ノードに移動し、元のノードを削除

3. シングルノード置換 (Single Node Replacement)
   └→ 1つのノードを、より小さい(安い)インスタンスタイプに置き換える

具体例:

Before Consolidation:
  Node A (m5.2xlarge - $0.384/h): CPU 使用率 20%
  Node B (m5.2xlarge - $0.384/h): CPU 使用率 30%
  Node C (m5.xlarge  - $0.192/h): CPU 使用率 90%

After Consolidation:
  Node A の Pod を Node B と Node C に移動 → Node A を削除
  コスト削減: $0.384/h → 月間約 $280 の節約

8.4 Disruption Budgets

spec:
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
    budgets:
      - nodes: "10%"
      - nodes: "0"
        schedule: "0 9 * * 1-5"
        duration: 8h
      - nodes: "0"
        schedule: "0 0 25 12 *"
        duration: 24h

8.5 コスト最適化の数値例

Consolidation なし(手動管理、ノードグループ固定):
  合計: $4.376/h = $3,190/月

Karpenter + Consolidation:
  合計: $1.744/h = $1,273/月

  コスト削減: 約60%($1,917/月の節約)

9. Drift Detection と自動更新

9.1 Drift Detection の概要

Drift Detection は、ノードの現在の設定が NodePool や EC2NodeClass の定義と一致しているかを継続的に監視する機能である。

9.2 Drift が検出されるケース

変更項目検出方法
AMI の変更EC2NodeClass の amiSelectorTerms に新しい AMI が一致
セキュリティグループの変更securityGroupSelectorTerms の結果が変更
サブネットの変更subnetSelectorTerms の結果が変更
NodePool の requirements 変更ノードの属性が新しい requirements に不一致
ブロックデバイスの変更blockDeviceMappings の設定変更
メタデータオプションの変更metadataOptions の設定変更
ユーザーデータの変更userData の内容変更

9.3 AMI の自動更新フロー

1. AWS が新しい EKS Optimized AMI をリリース
    ↓
2. Karpenter が定期的に AMI を確認(約15分間隔)
    ↓
3. 新しい AMI が検出される
    ↓
4. 既存ノードに "Drifted" 条件が追加される
    ↓
5. Disruption Controller がドリフトしたノードを処理
    ↓
6. ワークロードを他のノードに移動(cordon → drain)
    ↓
7. 新しい AMI でノードを起動
    ↓
8. 古いノードを削除

9.4 ローリングアップデートの戦略

spec:
  disruption:
    budgets:
      - nodes: "10%"
      - nodes: "0"
        schedule: "0 14 * * 1-5"
        duration: 9h
      - nodes: "25%"
        schedule: "0 0 * * 6"
        duration: 48h

10. Disruption(中断)管理

10.1 Disruption の種類

種類優先順位説明
Expiration1expireAfter で設定した期限を超えたノード
Drift2設定変更により現在の構成がスペックと不一致
Consolidation (Empty)3ワークロードが存在しないノード
Consolidation (Underutilized)4リソース使用率が低いノード

10.2 Disruption の防止

# Pod レベル
apiVersion: v1
kind: Pod
metadata:
  annotations:
    karpenter.sh/do-not-disrupt: "true"

# PodDisruptionBudget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: web-app

10.3 terminationGracePeriod

spec:
  template:
    spec:
      terminationGracePeriod: 48h

11. スポットインスタンスの活用

11.1 Spot インスタンスの設定

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: spot-workloads
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["4"]
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ["large", "xlarge", "2xlarge"]
  limits:
    cpu: "500"
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 30s

11.2 Spot 中断のハンドリング

1. AWS が Spot 中断通知を送信(2分前)
    ↓
2. Karpenter が通知を検出(SQS キュー経由)
    ↓
3. ノードを Cordon → Pod を Drain → 新しいノードをプロビジョニング

11.3 SQS キューの設定

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": ["events.amazonaws.com", "sqs.amazonaws.com"]
      },
      "Action": "sqs:SendMessage",
      "Resource": "arn:aws:sqs:us-west-2:111122223333:karpenter-events"
    }
  ]
}

11.4 Spot のベストプラクティス

  1. インスタンスタイプの多様化: 最低15種類以上のインスタンスタイプを許可する
  2. マルチ AZ: 複数の AZ にまたがってプロビジョニングする
  3. フォールバック戦略: On-Demand の NodePool をフォールバックとして用意する
  4. 適切なワークロードの選択: ステートレスで中断耐性のあるワークロードを配置する

12. マルチアーキテクチャ対応(ARM/AMD)

12.1 マルチアーキテクチャ NodePool

# ARM 優先 NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: arm-preferred
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["arm64"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["6"]
  weight: 80

---
# x86 フォールバック NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: amd-fallback
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["5"]
  weight: 20

12.2 コスト比較

4 vCPU, 16 GiB Memory:
  m5.xlarge  (x86):   $0.192/h
  m6g.xlarge (ARM):   $0.154/h  → 20% 削減
  m7g.xlarge (ARM):   $0.163/h  → 15% 削減

ARM + Spot の組み合わせ: On-Demand x86 比で最大 73% 削減

13. セキュリティとIAM設定

13.1 IAM アーキテクチャ

1. Karpenter Controller Role
   └→ IRSA で設定、EC2/SQS/SSM/EKS API へのアクセス

2. Karpenter Node Role
   └→ EC2 インスタンスプロファイル、ECR/EKS API へのアクセス

13.2 Karpenter Controller IAM ポリシー

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowEC2Operations",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateFleet",
        "ec2:CreateLaunchTemplate",
        "ec2:CreateTags",
        "ec2:DeleteLaunchTemplate",
        "ec2:DescribeAvailabilityZones",
        "ec2:DescribeImages",
        "ec2:DescribeInstances",
        "ec2:DescribeInstanceTypeOfferings",
        "ec2:DescribeInstanceTypes",
        "ec2:DescribeLaunchTemplates",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSpotPriceHistory",
        "ec2:DescribeSubnets",
        "ec2:RunInstances",
        "ec2:TerminateInstances"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "us-west-2"
        }
      }
    },
    {
      "Sid": "AllowSQSOperations",
      "Effect": "Allow",
      "Action": [
        "sqs:DeleteMessage",
        "sqs:GetQueueAttributes",
        "sqs:GetQueueUrl",
        "sqs:ReceiveMessage"
      ],
      "Resource": "arn:aws:sqs:us-west-2:111122223333:karpenter-*"
    },
    {
      "Sid": "AllowSSMOperations",
      "Effect": "Allow",
      "Action": ["ssm:GetParameter"],
      "Resource": "arn:aws:ssm:us-west-2::parameter/aws/service/*"
    },
    {
      "Sid": "AllowEKSOperations",
      "Effect": "Allow",
      "Action": ["eks:DescribeCluster"],
      "Resource": "arn:aws:eks:us-west-2:111122223333:cluster/my-cluster"
    },
    {
      "Sid": "AllowIAMPassRole",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::111122223333:role/KarpenterNodeRole-*",
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": "ec2.amazonaws.com"
        }
      }
    },
    {
      "Sid": "AllowPricing",
      "Effect": "Allow",
      "Action": ["pricing:GetProducts"],
      "Resource": "*"
    }
  ]
}

13.3 セキュリティベストプラクティス

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: secure-nodes
spec:
  amiSelectorTerms:
    - alias: al2023@latest
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster
        Type: private
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster
  role: KarpenterNodeRole-my-cluster
  metadataOptions:
    httpEndpoint: enabled
    httpProtocolIPv6: disabled
    httpPutResponseHopLimit: 2
    httpTokens: required
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 100Gi
        volumeType: gp3
        encrypted: true
        kmsKeyID: arn:aws:kms:us-west-2:111122223333:key/mrk-xxx
        deleteOnTermination: true

14. モニタリングとオブザーバビリティ

14.1 Prometheus メトリクス

メトリクス説明
karpenter_nodeclaims_created_total作成された NodeClaim の総数
karpenter_nodeclaims_terminated_total終了された NodeClaim の総数
karpenter_nodes_totalKarpenter 管理ノードの総数
karpenter_nodes_allocatableノードの allocatable リソース
karpenter_disruption_actions_performed_total実行された disruption アクションの数
karpenter_cloudprovider_duration_secondsAPI 呼び出しの応答時間
karpenter_cloudprovider_errors_totalAPI エラーの総数
karpenter_cloudprovider_instance_type_price_estimateインスタンスタイプの価格推定

14.2 Grafana ダッシュボード

# Karpenter 管理ノード数の推移
sum(karpenter_nodes_total) by (nodepool)

# インスタンスタイプ別のノード数
count(karpenter_nodes_total) by (instance_type)

# ノード作成のレイテンシ(P99)
histogram_quantile(0.99,
  sum(rate(karpenter_provisioner_scheduling_duration_seconds_bucket[5m])) by (le)
)

# コスト推定
sum(karpenter_cloudprovider_instance_type_price_estimate) by (nodepool)

14.3 アラート設定例

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: karpenter-alerts
  namespace: monitoring
spec:
  groups:
    - name: karpenter.rules
      rules:
        - alert: KarpenterNodePoolNearLimit
          expr: |
            sum by (nodepool) (karpenter_nodes_allocatable{resource_type="cpu"})
            /
            sum by (nodepool) (karpenter_nodepools_limit{resource_type="cpu"})
            > 0.9
          for: 5m
          labels:
            severity: warning
        - alert: KarpenterHighErrorRate
          expr: |
            rate(karpenter_cloudprovider_errors_total[5m]) > 0.1
          for: 10m
          labels:
            severity: critical
        - alert: KarpenterPendingPods
          expr: |
            count(kube_pod_status_phase{phase="Pending"} == 1) > 0
          for: 10m
          labels:
            severity: warning

15. Cluster Autoscaler との比較

15.1 詳細な比較表

機能Cluster AutoscalerKarpenter
スケーリング速度30秒〜数分数秒〜30秒
インスタンスタイプ選択ASG で固定Pod の要件に基づき動的選択
ビンパッキング限定的高度なアルゴリズム
Consolidationなし自動統合
Drift Detectionなし自動検出・更新
Spot 対応ASG の Mixed Instancesネイティブ対応
GPU 対応ノードグループごとに設定動的に選択
マルチアーキテクチャノードグループごとに分離統合管理
ノードの有効期限なしexpireAfter で設定可能
設定の複雑さ多数のノードグループが必要少数の NodePool で管理
クラウド対応マルチクラウドAWS(他は開発中)
ノードグループ依存あり(ASG)なし

15.2 スケーリング速度の比較

シナリオ: 100 Pod を同時にデプロイ(各 1 CPU, 2 GiB)

Cluster Autoscaler: 約 2分10秒、m5.xlarge x 25ノード
Karpenter:          約 1分5秒、m5.4xlarge x 7ノード(自動選択)

15.3 コスト比較

月間コスト比較(同一ワークロード):
  Cluster Autoscaler: $4,963/月(平均 CPU 使用率 45%)
  Karpenter:          $2,500/月(平均 CPU 使用率 75%)
  削減額: ~$2,463/月(約50%削減)

16. 本番環境でのベストプラクティス

16.1 NodePool 設計

# 良い例: 幅広いインスタンスタイプを許可
spec:
  template:
    spec:
      requirements:
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["4"]
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ["large", "xlarge", "2xlarge", "4xlarge"]

16.2 Helm チャートの設定

replicas: 2
controller:
  resources:
    requests:
      cpu: "1"
      memory: "1Gi"
    limits:
      cpu: "2"
      memory: "2Gi"
  env:
    - name: CLUSTER_NAME
      value: "my-cluster"
    - name: INTERRUPTION_QUEUE
      value: "my-cluster-karpenter"
    - name: LOG_LEVEL
      value: "info"
serviceAccount:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/KarpenterControllerRole
affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
            - key: app.kubernetes.io/name
              operator: In
              values: ["karpenter"]
        topologyKey: kubernetes.io/hostname
podDisruptionBudget:
  maxUnavailable: 1

17. トラブルシューティング

17.1 一般的な問題と解決策

問題1: Pod が Pending のまま

kubectl describe pod <pod-name>
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter -c controller
kubectl get nodepool -o yaml

よくある原因: NodePool の limits 到達、要件を満たすインスタンスタイプなし、IAM 権限不足

問題2: ノードが Ready にならない

kubectl get nodeclaim
kubectl describe nodeclaim <name>
aws ec2 describe-instances --instance-ids <instance-id>

よくある原因: ユーザーデータのスクリプトエラー、ネットワーク設定の問題、AMI の互換性

17.2 デバッグコマンド集

kubectl get nodepool
kubectl get nodeclaim -o wide
kubectl get ec2nodeclass
kubectl get nodes -l karpenter.sh/nodepool
kubectl top nodes -l karpenter.sh/nodepool
kubectl get events --field-selector source=karpenter --sort-by='.lastTimestamp'

17.3 ログのフィルタリング

# プロビジョニング関連
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter -c controller | \
  jq 'select(.logger == "controller.provisioner")'

# エラーログのみ
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter -c controller | \
  jq 'select(.level == "ERROR")'

18. 高度な設定パターン

18.1 テナント分離パターン

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: team-a
spec:
  template:
    metadata:
      labels:
        team: team-a
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: team-a-nodes
      taints:
        - key: team
          value: team-a
          effect: NoSchedule
  limits:
    cpu: "500"
    memory: 500Gi

18.2 バッチジョブパターン

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: batch-jobs
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: batch-nodes
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ["2xlarge", "4xlarge", "8xlarge"]
      taints:
        - key: workload-type
          value: batch
          effect: NoSchedule
      expireAfter: 24h
  limits:
    cpu: "2000"
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 30s

18.3 機械学習ワークロードパターン

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: ml-training
spec:
  template:
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: ml-nodeclass
      requirements:
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["p", "g"]
        - key: karpenter.k8s.aws/instance-family
          operator: In
          values: ["p4d", "p5", "g5", "g6"]
      taints:
        - key: nvidia.com/gpu
          value: "true"
          effect: NoSchedule
      expireAfter: 168h
  limits:
    cpu: "200"
    memory: 2000Gi
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 10m

19. 移行ガイド

19.1 Cluster Autoscaler からの移行

Phase 1: 並行運用(2-4週間)→ Phase 2: 段階的移行(2-4週間)→ Phase 3: 完全移行(1-2週間)

Karpenter のインストール

helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
  --version "1.1.0" \
  --namespace kube-system \
  --set "settings.clusterName=my-cluster" \
  --set "settings.interruptionQueue=my-cluster-karpenter" \
  --set replicas=2 \
  --wait

19.2 v1beta1 から v1 への API 移行

v1beta1v1変更内容
consolidationPolicy: WhenUnderutilizedconsolidationPolicy: WhenEmptyOrUnderutilized名称変更
spec.template.spec.kubelet削除(EC2NodeClass に移動)設定場所の変更
spec.disruption.expireAfterspec.template.spec.expireAfter設定場所の変更
なしspec.template.spec.terminationGracePeriod新規追加

19.3 Terraform / IaC による管理

resource "helm_release" "karpenter" {
  namespace  = "kube-system"
  name       = "karpenter"
  repository = "oci://public.ecr.aws/karpenter"
  chart      = "karpenter"
  version    = "1.1.0"

  set {
    name  = "settings.clusterName"
    value = module.eks.cluster_name
  }

  set {
    name  = "settings.interruptionQueue"
    value = aws_sqs_queue.karpenter.name
  }

  set {
    name  = "replicas"
    value = "2"
  }

  set {
    name  = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
    value = aws_iam_role.karpenter_controller.arn
  }
}

20. まとめと今後の展望

20.1 まとめ

Karpenter は、Kubernetes クラスターのノード管理を根本的に変革するツールである:

  1. アーキテクチャ: Group-less アーキテクチャにより、ASG の制約を排除し、Pod の要件に基づいた柔軟なプロビジョニングを実現
  2. コアコンセプト: NodePool、NodeClaim、EC2NodeClass の3つのCRDにより、宣言的なノード管理を提供
  3. 最適化: ビンパッキング、Consolidation、Drift Detection により、継続的なコスト最適化と設定の一貫性を実現
  4. スポットインスタンス: ネイティブなSpot対応により、大幅なコスト削減が可能
  5. セキュリティ: IMDSv2、暗号化、IAM最小権限の原則を標準でサポート
  6. オブザーバビリティ: 豊富なPrometheusメトリクスとJSON形式のログにより、運用の透明性を確保

20.2 今後の展望

  • マルチクラウド対応: Azure / GCP Provider の開発が進行中
  • 機能強化: より高度な Consolidation アルゴリズム、GPU ネイティブサポート強化
  • エコシステム: Terraform Provider 強化、ArgoCD/Flux 統合パターンの標準化

20.3 推奨事項

  1. 小規模な環境から開始: 開発環境で十分に検証する
  2. 段階的な移行: Cluster Autoscaler からの移行は段階的に実施する
  3. モニタリングの整備: Prometheus + Grafana でメトリクスを可視化する
  4. Disruption Budget の設定: 本番環境では適切な Disruption Budget を設定する
  5. 定期的な設定レビュー: NodePool と EC2NodeClass の設定を定期的に見直す
  6. コスト分析: Karpenter 導入前後のコスト比較を実施する

本記事は2026年4月時点の情報に基づいています。Karpenter は活発に開発が進められているプロジェクトであり、最新の情報は公式ドキュメント(https://karpenter.sh/)を参照してください。