Kubernetesスケジューリングエラーの解説: 解決策とベストプラクティス
Kubernetesは、コンテナ化されたアプリケーションをオーケストレーションするための事実上の標準です。その宣言的な性質はデプロイメントを簡素化しますが、Podが起動しない理由、特にスケジューリングの失敗をトラブルシューティングすることは、クラスターオペレーターや開発者にとって一般的な課題です。Podが長期間Pending状態のままである場合、KubernetesスケジューラーがそのPodを実行するのに適切なノードを見つけられないことを示しています。
スケジューリングエラーを理解することは、アプリケーションの稼働時間を維持し、クラスターの利用率を最適化するために不可欠です。このガイドでは、リソース不足、不適切なアフィニティルール、制限的なテイントなど、スケジューリング失敗の最も頻繁な原因を体系的に分析し、ワークロードが利用可能なノードに確実に配置されるための明確な解決策とベストプラクティスを提供します。
Pending状態のPodを診断する: 最初のステップ
修正を試みる前に、スケジューラーが失敗している理由を正確に診断する必要があります。この調査のための主要なツールはkubectl describe podです。
PodがPending状態でスタックしている場合、describe出力のEventsセクションには、スケジューリングの決定プロセスと拒否の詳細を示す重要な情報が含まれています。
kubectl describe podの使用法
常に問題のあるPodを対象にします:
kubectl describe pod <pod-name> -n <namespace>
出力の最下部にあるEventsセクションを特に調べてください。ここには、スケジューリングを妨げた制約が明示的に記載されています。一般的なメッセージは、Insufficient cpu、Insufficient memory、または特定の述語の失敗に関連していることが多いです。
一般的なスケジューリングエラーのカテゴリと解決策
スケジューリングの失敗は、一般的に次の3つの主要なカテゴリに分類されます: リソース制約、ポリシー制約 (アフィニティ/アンチアフィニティ)、ノード構成 (テイント/トレランス)。
1. リソース制約 (リソース不足)
これは最も頻繁な原因です。スケジューラーは、Pod仕様で定義されたリクエストを満たすことができるノードを必要とします。利用可能なCPUまたはメモリが十分なノードがない場合、PodはPendingのままになります。
問題の特定
Eventsセクションには、次のようなメッセージが表示されます。
0/3 nodes are available: 3 Insufficient cpu.0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match node selector.
リソース不足の解決策
- Podのリクエストを減らす: Podのリクエストが過度に高い場合、PodまたはDeploymentのYAMLでCPUまたはメモリの
requestsを下げてみてください。 - クラスター容量の増強: Kubernetesクラスターにノードを追加します。
- 既存のワークロードをクリーンアップする: 既存のノード上の優先度の低いPodや重要でないPodを終了し、リソースを解放します (
kubectl drainを使用するか、既存のデプロイメントのリソースリクエストを調整します)。 - Limit Rangesを使用する: 名前空間にリソース制限が定義されていない場合、
LimitRangeオブジェクトを実装して、単一のPodがリソースを占有するのを防ぎます。
2. ノードセレクターとアフィニティ/アンチアフィニティルール
Kubernetesは、nodeSelector、nodeAffinity、podAffinity/podAntiAffinityを使用して、Podを配置できる場所または配置する必要がある場所を細かく制御できます。
ノードセレクターの不一致
利用可能なノード上のどのラベルとも一致しないnodeSelectorを定義した場合、Podはスケジュールできません。
YAMLスニペットの例 (失敗の原因):
spec:
nodeSelector:
disktype: ssd-fast
containers: [...] # Pod remains Pending if no node has disktype=ssd-fast
解決策: nodeSelectorで指定されたラベルが少なくとも1つのノードに存在すること (kubectl get nodes --show-labels)、および大文字/小文字が正確に一致することを確認してください。
ノードアフィニティの制約
nodeAffinityは、より柔軟なルール (例: requiredDuringSchedulingIgnoredDuringExecution または preferredDuringSchedulingIgnoredDuringExecution) を提供します。requiredルールが満たせない場合、PodはPendingのままになります。
診断のヒント: 複雑なアフィニティルールを使用している場合、Eventsセクションにはしばしばnode(s) didn't match node selector.と表示されます。
Podアフィニティとアンチアフィニティ
これらのルールは、他のPodとの関連で配置を制御します。たとえば、アンチアフィニティルールが、特定のサービスをホストするノードでPodを実行しないことを要求しているにもかかわらず、すべてのノードがすでにそのサービスをホストしている場合、スケジューリングは失敗します。
解決策: アフィニティルール内のトポロジーキーとセレクターを慎重に確認してください。アンチアフィニティルールが厳しすぎる場合は、要件を緩和するか、ルールによって選択されたターゲットPodが、避けたいノードで実際に実行されていることを確認してください。
3. テイントとトレランス
テイントはPodを拒否するためにノードに直接適用され、トレランスはテイントされたノードにPodが配置されることを許可するためにPodのスペックに追加されます。
- テイント: 一致するトレランスを持たない限り、Podを拒否します。
- トレランス: 一致するテイントを持つノードにPodがスケジュールされることを許可します。
テイントによる拒否の特定
Eventsには、拒否理由が明示的に記載されます。
0/3 nodes are available: 2 node(s) had taint {dedicated: special-workload, effect: NoSchedule}, that the pod didn't tolerate.
テイントとトレランスの解決策
主に2つのアプローチがあります。
-
Podを変更する (アプリケーションPodに推奨): ノードのテイントに一致する必要な
tolerationsをPodの仕様に追加します。トレランスの例:
yaml spec: tolerations: - key: "dedicated" operator: "Equal" value: "special-workload" effect: "NoSchedule" containers: [...] -
ノードを変更する (クラスター管理者に推奨): 制限が不要になった場合、ノードからテイントを削除します。
```bash
テイントを削除するには
kubectl taint nodes
dedicated:special-workload:NoSchedule-
```
ベストプラクティス警告: 意図的に重要なコントロールプレーンコンポーネントをマスターノードにスケジュールする場合を除き、アプリケーションPodでグローバルな
node-role.kubernetes.io/master:NoScheduleテイントをトレランスしないようにしてください。
高度なスケジューリング制約
あまり一般的ではありませんが、重要な制約もスケジューリングをブロックする可能性があります。
ストレージボリュームの制約
Podが利用可能なノードに現在バインドできないPersistentVolumeClaim (PVC) を要求する場合 (例: 特定のストレージプロビジョナー要件やボリュームの利用不可のため)、PodはPendingのままになることがあります。
診断: まずPVCのステータスを確認します (kubectl describe pvc <pvc-name>)。PVCがPending状態でスタックしている場合、ボリュームが利用可能になるまでPodのスケジューリングは停止します。
DaemonSetsとトポロジースプレッド
DaemonSetは、選択基準 (もしあれば) に一致するノードにのみスケジュールされます。クラスターがパーティション分割されている場合や、新しいノードがDaemonSetのセレクターと一致しない場合、DaemonSetは実行されません。
トポロジースプレッド制約 (定義されている場合) は、均等な分散を保証します。現在の分散がスプレッド制約を尊重しながら任意のノードへの配置を妨げる場合、スケジューリングは失敗します。
スケジューリングを成功させるためのベストプラクティス
スケジューリングの問題を最小限に抑えるために、以下の運用上のベストプラクティスを採用してください。
- リソースリクエストを明示的に定義する: CPUとメモリには、常に適切な
requests(およびオプションのlimits) を設定してください。これにより、スケジューラーがノード容量を正確に評価できるようになります。 - ノードラベルをゾーン分けに活用する: 一貫したノードラベリング (例:
hardware=gpu、zone=us-east-1a) を実装し、nodeSelectorまたはnodeAffinityを使用して、ワークロードを適切なハードウェアに誘導します。 - テイントとトレランスを文書化する: ノードがメンテナンスやハードウェアの分離のためにテイントされている場合、これらのテイントを中央で文書化してください。テイントされたリソースへのアクセスを必要とするアプリケーションマニフェストには、対応するトレランスが含まれていることを確認してください。
- クラスターオートスケーラーを監視する (使用している場合): スケーリングソリューションに依存している場合、それが機能していることを確認してください。スケーリングをトリガーすべき容量不足がサイレントに失敗し、PodがPendingのままになる可能性があります。
- スケジューラーログを確認する (上級者向け): 詳細な診断のために、
kube-schedulerコンポーネント自体のログを確認してください。スケジューリングの試行と拒否理由がすべて記録されています。
結論
Kubernetesのスケジューリングエラーは、苛立たしいものですが、ほとんどの場合、Podが必要とするもの (リクエスト、アフィニティ、トレランス) とノードが提供できるもの (容量、ラベル、テイントの有無) との間の不一致に起因しています。kubectl describe podを使用してEventsを体系的に調査し、リソースの制限、アフィニティの不一致、またはテイントの障壁に対処することで、Pending状態のPodを迅速に解決し、コンテナオーケストレーションをスムーズに実行することができます。