Kubernetesを運用していると、「あれ?なんでこれがここに起動するの?」と思う瞬間が訪れます。最も代表的な誤解が、「Podはノードに均等に(Round-Robin)分散されるだろう」という思い込みです。
今日は、私が実際に経験した「Podの偏り現象」を通して、Kubernetesスケジューラーが実際にどのようにスコアを付け、ノードを選択するのか、その見えないロジックを解き明かしていきます。🚀

0. 問題状況: 「明らかにノードは複数あるのに…」
テストのためにMySQL、Nginx、そしてhttpdのPodを順番に作成しました。私たちの期待は当然、「1番ノード、2番ノード、3番ノード…と仲良く分けられるだろう?」でした。
しかし現実は異なりました。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
mysql 1/1 Running 0 66m 10.16.2.12 gke-cluster-1-default-pool-rr03
nx 1/1 Running 0 16m 10.16.2.13 gke-cluster-1-default-pool-rr03
nx1 1/1 Running 0 6s 10.16.2.14 gke-cluster-1-default-pool-rr03
httpd 1/1 Running 0 4s 10.16.2.15 gke-cluster-1-default-pool-rr03
ご覧の通り、mysql、nx、さらには別のイメージであるhttpdまで、すべてrr03という一つのノードにばかり延々と作成されました。他のノードはがらんどうなのに!😱
理由は何でしょうか?スケジューラーが故障したのでしょうか?
1. 誤解を解く: スケジューラーはABC順序を知らない ❌
多くの方がスケジューラーが「ラウンドロビン(Round-Robin)」方式を使うから、ノード名順序(A->B->C)でデプロイするだろうと考えます。しかしKubernetesスケジューラーは徹底的に「スコア(Score)」ベースで動きます。
- Filtering: 条件に合わないノードを脱落させる
- Scoring: 残ったノードにスコアを付ける (0~100点)
- Ranking: 1位のノードを選択する
つまり、rr03ノードが継続して選択された理由は順序のためではなく、スケジューラーの目にはrr03が常に「一番の候補」に見えたからです。
2. 犯人分析: なぜ満杯のノードが1位になったのか? 🤔
空のノードがあるにもかかわらず、すでにPodが3つもあるノードが1位になった理由は、2つの設定の組み合わせです。
① リソース要求(Requests)未設定 = 「透明人間」扱い
私はPodを作成する際にresources.requests (CPU/メモリ要求量)を設定しませんでした。
- スケジューラーの視線: 「このPodはCPU 0、メモリ 0を使うのか?」
- 判断: rr03ノードにPodが100個あろうと1000個あろうと、Requestが設定されていないPodばかりであれば、スケジューラーの立場では当該ノードの使用率は依然として「0%」です。
- 結果: 「空のノードも、rr03も、どうせどちらも余裕があるのは同じだな? (リソーススコア同点)」
② 決定打: イメージ局所性 (Image Locality) & レイヤー共有
ここで疑問が生じます。「スコアが同点ならランダムに行くべきなのに、なぜわざわざrr03に行ったのか?」
犯人はImageLocalityの加算点です。
- Nginxデプロイ時: mysqlのためにイメージがダウンロードされたノードに行けば速いからrr03を選択 (理解可能)
- Httpdデプロイ時: 「あれ?これは新しいイメージなのに?」
- しかしDockerイメージはレイヤー(Layer)構造です。
- mysql、nginx、httpdはそれぞれ異なりますが、基盤となるBase Image(Debian, Alpineなど)や共通ライブラリレイヤーを共有する確率が非常に高いです。
- 最終判定:
- 空のノード: 「ベースレイヤーから全部新しくダウンロードしなければならない」 -> 加算点なし
- rr03ノード: 「あれ?隣のPodが使っていたベースレイヤーがすでにあるな?ラッキー!」 -> 加算点付与 (+)
結局、リソーススコアは同点(0点影響)なのに、イメージキャッシュスコアでrr03がわずかにリードし、継続してPodを吸い込むブラックホールになったのです。🕳️
3. 解決方法: Podを分散させるには? 🛠️
この現象を防ぎ、クラスター全体に均等にPodを分散させるにはどうすればよいでしょうか?
✅ 方法1: リソース要求(Requests)を明示する (推奨)
Podに「私はこれくらいの大きさだよ!」と名札を付けてください。
resources:
requests:
cpu: "200m" # 0.2コアを要求
このようにすると、スケジューラーが「あっ、rr03はもう満杯だ。空のノードに送らなければ!」と正常に判断し、負荷分散(Least Allocated)ロジックが機能します。
✅ 方法2: アンチアフィニティ(Anti-Affinity)設定
「私と同じようなPodがいる場所は嫌だ!」と強制的に設定する方法です。
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector: ...
このオプションを使うと、スコア計算に関係なく、必ず重複しないように配置されます。
4. 結論 📝
- KubernetesスケジューラーはABC順序でデプロイしない。
- requestsを設定しないと、スケジューラーはPodのサイズを「0」と見なす。
- この場合、イメージレイヤーが少しでも残っているノードが加算点を受け、Podが1箇所に偏ることがある。
- 安定した運用のためには、Resource Requestsの設定は選択ではなく必須だ!
今日の試行錯誤はここまで!高慢なスケジューラーの心を理解するのに役立ったことを願っています。👋
コメントを残す