スケジューリングを制御する
目標
Podがどのノードで起動するかを決める7つの手段をすべて使ってみて、最後には、前のステップで作ったクラスターの状態(cordonされたノード、テイントされたノード)を考慮して、配置を設計します。
なぜ重要なのか
スケジューリングは、CKAでは配点が大きくありませんが、トラブルシューティングの問題の半分は、実はスケジューリングの問題です。PendingのPodを見て、filter段階のどの条件で引っかかったのかを、読み取れる必要があります。
特に、topologySpreadConstraintsは誤解が多いです。偏差を計算するときに使う候補ドメインは、PodのnodeAffinity/nodeSelectorを通過するノードたちです。テイントは、デフォルトでは無視して数えます。そのため、Podが使えないノードが、0個のドメインとして残って偏差を大きくし、DoNotScheduleなら残りのPodがすべてPendingになることがよくあります。候補を絞り込む鍵がノードアフィニティであると知ることが、このラボの核心です。
ステップ
- ネームスペース
cka-schedを作成してください。lab-node-0にtopology.kubernetes.io/zone=zone-a、lab-node-1にzone-b、lab-node-2にzone-cのラベルを付け、lab-node-0には追加でdisktype=ssdを付けてください。そのあと、Podssd-pod(イメージnginx:1.27)を、nodeSelectordisktype=ssdで作成し、lab-node-0で起動するようにしてください。 lab-node-2にdisktype=hddラベルを付け、Podaffinity-requiredを、requiredのノードアフィニティ(disktype In [hdd])だけで、lab-node-2に配置してください。nodeSelectorは使いません。- Deployment
pref(レプリカ2、イメージnginx:1.27)を作成し、preferredのノードアフィニティを付けてください。weightは50、条件はdisktype In [ssd]です。 - Deployment
spread(レプリカ3、Podラベルapp=spread)を作成し、requiredのPodアンチアフィニティを付けてください。labelSelectorはapp=spread、topologyKeyはtopology.kubernetes.io/zoneです。 - Deployment
drain-demo(レプリカ2、イメージnginx:1.27)を作成したあと、lab-node-1をcordonしてdrainし、完全に空にしてください。drain-demoは、別のノードで2/2に戻っている必要があります。 lab-node-2に、テイントworkload=batch:NoScheduleを設定してください。Podbatch-podに、そのテイントに耐えるトレラレーションと、nodeSelectordisktype=hddを与えて、lab-node-2に配置してください。- PriorityClass
cka-highを作成してください。value 100000、globalDefault false、preemptionPolicyPreemptLowerPriorityです。Podimportantに、このクラスを指定してください。 - Deployment
evenly(レプリカ4、Podラベルapp=evenly)を作成してください。topologySpreadConstraintsは、maxSkew 1、topologyKeytopology.kubernetes.io/zone、whenUnsatisfiableDoNotSchedule、labelSelectorapp=evenlyです。ここにrequiredのノードアフィニティdisktype In [ssd, hdd]と、workload=batch:NoScheduleのトレラレーションを一緒に付けて、4つのPodが2つのゾーンに2:2で配置されるようにしてください。
参考
kubectl get pods -n cka-sched -o wideで、どのノードで起動したかをすぐに見られます。- drainは、
--ignore-daemonsets --delete-emptydir-data --forceを付けると、たいてい最後まで進みます。 - よくあるミス1: ステップ5で、cordonだけして終わってしまうことです。cordonは、新しいPodだけを防ぎます。
- よくあるミス2: ステップ8で、ノードアフィニティを忘れることです。そうすると、cordonされた
lab-node-1がPod0個のゾーンとして残り、偏差が2になって、半分がPendingのままになります。
ノードのラベルとnodeSelector
ネームスペースcka-schedを作成してください。lab-node-0にtopology.kubernetes.io/zone=zone-a、lab-node-1にzone-b、lab-node-2にzone-cのラベルを付け、lab-node-0には追加でdisktype=ssdを付けてください。そのあと、Podssd-pod(イメージnginx:1.27)を、nodeSelectordisktype=ssdで作成し、lab-node-0で起動するようにしてください。
nodeSelectorは、Podのspecの最上位に来るマップです。ラベルが正確に一致する必要があり、式は使えません。
requiredのノードアフィニティ
lab-node-2にdisktype=hddラベルを付け、Podaffinity-requiredを、requiredのノードアフィニティ(disktype In [hdd])だけで、lab-node-2に配置してください。nodeSelectorは使いません。
affinity.nodeAffinityの下のrequiredDuringSchedulingIgnoredDuringExecutionの中には、nodeSelectorTermsの配列が入ります。matchExpressionsのoperatorはInです。nodeSelectorは使わないでください。
preferredのノードアフィニティ
Deploymentpref(レプリカ2、イメージnginx:1.27)を作成し、preferredのノードアフィニティを付けてください。weightは50、条件はdisktype In [ssd]です。
preferred側は配列で、各項目がweightとpreferenceを持ちます。合わせられなくても配置されるので、どこで起動したかは採点しません。
Podアンチアフィニティで散らばらせる
Deploymentspread(レプリカ3、Podラベルapp=spread)を作成し、requiredのPodアンチアフィニティを付けてください。labelSelectorはapp=spread、topologyKeyはtopology.kubernetes.io/zoneです。
podAntiAffinityの項目は、labelSelectorとtopologyKeyを持ちます。ノードごとにゾーンのラベルが違うので、ゾーン単位で散らばらせれば、結果としてノードが分かれます。
ノードを空にする
Deploymentdrain-demo(レプリカ2、イメージnginx:1.27)を作成したあと、lab-node-1をcordonしてdrainし、完全に空にしてください。drain-demoは、別のノードで2/2に戻っている必要があります。
cordonは新しいPodだけを防ぎ、すでに起動しているPodはそのままにします。実際に空にするにはdrainが必要で、DaemonSetとemptyDirに関するオプションを付けてはじめて進みます。
テイントとトレラレーション
lab-node-2に、テイントworkload=batch:NoScheduleを設定してください。Podbatch-podに、そのテイントに耐えるトレラレーションと、nodeSelectordisktype=hddを与えて、lab-node-2に配置してください。
テイントは、key=value:effectの形式です。トレラレーションは、operatorをEqualにして、valueまで合わせる必要があります。どのノードに行くかは、別途指定する必要があります。
PriorityClassを付ける
PriorityClasscka-highを作成してください。value 100000、globalDefault false、preemptionPolicyPreemptLowerPriorityです。Podimportantに、このクラスを指定してください。
PriorityClassはクラスタースコープです。globalDefaultをtrueにすると、クラスター全体のPodに影響するので注意してください。Podに名前だけを書けば、spec.priorityは自動で埋められます。
総合: 残りのノードに均等に散らばらせる
Deploymentevenly(レプリカ4、Podラベルapp=evenly)を作成してください。topologySpreadConstraintsは、maxSkew 1、topologyKeytopology.kubernetes.io/zone、whenUnsatisfiableDoNotSchedule、labelSelectorapp=evenlyです。ここにrequiredのノードアフィニティdisktype In [ssd, hdd]と、workload=batch:NoScheduleのトレラレーションを一緒に付けて、4つのPodが2つのゾーンに2:2で配置されるようにしてください。
今このクラスターで、Podを受け入れられるノードが何台あるか、まず数えてみてください。cordonされたノードは候補から外す必要があり、テイントされたノードは、トレラレーションがあってはじめて使えます。候補を減らす鍵は、ノードアフィニティです。