2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Kubernetes 1.24: SIG Scheduling の変更内容

Last updated at Posted at 2022-05-19

Kubernetes 1.24がリリースされましたね :tada:

今回もKubernetes v1.24のCHANGELOGをベースにSIG Schedulingに関する機能について紹介していきます。

過去の変更内容:

1.24変更点の所感

  • 多くのBeta FeatureがGAになりました: DefaultPodTopologySpread, NonPreemptingPriority, PodOverhead, PreferNominatedNode, PodAffinityNamespaceSelector
  • 1.24での機能変更/修正は、利用が増えてきているからか、Pod Topology Spread関連が目立ちます。コミュニティからのフィードバックを受けて成長してきている印象です。
    • nodeAffinity/nodeSeletorの扱いの変更(Urgent Upgrade Notes)
    • topologySpreadConstraint.minDomainの導入(API Changes)
  • それ以外は小さな修正にとどまっています。

下記 :pencil: がついた文章は、CHANGELOGの公式内容ではなく筆者の補足です。

:new: What's new! (新情報)

Storage Capacity and Volume Expansion Are Generally Available

Kubernetes 1.24: SIG Storageの変更内容を参照ください。

NonPreemptingPriority to Stable

NonPreempting Priority Classの機能がStableになりました。 

:pencil: バッチ処理やデータサイエンスワークロードなどPreemptionされたくないワークロードがある場合に、Preemptionする側のPriorityClasspreemptionPolicy: Never を設定することでPreemptionを行わないPriorityClassを作成することができる機能です(default値はPreemptLowerPriority)。ただしscheduler内のQueueは優先度に応じて並ぶので低優先度のPodよりスケジュールされやすくなるため、バッチワークロードを低優先度に割り当てておくことで、バッチはリソースが空いているときにだけスケジュールされ、プリエンプションされない。が、実現出来ます。

NoPreempting Priority Classの例
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "このpriority classはpodをpreemptしない"

:pencil: Preemptされる側でPreemptされるされないを制御できる
Preemption Toleration Scheduler Pluginというのもあります:bow:

:skull_crossbones: Urgent Upgrade Notes(必ず一読してからアップグレードしなければならない事項)

  • Pod Topology Spreadでskew1を計算する際にスケジュールしようとしているPodのnode affinity/selectorにマッチしないノードで稼働しているPodを除外するようになりました。この変更によって、もしtopology spread対象のlabelにマッチするpodが、node affinty/selectorにマッチしないノードで稼働している場合、これまでscheduleされていたpodがunschedulableになる可能性があります。その場合はこのシナリオが発生しないようにnode affinityもしくはpod selectorを調整してください。(#107009, @kerthcet)
:pencil: インパクトが有るのは、topology spread constraintとnode affinity/selectorの両方を使っている場合のみですのであまりケースは多くないかもしれません。が、どういうふうに治ったのか、説明してますので、興味ある方は折りたたみを開いてください。

おさらい

spec:
  topologySpreadConstraints:
    # 複数指定したらAND-ed
  - topologyKey: zone # zone単位で散らしたい(node label keyを指定できる)
    maxSkew: 1        # 各zoneに居るlabelSelectorにマッチする
    labelSelector:    # pod数の偏りは1以内にしたい
      matchLabels:
        foo: bar
    whenUnsatisfiable: DoNotSchedule # maxSkewを満たせない場合はscheduleしない
                                     # 'ScheduleAnyway'だと無視してスケジュールする
  
  # 今回はこのnodeAffinity/nodeSelectorを一緒に使っている時の挙動が変わります
  affinity:
    nodeAffinity:
    ...
  nodeSelector:
  ...

何が問題だったのか

4ノードが2つのゾーンに存在していて、zone1に2 pod, zone2に3 pod稼働しているとします。(label selectorは話を簡単にするために、全部マッチするとします。)

zone zone1 zone2
node worker worker2 worker3 worker4
existingPods pod1 pod2 pod3, pod4 pod5
vacancy 空き無し 空き無し 空き有り 空き有り

ここに、zoneに対するpod topology spreadと worker3に対するanti node-affinity(worker3にはスケジュールされない) をもったtest podが作成されたとします。すると、

  • test podはworker3にはscheduleされないはずなのに
  • 各zoneのpod数はzone1=2, zone2=3となるので、新しいpodはzone 1にスケジュールされようとしますが、
  • nodeに空きがないのでpendingになってしまう

という挙動が有りました。


どう治ったのか

test podのskewを計算するときにnode affinityを考慮するようになりました。なので、

  • 各zoneのpod数はzone1=2, zone2=1となり、
  • test podはzone2にスケジュールされることになります

副作用

上で説明したシナリオはこの挙動を修正することでスケジュールされやすいケースを意図的に紹介しましたが、当然逆もありえます。この変更によってこれまでscheduleされていたpodがunschedulableになることがあります。

各ゾーンに2nodeずつ、各nodeにはmatching pod(topology constraintにマッチするラベルを持つpod)が1個ずつ動いているとします。各ノードの利用状況はworker3だけ空きがないとします。

zone zone1 zone2
node worker worker2 worker3 worker4
existingPods pod1 pod2 pod3 pod4
vacancy 空き有り 空き有り 空き無し 空き有り

このとき、worker4にanti-node affinityを持つpodが作成されたとします。そうすると

  • 以前の挙動: zone1=2, zone2=2なので、どちらのzoneにもスケジュール可能となり、zone1のnodeが空いているので、worker,worker2のどちらかにスケジュールされる
  • 修正後の挙動: zone1=2, zone2=1となるので、zone2にのみスケジュール可能となるが、worker3はリソース足りない、worker4はanti node affinityがついている、ので、結果としてunschedulableになってしまうという副作用がおきるので注意が必要です。

こぼれ話

もともと、pod topology constraintとnode affinityを両方指定した時の細かい挙動は議論の余地があり、upstreamでもこれをバグとして修正すべきなのか、backportするほどの修正なのか?が議論されていました。

が、Example: TopologySpreadConstraints with NodeAffinity に、TopologySpreadConstraintsはnodeSelectorをrespectする旨の言及が有り、今回修正されることになったようです。ただし、backportはされないことになりました。

参照: kubernetes/kubernetes#106971

:pencil:ちなみに1.25ではNodeInclusionPoliciesと呼ばれていた新しいAPIが追加される予定です(KEP-3094,kubernetes/kubernetes#108492)。podTopologyConstraints[*].{nodeTaintsPolicy|nodeAffinityPolicy}というAPIが追加され、Ignore,Honorを指定することでユーザがskewの計算に対してnode affinity, taintをそれぞれ考慮するかどうかが選べるようになります。NodeInclusionPoliciesについての詳細は下記のスライドが詳しいです。

:wastebasket: Deprecation(非推奨)

:pencil: ありません

:earth_asia: API Changes(API変更)

  • DefaultPodTopologySpreadがGAになりました(#108278, @kerthcet)
  • NonPreemptingPriorityがGAになりました(#107432, @denkensk)
  • PodOverheadがGAになりました(#108441,@pacoxu)
  • Pod affinity namespace selectorとcross-namespace quotaがGAになりました。PodAffinityNamespaceSelector feature gateはlockされ1.26で削除されます。(#108136, @ahg-g)
  • topologySpreadConstraintsminDomainsフィールドが追加され、topology domain の最小数を制限できるようになりました(#107674, @sanposhiho)
    • :pencil: KEP-3022: Min domains in PodTopologySpread
    • :pencil: feature gate: MinDomainsInPodTopologySpread, status: alpha
    • :pencil: topology spread constraintにおいて、どのくらいのdomain(zoneとかnode等)に分散させたいか?を調整できるパラメータです。例えば、minDomains:3と指定すると、最低3 domainに散らばるようにpodが配置されるようになります。
    • :pencil: これまではspreadするdomain数を制御するパラメータが有りませんでした。
:pencil: `minDomains`の挙動の具体例

おさらい

  • schedulerは将来にわたってtopology domainが何種類あるか?はわからないので、
  • 存在しているnode群からtopology domainを把握し、
  • 存在しているdomain間でskewを計算し、
  • maxSkewの範囲内でspreadされるようにスケジュールされます。

シナリオ

  • 現在zone1, zone2が存在しており、zone3はcluster autoscaler等によって自動的に発生するとします。どのnodeもリソースは潤沢にあるとします。
  • pod1, pod2, pod3という3つのpodを、topology domain=zone, maxSkew=1, whenUnstatifiable=DoNotScheduleのconstraintでscheduleすることを考えます。
  • かつ、少なくとも3 domainにspreadしたいと想定します
zone zone1 zone2 (zone3)
node name nodeA nodeK (nodeX)

minDomainが無いと何が困るか?

step zone1 zone2 (zone3) skew
1 pod1 1
2 pod1 pod2 0
3 pod1 pod2,pod3 1
  • 1: pod1がzone1にスケジュールされたとする。
  • 2: pod2はskewを小さくしたいのでzone2にスケジュールされる。
  • 3: pod3は、zone3は存在しないので、zone1, zone2のどちらにスケジュールしてもskewは1でmaxSkewに反しないのでどちらかにスケジュールされる。
  • リソースが潤沢にある場合、どれだけpod作ってもリソースがなくなるまで新しいdomainは出現しない。つまり3 domainにspreadされない

minDomainがあるとどうなるか?

minDomain: 3をつけると、spreadしているtopology domainの個数がそれに満たない場合は各domainのpod数の最大値をskewとします(skewは最大-最小なので、最小を0とするとも言えます)。こうすることでこのシナリオは下記のように変わります。

step zone1 zone2 zone3 skew comment
1 pod1 1
2 pod1 pod2 0
3' pod1 pod2,pod3 2 maxSkew違反
3 pod1 pod2 pod3 0
  • 1: pod1がzone1にスケジュールされたとする
  • 2: pod2はskewを小さくしたいのでzone2にスケジュールされる。
  • 3': pod3を作ると、spreadしているdomain数がまだ2なので、zone1, zone2どこにscheduleした仮定としてもと、skewは2になってしまう→つまりmaxSkew制約を満たせない→pod3はpendingになる。
  • 3: するとCluster Autoscalerによってzone3が出現する。すると、skew=0となるスケジュールが可能となり、pod3はzone3にスケジュールされる
  • 既存のdomainがminDomain未満だと各domainのpodがmaxSkew個になると制約を満たせなくなる。つまりdomainが足りない状態になる。

:sparkle: FEATURE(機能追加)

  • unschedulable queueからactive queueもしくはbackoff queueに強制的にflushする間隔を指定するdeprecatedなコマンドラインflagを追加しました(#108017, @denkensk)
    • :pencil: もともとはReevaluate flushing unschedulable pods into activeQ kubernetes/kubernetes#87850で議論されているissueを一部カバーする機能です。
    • :pencil: kubernetesはその分散システムとしての特性上、一度unschedulable判断されたPodが一定期間経過すると強制的にschedule可能かを再評価する仕組みがあります(単純に考えると一旦unschedulableになったpodはクラスタのステートが変化するまで再評価しなくても良い気がしますが、分散システムでは何があるかわからないため)。
    • :pencil: ただ、この期間は現在までハードコードされ変更出来ない値でした。これが変更出来ない場合、一定期間ごとにactiveQにpodが戻ってくるため、高優先度のpodが大量にある場合、低優先度のpodがscheduling cycleに入れない状態が発生してしまう(kubernetes/kubernetes#86373)ため、設定可能にする修正です。
    • :pencil: kubernetes/kubernetes#87850ではまだ具体的な解決策は出来ていないですが、この単純なしくみよりもより良い解決策を議論しており、はじめからdeprecatedで将来削除予定のflagとして追加されました。
    • :pencil: scheduler内部の3つのQueue(active, unschedulable, backoff)については自作して学ぶKubernetes Schedulerが詳しいです。
  • CycleStatewrite once and read many timesなユースケースに対して最適化されました. (#108724, @sanposhiho)
    • :pencil: CycleStateはscheduler pluginが使えるScheduling Cycleをライフサイクルとするcontextのようなものです。もともと sync.RWMutex排他制御されるmap[StateKey]StateDataでしたが、この修正でsyncMapに変更になりました。sync.Mapwrite once and read many timesに最適化されたデータですが、もともとCycleStateのユースケースはそれに限定されていなかったのですが、これまでで、主なユースケースはPreFilterフェーズで事前計算を行って後続ではそれを参照するのみ、というケースが大半であることから変更されました。
  • PreferNominatedNodeがGAになりました。(#106619, @chendave)
    • :pencil: Schedulerのpreemptionは2 scheduling cyle掛けて行われます。
      • scheduling cycle C1: Preemptor Pod Pがnode N上のVictim Pod Vをpreemptします。このcycleでP.status.nominatedNode: Nがセットされます。
      • scheduling cycle C2: Preemptor Pod Pが実際にスケジュールされる、という挙動をします。
    • :pencil: cycle C2において、nominatedNodeをまずチェックしてスケジュール可能であればそこにスケジュールするという機能です。通常のFilter→Scoreという処理を行いません。つまり、Node Score的により良いノードがいるかも知れない状況ですが、cycleC1でpreemption下nodeを優先的に選ぶという機能です。
  • Scheduler FrameworkにおいてPreFilter拡張点の返り値がStatusからPreFilterResultに変更になりました(#108648, @ahg-g)
    • :pencil: Scheduling Frameworkのメソッドの多くはframework.Statusというデータを返すようになっています。
    • :pencil: 現在のSchedulerのFilterフェーズ(スケジュール可能なノードを探すフェーズ)は、Podのスケジューリングのスループットを上げるためにPercentageOfNodesToScoreで指定した割合のnode数を見つけたらFilterフェーズを早期リターンする用になっているのですが、大規模なクラスタでまだスループットが足りないことが報告されていました。
    • :pencil: なぜかというと、schedule不可能なPodは、Filterフェーズで全ノードがschanされてしまい、ここでスループットが落ちてしまうとのこと。
    • :pencil: そこでPreFilterフェーズでFilterフェーズでscanするnodelistを返せるように変更したらどうか?という提案がなされてそれが、PrefilterResultという形で実現されました。
    • :pencil: 最初のユースケースとしてはNodAffinity Pluginが先にnodeAffinityをチェックしてnode対象のNodeの集合をPrefilterすることが想定されている模様。
    • :pencil: Filterフェーズでは複数のPluginが実行されるので、各nodeを事前にスキップできると不必要なPlugin実行が軽減できスループットの向上が期待されます。
  • kube-schedulerのインセキュアなコマンドラインフラグ(--port)が削除されました。代わりに--bind-address, --secure-portを使ってください。(#106865, @jonyhy96)
  • PodMaxUnschedulableQDuration(のデフォルト値)が5分にセットされました。(#108761, @denkensk)
    • :pencil: で導入された値のデフォルトが5分になりました。バグ修正のセクションでも触れられているように現在は、unschedulablePodをactiveQ/backoffQに戻るタイミングをPluginが細かく制御できるため、デフォルト間隔が延長になりました。
  • Extenderがエラーを返したときに、ログを出力するようになりました。(--v>5) (#107974, @sanposhiho)

:bug: バグ修正

  • Scheduller pluginのevent registrationを修正し、unschedulable podがre-queueされる間隔が長くなってしまう問題を修正しました。(#109442, @ahg-g)
    • :pencil: 各Scheduler PluginはunschedulableなpodをactiveQもしくはbackoffQに戻す必要のあるクラスタイベント(Nodeの追加や更新等)を登録できますが、この登録が正しく行われていませんでした。
    • :pencil: :back:この修正はv1.21以降のすべてのバージョンにbacckportされています。
  • Podの.status.nominatedNodeNameが適切にクリアされずリソースが確保されいる状態になっていた問題を修正しました。(#106816, @Huang-Wei)
    • :pencil: で説明したように、preemptionは2 scheduling cycleかけて行われますが、一般にC1(低優先度podをpreemptionするcycle)とC2(preeptorをscheduleするcycle)は連続していません。
    • :pencil: この時、C1C2の間のcycleでPNにscheduleできなくなる事象が起きた際に適切にstatus.nominatedNodeが削除されず、scheduler上でP分のリソース利用が予約され続けてしまうバグがありました。
    • :pencil: 例えば、 C1C2の間のcycleでより高い優先度のPod QNにスケジュールされてしまってPが入る空きがなくなってしまった場合等です。
    • :pencil::back:変更量が多いため1.23にのみbackportされています
  • v1beta3 configを使った場合、out-of-tree scheduler pluginの想定外の順序で配置されていた問題を修正しました。(#108613, @Huang-Wei)
    • :pencil: default scheduler pluginよりも前にout-of-tree pluginが配置されてしまっていたようです。
    • :pencil::back:1.23にbackportされています(v1beta3は1.23から導入されました)
  • Pod数が少ない場合のPodToplogySpread score計算時の丸め誤差を改善し、より正確にscoreを考慮するようになりました。(#107384, @sanposhiho)
    • :pencil: int(someFloat64Value)ではなくmath.Roundを使うようになりました。
  • 実際にscheduleされているnodeへのnomiationを行わないようにしました。(#109245, @alculquicondor)
    • :pencil: ある特定のシナリオで、scheduler内のcahceでpodが実際にスケジュールされているにも関わらずnominated podにもなってしまっており、リソースを余分に確保している状態になってしまっていた。
  • Scheduler ExtenderのPreemptを呼んだ際に、後続のpickOneNodeForPreemptionというfilter stepで必要な、NodeのNumPDBVioloationsを復元するように修正しました。(#105853, @caden2016)
    • :pencil: Scheduler ExtenderのPreemptは、kube-schedulerが選出した各victim候補ノードのvictim Pod群とPDB違反Pod数を入力として、それをfilter(victim候補から外して)して、新たなvictim候補と新たなPDB違反Pod数を返すことが出来ます。
    • :pencil: SchedulerがExtenerのPreemptメソッドを呼んだ際、PDB違反POD数がリセットされてしまっていました。この値は最終的にvictim候補ノードを1個に絞るpickOneNodeForPreemptionという処理で使われます。
    • :pencil: 影響が大きそうな修正ですが、どのバージョンにもbackportはされていないようです。Scheduler ExtenderのしかもPreemptを使っている事例は殆どないのかもしれません。

:microscope: Other (その他の修正)

  • PriorityClasskubectl describeする際にPreemptionPolicyが表示されるようになりました。(#108701, @denkensk)
  • preemptionの詳細がscheduling失敗eventに追加されました。(#107775, @denkensk)
    • :pencil: FailedScheduling eventのMessageに↓のような詳細結果が記載されるようになりました。
    • :pencil: preemption: not eligible due to preemptionPolicy=Never
    • :pencil: preemption: 0/4 nodes are available: 1 Insufficient cpu, 3 Preemption is not helpful for scheduling.
  • unschedulableQunschedulablePodsにrenameされました(#108919, @denkensk)
    • :pencil: Qと名前がついていても実際にはFIFOやLIFOのような構造を持っておらず単なるmapだったので実際に即したrenameです。
  • Scheduler frameworkのrunAllFiltersオプションが削除されました。(#108829, @kerthcet)
    • :pencil: もう使われていなかったオプションの削除です。
  1. 各topology domain(zoneやnode)にどのぐらい偏ってpodがスケジュールされているかを示す。具体的には、topology domainをzoneだとすると、各zoneにスケジュールされているpod数の最大値-最小値がskewとなります。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?