Open1

Cluster Autoscaler/Karpenter Alighment AEP

Tsubasa NagasawaTsubasa Nagasawa

Cluster Autoscaler/Karpenter API Alignment AEP (Revised 10/15/23) の日本語訳です。

このドキュメントは 2023 年の 9 月上旬に書かれた Cluster Autoscaler/Karpenter API Alignment AEP を改訂したものだ。Cluster Autoscaler と Karpenter の API を時間を掛けて擦り合わせてきたので、Karpenter 寄贈の提案に対する SIG としての最新の考えをまとめる。

Summary

Karpenter と SIG Autoscaling は、Karpenter を SIG Autoscaling に寄贈する提案を受けて Cluster Autoscaler と Karpenter の API や動作を擦り合わせるために議論してきた。いくつかの領域で擦り合わせを進めてきたが、範囲が広く、Kubernetes のエコシステムと密接に関わりがあるため、議論の結果 SIG Autoscaling だけで答えを出すのが難しいという結論に至った。結果として、Karpenter を karpenter.sh の API グループの元で SIG Autoscaling として受け入れることを提案する。

Background

現在、SIG Autoscaling と Karpenter は Karpenter のコア機能を SIG に寄贈する提案に応じて協力している。Cluster Autoscaler (CAS) は現在 SIG のサブプロジェクトであり、クラスタ内のノードのライフサイクル管理の点で Karpenter と重複した機能や API を持っている。

プロジェクトのスコープが重複しているため、Karpenter と CAS のメンテナから主要な挙動、API、ワークロードに関するドキュメントを擦り合わせたいと要望があった。この擦り合わせは、Karpenter が SIG Autoscaling に参加する前提条件という意味合いがあった。ユーザーがそれぞれのプロジェクトを評価する際に、2 つのプロジェクトの間にある根本的な違いによって混乱が生じないようにするためである。

擦り合わせの範囲は以下を含む:

  • 共通の API グループで Labels / Annotations / Taints を共有
    • 2 つのプロジェクトで共通のラベル、アノテーション、Taints を置く API グループを選ぶ
  • Eviction や停止処理のフロー
    • ノードを停止する際の共通の Eviction や停止の流れを擦り合わせる
  • コンセプトのドキュメント
    • 2 つのプロジェクトで重複しているコンセプトについて同意を得る
    • Kubernetes の upstream やサブプロジェクトのドキュメントを更新して共通のコンセプトを公開する

擦り合わせの範囲の詳細は以降の章で取り扱う。

Alignment Scope

2 つのプロジェクトの API の触り心地や動作の擦り合わせの提案は、ノードのライフサイクル管理 (https://github.com/kubernetes/kubernetes/issues/115139 を参照) のコンセプトに対する Kubernetes コミュニティの混乱からも合点がいく。

実際のところ、ノードのライフサイクル管理周りで共通の API や挙動を擦り合わせてくれと言われているが、Kubernetes においてこれらのコンセプトをどう扱うかという大きな問題に帰着する。(SIG Autoscaling の 2 つのプロジェクトに限定した話ではない。)

結局のところ、CAS や Karpenter を取り巻く議論を進めていく中で、SIG Autoscaling 以外の他の SIGs (e.g. SIG Node, SIG Cluster Lifecycle, SIG Architecture, …) のコンセプトも絡むため、適切に議論する必要があるという結論に至った。

Proposal

私たちは当初、Karpenter が SIG Autoscaling に参加するために必須な前提条件として、共通のスコープを擦り合わせる予定だった。しかし、これらの擦り合わせの多くが SIG Autoscaling の枠を超えてスコープが拡大していることに気付いたため、考えを改めることにした。今は、Karpenter の SIG への寄贈と API の擦り合わせに関する話を切り離して考えることが、Karpenter の寄贈を進める上で最善の道であると考えている。

実際のところ、短期・中期・長期で以下を意味する

  • [短期] Karpenter の SIG Autoscaling への寄贈を承認する。共通の API グループに関して合意するまでは、CAS は cluster-autoscaler.kubernetes.io の API グループを、Karpenter はこれまで通り karpenter.sh の API グループを使用する
  • [短期] Karpenter と CAS は今あるまたは将来挙げられる KEP への対応を協力して行う。(例えば、Dynamic Resource Allocation と Pod Topology Spread Constraints)
  • [中期] Karpenter と CAS のメンテナは Autoscaling とノードのライフサイクルに関する設計概念のドキュメントの擦り合わせにすぐ取り掛かり、Kubernetes の upstream まで持っていく
  • [長期] 他の SIGs (SIG Node, SIG Cluster Lifecycle, SIG Architecture, …) と Autoscaling やノードライフサイクル管理のための共通の API グループの追加に向けた議論を継続して進める
  • [長期] 他の SIGs (SIG Node, SIG Cluster Lifecycle, SIG Architecture, …) と共通の安全な停止の流れに関する議論を継続して進める

擦り合わせの詳細

[中期] 設計概念のドキュメント

現在、upstream の Kubernetes には Autoscaling やノードのライフサイクル管理に関するコンセプトについて書いた詳細なドキュメントがない。複数のプロジェクト (現在は Karpenter と CAS) 用の upstream のリポジトリが生えるので、2 つのプロジェクトの Autoscaling やノードのライフサイクル管理に関する共通のコンセプトについて明確にすべきである。2 つのプロジェクトが成熟するにつれて、もしくは他のプロジェクトが SIG に参加するにつれて、コンセプトを拡張していく。

最初の段階で実装するべきと思っていたコンセプトは以下の通りだが、別にこれに限らない:

  1. ノードのライフサイクル管理のプロジェクトが Pod やノードのスケールアップをどう評価するか
  2. ノードのライフサイクル管理のプロジェクトが Pod やノードのスケールダウンをどう評価するか
  3. ノードのライフサイクル管理のプロジェクトがノードを安全に停止するために停止の流れをどうするべきか

これらの領域のコンセプトの擦り合わせを整合性を持って進めるために、ドキュメント内で使われる用語を整理する必要がある。

  1. ノードのプロビジョニング: ノードライフサイクル管理主導な方法 (CAS や Karpenter だと Pod のリソース要求) でノードを作成するプロセス
  2. ノードの中断: ノードを停止するプロセス。一般的に中断候補となるノードの評価から Pod がそのノード上にスケジュールされなくなるようにして、ノードをドレインし、削除するまでの一連のプロセスのこと。
  3. ノードの統合: スカスカな単一のノードやノードのセットを削除するかや置き換えるかを考慮するプロセス

[長期] API グループ

ホストマシンの起動や正常性についての挙動や API は Kubernetes の upstream でも正確に記載されている。これらの API は node.kubernetes.io の namespace の中にあり、初期は kubelet や apiserver、余っている CPU やメモリ、ファイルシステムの正常性、プロセスの正常性などのホストマシンの監視項目のために使われてきた。API の例として node.kubernetes.io/unreachable, node.kubernetes.io/not-ready などがある。

API はホストマシンが起動する時や問題が起きた時に何が起こるかを明確に定義しているが、ノードの停止候補の決定や安全な停止など、ノードを管理する外部コンポーネントが関わる動作を明確に定義できていない。特に、CAS や Karpenter を見ると、ノードのライフサイクル管理と停止候補の決定に関する API は、後述する非常によく似た Taints と Annotations を使っている。

Karpenter と CAS の機能や API が似ていることから、ノード管理の外部コンポーネントと共通のノードのライフサイクルの挙動を 、upstream の Kubernetes とも擦り合わせられると思っている。我々は挙動を定義する upstream の Kubernetes の Labels / Annotations / Taints のための共有の namespace として node-lifecycle.kubernetes.io の API グループを使えないか検討したいと思っている。

最初の見た目

最初のスタート地点として、新しい API グループに以下の Labels / Annotations / Taints を導入する。API は node-lifecycle.kubernetes.io の namespace を使うことに対してユーザーからのフィードバックを収集する間、アルファ段階の Labels / Annotations / Taints として導入する。

Taints

  • node-lifecycle.kubernetes.io/disruption=candidate:PreferNoSchedule
    • CAS でいう DeletionCandidateOfClusterAutoscaler:PreferNoSchedule
    • Karpenter では対応するものが現状ない
  • node-lifecycle.kubernetes.io/disruption=disrupting:NoSchedule
    • CAS でいう ToBeDeletedByClusterAutoscaler:NoSchedule
    • Karpenter でいう kubernetes.io/unschedulable:NoSchedule

Annotations

  • node-lifecycle.kubernetes.io/do-not-disrupt
    • CAS でいう cluster-autoscaler.kubernetes.io/safe-to-evict
    • Karpenter でいう karpenter.sh/do-not-evict

[長期] Eviction / 停止の流れ

現在の Kubernetes エコシステムでは、ノードのライフサイクルの流れが明確に定義されていない。特にノードのデプロビジョニングやノードの安全な停止に関して。upstream の Kubernetes は安全にノードをドレインする方法を書いたドキュメントやノードを安全に停止するための実行コマンドを用意している。しかし、ほとんどの Kubernetes ユーザーにとって有用な停止処理を宣言的に定義する方法がまだ確立されていない。例として、Node Maintenance Operator (OpenShift) は、ユーザーが定義した入力と構成によって異なる CRD を定義して、ドレインを宣言的に管理する。一方で Karpenter は、ノードの fainalizer を使って宣言的にドレインを実行する。

様々な異なる観点があるものの、我々はノードのデプロビジョニングと安全な停止に関する単一で共通の解決策を探りたい。

特に、上述の node-lifecycle.kubernetes.io の namespace として追加する Taints をベースにした安全にノードを停止できる宣言的な方法を検討した。

  1. ノードの削除候補となったら、node-lifeycycle.kubernetes.io/disruption=candidate:PreferNoSchedule の Taints をノードに付与する
  2. ノードを削除すると決まったら、node-lifeycycle.kubernetes.io/disruption=disrupting:NoSchedule の Taints をノードに付与する。代替のノードが起動するまでの時間を確保する。
  3. 古いノードが安全に停止できたら、node-lifeycycle.kubernetes.io/disruption=disrupting:NoSchedule に耐性のない Pod のドレインを開始する
    • 2つのポッドの優先順位が同じ場合、Pod は優先順位と逆順 (優先順位の低い Pod が先) で退避され、通常のワークロードの Pod は DaemonSet の Pod より先に退避される。
  4. 対象となるノードが全て Taints されたら、node-lifeycycle.kubernetes.io/disruption=disrupting:NoExecute の Taints を追加して、残りのノードを強制終了する
  5. ノードから退避可能な全ての Pod が退避できたら、クラウドプロバイダーの API を呼び出してインスタンスを終了し、ノードを削除する

これは CAS と Karpenter を念頭に置いた安全なノードの停止を管理する仕組みの 1 つだが、エコシステム内には他にも提案やアイデアが存在する。特に、現在 Red Hat OpenShift から Declarative Node Maintenance に関する KEP が挙げられている。我々が upstream で統一した解決策に辿り着くためには、これらのアイデアをより深く探究し続けたいと考えている。