だいぶ日が空いてしまって今更ですが、先日行われた Kubernetes Meetup Tokyo #10 で、v1.8*1 から導入された新機能 Preemption について発表してきました。
Preemption は、Kubernetes クラスタのリソースが不足した際に、優先度が低い Pod を追い出して優先度が高い Pod の稼働を保証する仕組みです。
当日は時間が不足気味だったので、説明不十分だったかなと思われる点についていくつか補足しておきます。
Priority の指定について
スライド中では詳しく説明しませんでしたが、ユーザは直接 Pod(や Deployment 中の Pod Template)に対して Priority を指定するのではなく、
- Priority の値を保持する PriorityClass を作成する
- Pod Template には priorityClassName を指定する
という手続きを踏むことになります。Persistent Volume を使用する際あらかじめ StorageClass を作成しておくのに似ていますね。
なお、Pod の Priority が確定するのはその Pod が作成されるタイミング(のみ)である、という点には若干注意が必要です。 Pod 作成時に Admission Control が Pod に指定された priorityClassName を確認して、 PriorityClass から Priority の値を取得し、その値を Pod の status に書き込みます。実際の Preemption に際して Scheduler が参照するのは priorityClassName ではなく、status に書き込まれた値の方です。
このことから、以下の挙動が従います。
- API Server に対して明示的に Admission Control を有効化する必要があり、したがって MiniKube ではそもそも実験ができなかったりする
- Priority 指定なしで起動した Pod は、後からデフォルトの PriorityClass を作成しても反映されず Priority が 0 の扱いになる
- 逆に、PriorityClass を削除した後も Pod に設定された Priority は有効のまま残り続ける
仕組みを理解していれば納得できる話ではありますが、やや混乱しがちなのでもし実践投入する場合は気を付けましょう。
配置待ち Pod のキューについて
スライド中では適当な図でごまかしていますが、今回の Priority/Preemption 導入に伴って、配置待ち(Pending 状態)の Pod が登録されるキューの実装が変更されています。
- Priority 導入のために、文字通りキューが優先度付きキューになった
- 「まだ配置可能かどうかの判定がされていない Pod」と「一度 Preemption の可能性を含めて判定したが配置不能だった Pod」が区別されるようになった
機能としての Preemption を使う立場からすると中身を知らなくても取り立てて困ることはないですが、スライド中の図がミスリーディングだった気がするのでここで補足しておきます。
Affinity との兼ね合いについて
スライドでも説明している通り、Preemption のアルゴリズムは
- 判定対象 Node 上にある Pod の内、配置したい Pod より Priority が低いものをすべて追い出したと仮定して配置可能かどうか確認
- 配置できる場合は、一度追い出したと仮定した Pod を戻せるところまで戻してみて、最終的に追い出しの影響が一番少なく済む Node を選択
という 2 ステップで行われます。
このアルゴリズムの特性として、Pod 間の Affinity もしくは Anti-Affinity が指定されている場合、Preemption のアルゴリズムが上手く機能しない可能性があります。大きく分けて次の二つのパターンです。
Affinity による影響
配置したい Pod X と、今 Node 上にいる Pod Y の間に Affinity が指定されているパターンです。
このとき、もし Y の Priority が X より低いと、最初のステップで Y が追い出された状態で判定が行われるため、実際には X と Y が同居できるだけのリソースがあっても配置できないと判定されてしまいます。
プロポーザルを読むと、この可能性は考慮されていて、しかしなお
- すべての Pod 間の関係について考慮すると組み合わせ爆発でパフォーマンスが落ちる
- 組み合わせ爆発を防ぐために調べる組み合わせを制限すると、ユーザから見て挙動が理解しづらくなる
- Affinity が指定されているならば、その相手はより必要性の高い(= Priority が高い)Pod のはずで、このパターンは現実には発生しにくい
という理由から特に対策は取らない、という選択がなされたようです。
Anti-Affinity による影響
Node を N 上に Pod X が配置できるかどうか判定するとしましょう。N と同じ障害ドメイン内に Node N' があって、その N' 上で Pod Y がいるとします。
このとき、もし Pod X と Pod Y の間に Anti Affinity が指定されていると、Preemption の「仮想追い出し判定」は Node ごとに行われるため、N 上の Pod をすべて追い出したと仮定しても N' 上の Y が邪魔で X が配置できません。
こちらの問題もプロポーザルで指摘されているのですが、残念ながら「いい方法が見つからないので特に対策せず」という消極的な結論になっています。
まとめ
Kubernetes v1.9 から導入された Pod の Priority 指定および Preemption の仕組みについて補足しました。簡単ですがこれ以上遅くなってもまた放置するだけになりそうなのでこの記事はこれで一度公開します。
本当は、ソースコードを追いながら Preemption のアルゴリズムを説明する記事を書こうと思った(そして実際途中まで書いてある)のですが、それはまた別の話。