TT Lab
はじめる
学ぶ 学習パス コース

CKA — Kubernetes管理者

誰がPodを作り、誰がノードを選ぶのか

TT Labで続きを見る

一言でいうと

DeploymentはPodを作りません。ReplicaSetを作り、ReplicaSetがPodを作り、スケジューラーがノードを選び、kubeletが実行します。各層は、自分の1つ下の層だけを見ます。この連鎖を知っていれば、ロールアウトとスケジューリングの問題を、層ごとに切り分けて診断できます。

Podが作られるまでの連鎖: DeploymentコントローラーがReplicaSetを、ReplicaSetコントローラーがPodを作ります。nodeNameが空のPodはPendingで、スケジューラーがその1マスを埋めると、そのノードのkubeletが実行します。5つの主体は互いを直接呼び出さず、すべてapiserverを経由します

なぜ必要なのか

なぜDeploymentはPodを直接作らないのでしょうか。ローリングアップデートのためです。

イメージを変更すると、Deploymentは新しいReplicaSetを1つ追加で作ります。そして、新しいものを増やしながら、古いものを減らします。このとき、どれくらいの速さで増減させるかが、maxSurgeとmaxUnavailableです。

パラメーター 意味 replicas=4のとき
maxSurge: 2 目標値より、いくつ多く起動できるか ローリング中は最大6個
maxUnavailable: 0 目標値より、いくつまで不足してよいか 常に4個はReady

両方とも0にすると、何も動かせません。maxUnavailable: 0は無停止を意味しますが、余裕のあるリソースが必要で、maxSurge: 0はリソースを節約する代わりに、一時的に容量が減ります。

元に戻せる理由も、ここにあります。古いReplicaSetは削除されず、レプリカ0のまま残っています。revisionHistoryLimitの個数だけです。rollout undoは、その古いReplicaSetを再び大きくするだけです。この値を0にすると、元に戻せません。

どう動くのか

スケジューラーに配置を指示するレバーは、大きく分けて7つです。

ここでよく間違える部分があります。topologySpreadの候補ドメインの集合は、PodのnodeAffinity/nodeSelectorを通過するノードたちです。一方、テイントは、デフォルトでは無視して数えます。そのため、cordonされたり、テイントされたりしたノードが、Pod0個のドメインとして残って偏差を大きくし、DoNotScheduleなら残りがPendingになることが起こります。

現場での姿

事例1: Pendingはエラーではないかもしれません。ホームラボをkubeadm 1.34 + Ciliumで再構築した直後、単一ノードの状態で、hubble-relayとhubble-uiがPendingのままになりました。

Warning  FailedScheduling  0/1 nodes are available: 1 node(s) had untolerated taint(s).

コントロールプレーンノードには、node-role.kubernetes.io/control-plane:NoScheduleテイントが設定されており、hubbleの構成要素は、DaemonSetではなくDeploymentなので、これを許容しません。一方、CoreDNSは、デフォルトのトレラレーションを持っているため、問題なく起動しました。同じクラスター、同じノード、違う結果でした。違いは、トレラレーション1つでした。ワーカーが参加すると、すぐに解消しました。これはバグではなく、正常な動作です。

事例2: GPU 1個はGPU 1個ではありません。同じホームラボには、GPUが4枚付いています。RTX 3090 24GB、RTX 5090 32GB、RTX 4070 Laptop 8GBが2枚です。Podがnvidia.com/gpu: 1だけを要求すると、32GBが必要な学習が、8GBのノートPC用GPUに載ってしまうことがあります。スケジューラーにとって、拡張リソースは個数にすぎず、どちらも同じGPU 1個だからです。

GPU Feature Discoveryが付けてくれるgpu.memoryラベルは文字列なので、24GB以上のような比較セレクターが使えません。そこで、意味ベースのラベルを自分で載せました。

gpu.homelab/tier=xlarge  gpu.homelab/vram=32g   # 5090
gpu.homelab/tier=large   gpu.homelab/vram=24g   # 3090
gpu.homelab/tier=small   gpu.homelab/vram=8g    # 4070 Laptop x2

これで、ワークロードがnodeSelector: {gpu.homelab/tier: xlarge}で、自分の階級を選びます。スケジューリングとは、結局のところ、リソースの名前をどれだけ正確に付けたかの問題です。

付け加えると、配置対象の選択の例として、同じクラスターでGPU Operatorを導入したところ、NFD workerはノード5台すべてに、残りのGPU構成要素は、GPUノード4台にだけ起動しました。NFDが付けたラベルを、ほかのDaemonSetがnodeSelectorで見ているからです。

次のラボですること

最初のラボで、Deploymentを作成してスケールし、ローリングのパラメーターを調整したあと、実際にロールアウトして元に戻します。DaemonSetとStatefulSetも作成してみます。2つ目のラボでは、nodeSelectorからtopologySpreadConstraintsまで、7つのレバーを順番に使ってみます。最後の問題は、前にcordonしてテイントしたノードの状態を覚えていてはじめて解けます。