KarpenterとDescheduler併用のメリットと活用ガイド

**前提:** 本記事では Kubernetes 1.30 以降のクラウド環境（例: Amazon EKS）を対象に、Karpenter と Descheduler を併用するメリットについて解説します。読者は Kubernetes クラスター運用の基礎知識があり、Descheduler の基本インストール手順は既知であることを想定しています。

Karpenter は動的なノードプロビジョニングによるオートスケーリングを実現する先進的なツールで、一方 Descheduler は既存の Pod をエビクト（強制退去）して再スケジューリングすることでクラスタ内のリソース配置を最適化する Kubernetes アドオンです。これらを組み合わせることで、**効率的なスケーリング**と**最適なPod分布**の双方を自動化できます。本記事では、その併用メリット、ユースケース、運用上のベストプラクティス、そしてパフォーマンスおよびコスト最適化の戦略について解説していきます。

## KarpenterとDeschedulerを併用するメリット

Karpenter 単独でもノードの自動プロビジョニング・削除や**Bin Packing**（詰め込みによる集約）によるコスト最適化が可能ですが、Descheduler を併用することで Kubernetes スケジューラだけでは扱いきれない状況に対処できます。以下に主なメリットを整理します。

- **クラスタリソースの自動集約と解放によるコスト削減:** Karpenter は未スケジューリングのPodを検知すると必要なノードを即時に起動し、不要になったノードは自動的に終了させます。また**Consolidation（統合）機能**により、常にPod配置とノード利用率を監視し、ワークロードをより少ないノードに集約することでリソース利用効率を高めます ([Spread workloads across nodes and Availability Zones - AWS Prescriptive Guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/ha-resiliency-amazon-eks-apps/spread-workloads.html#:~:text=For%20workloads%20that%20use%20Karpenter%2C,following%20code%20provides%20an%20example))。Karpenterは各Podのリソース要求やコストを考慮して最適なノードを継続的に評価し、不要なノードを削除することでクラウド利用料金を抑えます ([Spread workloads across nodes and Availability Zones - AWS Prescriptive Guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/ha-resiliency-amazon-eks-apps/spread-workloads.html#:~:text=functionality%20to%20optimize%20resource%20utilization,following%20code%20provides%20an%20example)) ([Spread workloads across nodes and Availability Zones - AWS Prescriptive Guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/ha-resiliency-amazon-eks-apps/spread-workloads.html#:~:text=,the%20node%20to%20reduce%20cost))。一方、Deschedulerも`LowNodeUtilization`や`HighNodeUtilization`戦略を使えば類似の再配置が可能ですが、Karpenterの統合機能はこれを自動化し、ノードの削除まで一貫して行います ([Comparing the Kubernetes Cluster Autoscaler and Karpenter](https://notes.elmiko.dev/2024/01/21/comparing-cas-and-karpenter.html#:~:text=Karpenter%20provides%20users%20with%20more,CAS%20does%20not%20provide%20an))。

- **Pod分布の最適化と自動修正:** 初回スケジューリング時に満たされていた**トポロジースプレッド制約**(Topology Spread Constraints)や**Podのアンチアフィニティ**(Inter-Pod AntiAffinity)も、スケールダウンやノード障害の後には崩れてしまうことがあります。Kubernetes のスケジューラは既存Podの再配置までは行わないため、ダウンスケール後には特定ノードや特定のAZ(Availability Zone)にPodが偏ってしまうケースがあります ([Implement more kubernetes-sigs/descheduler features in Karpenter · Issue #737 · kubernetes-sigs/karpenter · GitHub](https://github.com/kubernetes-sigs/karpenter/issues/737#:~:text=From%20my%20point%20of%20view,solve%20this%20issue%20over%20time))。Karpenter 自身も現時点ではトポロジーの歪み（skew）を解消するためにPodをエビクトする機能は持っていません ([Implement more kubernetes-sigs/descheduler features in Karpenter · Issue #737 · kubernetes-sigs/karpenter · GitHub](https://github.com/kubernetes-sigs/karpenter/issues/737#:~:text=ellistarn%20%20%20commented%20,72))。Descheduler を併用することで、このような**アンバランスなPod分布を自動で是正**できます ([Spread workloads across nodes and Availability Zones - AWS Prescriptive Guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/ha-resiliency-amazon-eks-apps/spread-workloads.html#:~:text=There%27s%20no%20guarantee%20that%20the,unbalanced%20across%20the%20failure%20domain))。例えば、`RemovePodsViolatingTopologySpreadConstraint` や `RemovePodsViolatingInterPodAntiAffinity` といった戦略を有効にすると、トポロジー制約やアンチアフィニティ規則に違反しているPodを検出してエビクトし、他ノードへ再スケジューリングさせることで望ましい分散状態を取り戻します ([Spread workloads across nodes and Availability Zones - AWS Prescriptive Guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/ha-resiliency-amazon-eks-apps/spread-workloads.html#:~:text=To%20maintain%20a%20balanced%20pod,consider%20the%20following%20Descheduler%20strategies))。これにより、マルチAZ環境で一部のAZにのみPodが集中している場合でも、自動的に再分散され高可用性が担保されます。

- **パフォーマンスの安定化とノード負荷の均衡:** クラスター内の一部のノードにだけPodが集中して高負荷になり、他のノードが遊休状態になるのは望ましくありません。Karpenter は新規Podに対して最適なノードをプロビジョニングしますが、既存ノード間の負荷偏りまでは積極的に是正しません。Deschedulerの `LowNodeUtilization` 戦略を使えば、**過度に高負荷なノード上のPodを低負荷ノードへ退避**させることでノード間のリソース利用を均衡できます。例えばあるノードのメモリ使用率が70%を超え、他に20%未満のノードがあれば、一部Podを後者へ移動させるといった調整が自動で行われます ([descheduler/docs/user-guide.md at master · kubernetes-sigs/descheduler · GitHub](https://github.com/kubernetes-sigs/descheduler/blob/master/docs/user-guide.md#:~:text=)) ([descheduler/docs/user-guide.md at master · kubernetes-sigs/descheduler · GitHub](https://github.com/kubernetes-sigs/descheduler/blob/master/docs/user-guide.md#:~:text=,70%20plugins))。この負荷バランシングにより、特定ノードのリソース逼迫によるアプリケーション性能低下を防ぎ、クラスタ全体として安定したパフォーマンスを維持できます。

- **運用負荷の軽減と自動化:** 手作業でPodの再配置やノードスケールを調整する必要が減り、運用者の負担を軽減できます。特にクラスターのサイズが大きくワークロードの変動が激しい場合、人手で最適なノード構成やPod分布を維持するのは困難です。KarpenterとDeschedulerの組み合わせにより、**スケーリングから配置最適化までをフル自動化**できるため、オペレーターはポリシー設定に注力するだけで済みます。

## ユースケースの具体例

では、どのようなワークロードでKarpenterとDeschedulerの併用が効果を発揮するのでしょうか。いくつか代表的なユースケースを紹介します。

- **突発的な需要に対応するバッチ処理・CIジョブ:** 処理要求が一時的に急増・減少するバッチジョブやCI/CDパイプラインの実行環境では、Karpenterが即座に必要なだけノードを追加し、アイドル時には縮退させてコストを抑えます。ジョブ完了直後はノード上に少数のPodが残ることがありますが、Descheduler（`HighNodeUtilization`戦略など）を使えばこれらを他ノードへ集約し、不要になったノードを空にできます。結果として、Karpenterは空ノードを検知して安全に削除し（または `WhenUnderutilized` 設定の場合は低利用ノードも統合し）、リソースの遊休を最小限に抑えられます。

- **マルチAZにまたがる可用性重視のサービス:** 複数のAZに分散デプロイされたウェブサービスやデータベースクラスターでは、各AZにワークロードが均等に配置されていることが重要です。通常のスケジューリングではAZ障害発生時に一時的にPodが他のAZに偏る可能性があります（例: 1つのAZがダウン -> 残りのAZに全Podが再スケジューリング）。復旧後も偏りが残る場合、Deschedulerの`RemovePodsViolatingTopologySpreadConstraint`戦略が自動で一部Podを元のAZにエビクトし、**AZ間でPod数を再均衡**します。Karpenterは各AZにノードをプロビジョニングする際にゾーンアフィニティも考慮できるため（ゾーンを指定したプロビジョナー設定が可能）、Deschedulerと組み合わせることで障害からの復帰後も安定した分散状態を維持できます。

- **混在ワークロードのマルチテナント環境:** 複数チームや多種多様なアプリケーションが同一クラスター上で動作する環境では、時間とともにリソース利用状況が断片化し、非効率な配置になることがあります。例えば一部のノードは大量の小さなPodで溢れ、他のノードは大きなPodが1つだけ載って残り資源を遊ばせている、といった状況です。KarpenterとDeschedulerを導入すれば、こうした**断片化の解消**が自動化されます。Karpenterは新規Podに最適なノードタイプを提供しつつ、Deschedulerが定期的に不要な重複配置（`RemoveDuplicates`戦略）や不適切な配置を是正します。結果として、各テナントのPodが適切に分散・集約され、全体としてムダの少ないノード利用が達成されます。

以上のように、**スパイク的な負荷変動**や**高可用性要件**、**複雑な混在環境**といった状況で、両者の併用が有効であることが分かります。

## 運用のベストプラクティス

KarpenterとDeschedulerを実運用で併用する際に押さえておきたい設定やパラメータ調整のベストプラクティスを紹介します。適切なチューニングを行うことで、両者の機能を最大限に活かしつつ不要な干渉を避けることが可能です。

### Karpenterの設定ポイント

- **Provisioner/NodePoolの設定:** Karpenterではリソースプロビジョニングの挙動を制御するために `Provisioner`（または Kubernetes 1.30+ では `NodePool` リソース）を作成します。ここでインスタンスタイプやAZ、スポットインスタンス利用可否などを指定可能です。特に重要なのが**統合ポリシー（Consolidation Policy）**の設定です。`spec.disruption.consolidationPolicy` を `WhenUnderutilized` に設定すると、**未使用または低利用のノードも対象に**積極的な統合が行われ、コスト削減効果が最大化されます ([Spread workloads across nodes and Availability Zones - AWS Prescriptive Guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/ha-resiliency-amazon-eks-apps/spread-workloads.html#:~:text=,the%20node%20to%20reduce%20cost))。一方で `WhenEmpty` に設定すると**完全に空のノード**のみを削除対象とするため、若干のリソース非効率を許容する代わりに過度なPodエビクトを防ぎ安定性を重視できます ([Spread workloads across nodes and Availability Zones - AWS Prescriptive Guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/ha-resiliency-amazon-eks-apps/spread-workloads.html#:~:text=,the%20node%20to%20reduce%20cost))。

```yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: default
spec:
  # 例: 全てのノードを統合対象とし、30日以上経過したノードは更新
  disruption:
    consolidationPolicy: WhenUnderutilized   # "WhenEmpty"も選択可
    expireAfter: 720h                        # ノード更新間隔の例（30日）
  template:
    spec:
      # 例: 米国西部リージョンの3つのAZに分散、スポット利用可
      providerRef:
        name: default
      taints: []  # 必要に応じてノードに付与する taint
      labels:
        workload: general
      requirements:
      - key: "topology.kubernetes.io/zone"
        operator: In
        values: ["us-west-2a","us-west-2b","us-west-2c"]
      - key: "karpenter.sh/capacity-type"
        operator: In
        values: ["spot","on-demand"]
```

- **ノード削除の安全性:** Karpenterがノードを削除する際には、Kubernetesのエビクションメカニズムを使ってPodを退避させます。デフォルトで Pod Disruption Budget (PDB) を順守するため、重要なアプリケーションには適切なPDBを設定しておきましょう。例えば、レプリカ数3のDeploymentなら`maxUnavailable: 1`のPDBを設定することで、KarpenterやDeschedulerが同時に落とすPodは1つまでに制限できます。これにより統合や再配置によるサービス影響を最小限にします。

- **スポットインスタンス活用:** EKSでKarpenterを使う場合、プロビジョナーの`requirements`にて`karpenter.sh/capacity-type: spot`を許可すると低コストなスポットインスタンスを自動利用できます。Karpenterはスポット中断イベントを検知すると該当ノード上のPodを速やかに再スケジューリングできる新規ノードを起動するため、Deschedulerで明示的に対応しなくても高可用性を保てます。コスト最重視のワークロードではスポット活用を検討してください（オンデマンドとの混在も可能）。

- **スケーリング速度と安定性のバランス:** Karpenterはイベント駆動で高速にノードを増減できますが、急激なスケーリングはシステムの他部分に負荷をかける場合もあります。例えば一度に多数のノードを起動するとCSIボリュームのアタッチやネットワーク初期化に時間がかかることがあります。必要に応じてKarpenter Provisioner設定で新規ノードの起動間隔や並行数を調整することも検討してください。また、Deschedulerとの組み合わせではKarpenterの統合が頻繁に発生しすぎないよう、**統合ポリシーは慎重に設定**します。まずは `WhenEmpty` で運用し、余裕があれば `WhenUnderutilized` に切り替えるといった段階的な適用が安全策となります。

### Deschedulerの設定ポイント

- **戦略（Strategy）の選定:** Deschedulerはポリシーに基づいてさまざまな戦略でPodエビクトを行います。併用環境で特に有用なのは前述の**トポロジー/親和性関連**の戦略です。以下は一例として、トポロジー違反、アンチアフィニティ違反、重複Pod排除の3つの戦略を有効にしたポリシーYAMLです。

```yaml
apiVersion: descheduler/v1alpha2
kind: DeschedulerPolicy
profiles:
  - name: default
    plugins:
      deschedule:
        enabled:
          - RemovePodsViolatingTopologySpreadConstraint
          - RemovePodsViolatingInterPodAntiAffinity
          - RemoveDuplicates
```

  上記の設定により、Descheduler 実行時にこれらの条件に合致するPodが検出されるとエビクトされます ([Spread workloads across nodes and Availability Zones - AWS Prescriptive Guidance](https://docs.aws.amazon.com/prescriptive-guidance/latest/ha-resiliency-amazon-eks-apps/spread-workloads.html#:~:text=To%20maintain%20a%20balanced%20pod,consider%20the%20following%20Descheduler%20strategies))。例えば、デプロイメントのレプリカが同一ノードに偏っていれば`RemoveDuplicates`で一部Podが退避されます。またTopology Spread Constraints違反のPodがあれば`RemovePodsViolatingTopologySpreadConstraint`でエビクトされ、再スケジューラ後にバランスが改善されます。

- **リソース利用偏りの是正:** クラスタ内のCPUやメモリ利用率の偏りを解消したい場合は、`LowNodeUtilization` や `HighNodeUtilization` 戦略をプロファイルの`balance`カテゴリで有効化します。前者は高負荷ノードから低負荷ノードへPodを移動し全体を平均化する戦略、後者は逆に極端に低負荷なノード上のPodを集約しノードを解放する戦略です ([descheduler/docs/user-guide.md at master · kubernetes-sigs/descheduler · GitHub](https://github.com/kubernetes-sigs/descheduler/blob/master/docs/user-guide.md#:~:text=)) ([descheduler/docs/user-guide.md at master · kubernetes-sigs/descheduler · GitHub](https://github.com/kubernetes-sigs/descheduler/blob/master/docs/user-guide.md#:~:text=))。Karpenterの統合機能と重複する部分もありますが、**即時の再配置**が必要な場合や細かな閾値制御を行いたい場合には有効です。例えば閾値を「CPU使用率20%未満のノードから70%超のノードへ移動」と設定すれば、アイドルノードを減らしつつ過負荷ノードも緩和できます。

- **実行方法と間隔:** Descheduler は Deployment/Job としてデプロイし、一定間隔で実行するのが一般的です（公式では1分毎程度のCronJob実行例が紹介されています）。実環境ではクラスターの規模と変動率に合わせて実行間隔を調整してください。**高頻度で実行しすぎるとPodの揺り戻し（頻繁な再スケジューリング）**が発生しかえって不安定になります。まずは5～15分間隔程度で様子を見て、必要に応じて頻度を上げ下げするとよいでしょう。Karpenterの統合処理とも干渉しないよう、統合ポリシーが動作するタイミングとDeschedulerの間隔に若干のゆとりを持たせることをお勧めします。

- **重要リソースの保護:** Deschedulerはデフォルトでローカルストレージを使用しているPodや特定のシステムPodはエビクトしない設定ですが、念のためクリティカルなワークロードには`priorityClass`やラベルを設定し、Deschedulerの対象から除外することも検討してください（ポリシーで除外可能）。また、Karpenterの場合と同様にPDBを活用し、同時に落としてよいPod数を制限することで、安全に再配置が行えるようにします。

## パフォーマンスとコスト最適化のスケーリング戦略

クラウド上のKubernetes環境では、リソースコストとアプリケーションパフォーマンスの最適化が常にトレードオフになります。KarpenterとDeschedulerを組み合わせることで、このバランスを細かく調整したスケーリング戦略を実現できます。

- **コスト最優先の戦略:** ワークロードが許す限りリソースを集約し、未使用ノードを速やかに削除していきます。Karpenterの`WhenUnderutilized`統合とDeschedulerの`HighNodeUtilization`戦略（必要なら）を駆使することで、常に最少ノードで運用するポリシーです。 ([Karpenter vs. Cluster Autoscaler in EKS: A Comparative Guide - DEV Community](https://dev.to/femilawal/karpenter-vs-cluster-autoscaler-in-eks-a-comparative-guide-4o71#:~:text=scaling%20based%20on%20custom%20metrics))で指摘されているように、Karpenterの効率的なBin Packingと統合機能はクラスタの計算コストを削減できます ([Karpenter vs. Cluster Autoscaler in EKS: A Comparative Guide - DEV Community](https://dev.to/femilawal/karpenter-vs-cluster-autoscaler-in-eks-a-comparative-guide-4o71#:~:text=Optimized%20Compute%20Costs%3A%20Unlike%20Cluster,consolidation%20can%20reduce%20computation%20costs))。特に開発環境やステージング環境など一時的な負荷に対し、多少のPod再スケジューリングが発生しても問題ないケースでは、この戦略によって大幅なコスト圧縮が可能です。例えば、夜間にほぼアイドルとなるようなクラスタでは、Podを数台のノードに集約して他を全て停止する運用が考えられます（翌朝の需要増加時にはKarpenterが即座にノードを追加します）。

- **パフォーマンス・可用性重視の戦略:** ユーザー向け本番サービスなど、常に安定した応答性能と可用性が求められるケースでは、リソースに多少の余裕を持たせつつ分散配置を維持する方が安心です。Karpenterの統合ポリシーを`WhenEmpty`もしくは無効に設定し、Deschedulerでは`RemovePodsViolatingTopologySpreadConstraint`や`LowNodeUtilization`戦略を用いて**各ノードの負荷が均等になるよう調整**します。これにより、単一ノード故障時の影響範囲を局所化し（各ノードに重要Podが偏りすぎない）、かつノード間でリソース消費が平準化されるためスループットのばらつきも減ります。多少コストは増えますが、クラスタ全体の健全性とユーザー体感パフォーマンスを優先できます。

- **ハイブリッド戦略:** 実際には多くの環境で、多様なワークロードが混在するため上記を組み合わせたハイブリッドなアプローチが求められます。Karpenterでは複数のプロビジョナー（NodePool）を定義し、ワークロードの特性に応じて別々のスケーリングポリシーを適用できます。例えば「スポット重視でコスト最適化するバッチ用NodePool」と「オンデマンドのみで性能重視のサービス用NodePool」を用意し、それぞれに適切な統合設定を行う、といった構成です。 ([Comparing the Kubernetes Cluster Autoscaler and Karpenter](https://notes.elmiko.dev/2024/01/21/comparing-cas-and-karpenter.html#:~:text=A%20frequent%20problem%20with%20distributed,providing%20a%20more%20active%20approach))で述べられているように、時間経過とともにクラスタがスパース（低密度）になっていく問題に対し、Karpenterはよりアクティブにアプローチできます ([Comparing the Kubernetes Cluster Autoscaler and Karpenter](https://notes.elmiko.dev/2024/01/21/comparing-cas-and-karpenter.html#:~:text=A%20frequent%20problem%20with%20distributed,providing%20a%20more%20active%20approach))。一方でDeschedulerは微調整を効かせる役割と考え、必要な戦略のみを有効にしておくことがポイントです。両者の強みを使い分けることで、クラウドリソースの費用対効果を最大化しつつサービス品質を維持できるでしょう。

---

以上、KarpenterとDeschedulerを組み合わせるメリットとその活用方法について解説しました。Karpenterの高度なプロビジョニング能力とDeschedulerの柔軟な再スケジューリング戦略を併用することで、クラウド上のKubernetesクラスター運用は一段と効率的かつ自律的になります。自組織のワークロード特性に合わせて適切に設定をチューニングし、ぜひ両ツールの相乗効果によるスケーリング最適化を体感してみてください。