ピークパフォーマンスを実現するための Kubernetes リソースリクエストとリミットの習得
Kubernetesは、コンテナ化されたアプリケーションのデプロイ、スケーリング、管理を自動化するための強力な機能を提供します。しかし、クラスター全体で最高のパフォーマンスを実現し、安定性を維持できるかどうかは、ワークロードのリソース要件をどのように定義するかに大きくかかっています。不適切に構成されたリソース設定は、パフォーマンスのボトルネック、予測不可能なスケジューリング、非効率なクラスター利用の主な原因となります。
このガイドでは、Kubernetesのリソースリクエスト (Resource Requests)とリミット (Limits)について深く掘り下げます。これらの違いを理解し、正しく適用することは、アプリケーションのサービス品質 (QoS) を確保し、『騒がしい隣人 (noisy neighbors)』を防ぎ、インフラストラクチャの費用を最適化するために不可欠です。これらの設定がKubernetesスケジューラーと基盤となるオペレーティングシステムとどのように連携するかを探ります。
コアコンセプトの理解:リクエストとリミットの比較
Kubernetesでは、Pod内のすべてのコンテナ仕様が、resources.requestsとresources.limitsを使用して、予期されるリソース消費量を定義する必要があります。これらの設定は、アプリケーションの健全性にとって最も重要な2つのリソースであるCPUとメモリを管理します。
1. リソースリクエスト (requests)
リクエストは、コンテナがスケジューリング時に受け取ることが保証されているリソース量を表します。これは、kube-schedulerがPodをどのノードに配置するかを決定する際に使用する最小限のリソース量です。
- スケジューリング: 新しいPodがノードにスケジューリングされる前に、そのノードは、すべてのPodリクエストの合計を満たすのに十分な利用可能な割り当て可能なリソースを持っている必要があります。
- 保証: その後、ノードのリソースが不足した場合でも、コンテナは(退去の対象とならない限り)要求された量以上を受け取ります。
2. リソースリミット (limits)
リミットは、コンテナが消費することを許可されているリソースの最大量を定義します。これらのリミットを超えると、CPUとメモリに対して特定の定義された動作が発生します。
- CPUリミット: コンテナがリミットよりも多くのCPUを使用しようとすると、Linuxカーネルのcgroupsによって使用がスロットリングされ、それ以上サイクルを消費できなくなります。
- メモリリミット: コンテナがメモリリミットを超えると、オペレーティングシステムは直ちにプロセスを終了します(OOMKill: Out Of Memory Kill)。
CPUとメモリの動作の違い
KubernetesがCPUとメモリの境界をどのように適用するかについて、定性的な違いを理解することが重要です。
| リソース | リミット超過時の動作 | 適用メカニズム |
|---|---|---|
| CPU | スロットリング(減速)される | cgroups (CPU帯域幅制御) |
| メモリ | 終了される (OOMKill) | カーネルOOM Killer |
ヒント: CPUスロットリングは一般にOOMKillよりも破壊的ではないため、ノードの不安定性を防ぐために厳密なメモリリミットを設定する一方で、通常のピーク使用量よりもわずかに高いCPUリミットを設定することがベストプラクティスとなることがよくあります。
Pod仕様におけるリソースの定義
リソースはspec.containers[*].resourcesブロック内で定義されます。量は標準的なKubernetesのサフィックス(例: milli-CPUにはm、メビバイトにはMi)を使用して指定されます。
CPU単位の定義
1CPUユニットは1フルコア(またはクラウドプロバイダー上のvCPU)に相当します。1000m(ミリコア)は1 CPUユニットに相当します。
メモリ単位の定義
Mi(メビバイト)またはGi(ギビバイト)が一般的です。1024Mi=1Giです。
YAML設定の例
最低保証として500mのCPUと256Miのメモリを必要とするが、決して1 CPUと512Miを超えてはならないコンテナを考えます。
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1"
サービス品質 (QoS) クラス
リクエストとリミットの関係によって、Podに割り当てられるサービス品質 (QoS) クラスが決まります。このクラスは、リソースが不足し、ノードがメモリを回収する必要がある場合(退去: eviction)のPodの優先順位を決定します。
Kubernetesは3つのQoSクラスを定義しています。
1. Guaranteed (保証)
定義: Pod内のすべてのコンテナは、CPUとメモリの両方について、同一の、ゼロではないリクエストとリミットを持つ必要があります。
- 利点: これらのPodは、リソースがひっ迫した際に最後に退去されるため、最大限の安定性が保証されます。
- 使用例: 厳密なパフォーマンス分離を必要とする重要なシステムコンポーネントまたはデータベース。
2. Burstable (バースト可能)
定義: Pod内の少なくとも1つのコンテナにリクエストが定義されているが、かつすべてのコンテナでリクエストとリミットが等しくない、または一部のリソースにリミットが設定されていない(ただし、リミットの設定を強く推奨)場合です。
- 利点: コンテナがリクエストを超えてバーストし、定義されたリミットまでノード上の未使用容量を利用できます。
- 退去優先度: BestEffort Podsよりも前に退去されますが、Guaranteed Podsよりも後です。
- 使用例: レイテンシのわずかな変動が許容できる、ほとんどの標準的なステートレスアプリケーション。
3. BestEffort (最大限の努力)
定義: Pod内のどのコンテナにもリクエストまたはリミットが定義されていません。
- 利点: シンプルであること以外に利点はありません。
- リスク: ノードがリソースひっ迫を経験した際、これらのPodは退去の最初の候補となります。また、ノード容量が超過した場合、直ちにスロットリングまたはOOMKillの対象となります。
- 使用例: 再起動が容易な、重要度の低いバッチジョブやロギングエージェント。
実践的な最適化戦略
効果的なリソース管理には、測定、反復、および慎重な計画が必要です。
戦略 1:リクエストを正確に測定・設定する
リクエストは、アプリケーションが必要とする典型的または維持可能な最小限の負荷を反映する必要があります。リクエストを高すぎると設定すると、コンテナがそのリソースを使用していなくてもスケジューラーが予約してしまうため、クラスター容量を浪費します。
ベストプラクティス: 監視ツール(Prometheus/Grafanaなど)を使用して、過去の使用状況データを分析します。通常運用中に観測された使用量の90パーセンタイル付近にリクエストを設定します。
戦略 2:控えめなリミットを定義する
リミットはセーフティネットとして機能します。メモリの場合、クラッシュを防ぐために、測定されたピークよりもわずかに高いリミットを常に設定します。CPUの場合、リミットを設定することで、暴走したプロセスが同じノード上の重要な兄弟プロセスからリソースを奪うのを防ぎます。
CPUリミットに関する警告: CPUリミットを厳しすぎると(例: 実際の必要量の50%など)設定すると、絶え間ないスロットリングにより深刻なパフォーマンス低下につながります。Guaranteedによる厳密な分離が特定の理由で必要でない限り、常にBurstable QoSを優先してください。
戦略 3:Vertical Pod Autoscaler (VPA) を活用する
リソースの手動調整は困難で時間がかかります。Vertical Pod Autoscaler (VPA) は、ランタイムの使用状況を監視し、時間の経過とともにPod仕様で定義されたリクエストとリミットを自動的に調整します。VPAは、不適切に構成された状態(またはBestEffort)のワークロードを、最適なBurstableまたはGuaranteed構成に移行するのに役立ちます。
戦略 4:Namespace向けのリソースクォータ
チーム間や環境間でのリソースの占有を防ぐために、管理者はNamespaceレベルでリソースクォータ (Resource Quotas)を使用する必要があります。ResourceQuotaは、そのNamespace内に存在できるCPU/メモリのリクエストおよびリミットの合計量に集約的な制限を課し、公平性を確保します。
Namespaceクォータの例
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: development
spec:
hard:
requests.cpu: "10"
limits.memory: "20Gi"
これにより、development Namespace内のすべてのPodが要求する合計CPUが10コアを超えず、合計メモリリミットが20Giを超えないことが保証されます。
結論
Kubernetesのリソースリクエストとリミットを習得することは、クラッシュを防ぐだけでなく、予測可能なパフォーマンスを実現し、インフラストラクチャ投資に対するリターンを最大化することでもあります。スケジューリングの確実性のためのリクエストと、暴走消費に対する安全性のためのリミットを正しく設定することにより、Podを望ましいQoSクラス(好ましくはBurstableまたはGuaranteed)に引き上げることができます。アプリケーションの進化に合わせて、パフォーマンスメトリックを定期的に確認し、VPAのようなツールを活用して最適なリソースアライメントを維持することを検討してください。