ClusterAutoscaler(以下CA)を採用した際にEKSのNodeGroupの分け方について気をつけないといけないことがあるので記事にします。
それは NodeGroupはZoneごとに分けたほうがいい ということです。
Zoneごとに分けたほうがいい理由
公式が推奨している
まずAWS公式でそのような記載があります。
複数のノードグループを構成し、各グループで 1 つのアベイラビリティーゾーンにスコープし、--balance-similar-node-groups 機能を有効化することをお勧めします。ノードグループを 1 つだけ作成する場合には、そのノードグループを複数のアベイラビリティーゾーンにまたがるようにスコープします。
スポットインスタンスを使わないかつ小規模な環境であればNodeGroupは1つでいいと思うので、Zoneごとに分けなくてもよさそうです。
スポットインスタンスを使う場合は停止リスクを考えインスタンスタイプの選択肢を増やさないといけないため、複数のNodeGroupが必要になってきます。
うまくPodのスケジューリングができない場合がある
PodのTopology Spread Constraintsを使って、ZoneごとにPodを分散配置したい場合に問題が起こることがあります。
それがPod.spec.topologySpreadConstraints.whenUnsatisfiable
が DoNotSchedule
の場合です。
CAはPodのスケジューリングが可能なNodeを探索するとき
NodeGroupからテンプレートノードというものを選択します。ここで得られたノードの情報がそのNodeGroupのサーバーのスペックと見なされます。
このノードの情報の中にZoneも含まれます。
たとえばNodeGroupが1つあったとします。このNodeGroupはa/c/d3つのZoneにまたがっているとします。
NodeGroupに2つのNodeがいたとして、それぞれaとc zoneに配置されているとします。
このテンプレートノードを取得する際にc zoneのノードが選択された場合、そのNodeGroupはc zoneのインスタンスが配置されていると判定されます。
ここでPodの数が1つ増え、d zoneのNodeにPodをスケジューリングしないといけなくなったときに
CAにはd zoneのNodeを生成してほしいわけですが、d zoneに属するNodeGroupは(CAの中では)存在しないので
スケールアウトされず、Podもスケジューリングされない状況となります。
ZoneごとにNodeGroupを分けるときの注意点
2つ注意点があります。
- NodeGroupが多くなるとスケールアウトに時間がかかる場合がある
- 単純にZoneごとにNodeGroupを分けてもうまくバランシングしてくれない
NodeGroupが多くなるとスケールアウトに時間がかかる場合がある
AWS公式が言及しています。
ノードグループの数を減らして、大規模なクラスタでの Cluster Autoscaler のパフォーマンスを向上させることができます。個々のチームまたはアプリケーションベースでノードグループを構造化する場合には、この作業は難しいかもしれません。これは Kubernetes API で完全にサポートされていますが、スケーラビリティに影響を与え得る、Cluster Autoscaler のアンチパターンとして見なされます。
これはNodeGroupの数が増えることによって、スケールアウトする際にどのNodeGroupを選択するかという計算が増えることによるCAのパフォーマンス劣化によるものだと考えられます。
とはいえ極端に多くなければそこまで影響は出ないと思いますが、いずれにせよスケールアウトの時間の検証が必要です。
単純にZoneごとにNodeGroupを分けてもうまくバランシングしてくれない
--balance-similar-node-groups
というオプションをCAに付与すると、スケールアウトする際に選択されたNodeGroupと似ているNodeGroupを探し、それらのNodeGroupで起動するノードをバランシングしてくれます。
この 似ているNodeGroup というのがクセモノで、デフォルトだと以下のような定義をされています。
- 各リソース(CPU・Memoryなど)のCapacityが一緒
- 割当可能(Allocatable)なリソースの差が5%以内
- 空きリソース(AllocatableからDaemonSetやkube-proxyが使用するリソースを引いたもの)が5%以内
- 同じLabelを持っている(zone/hostname除く)
NodeGroupでノードを起動すると様々なLabelが付与されています。
Labelには instanceType
の情報が含まれるため、似ているNodeGroupと判定されないことがあります。
たとえば以下のようなインスタンスタイプで起動するNodeGroupがZoneごとにあるとします。
これらのインスタンスタイプはCPU/Memoryは同じです。
- c5.large
- c6i.large
- c6a.large
3つのNodeGroupはそれぞれ1つずつノードが起動されており以下のような状況になっているとします。
zone | 起動しているノードのinstanceType |
---|---|
a | c5.large |
c | c6i.large |
d | c5.large |
CAは各NodeGroupでテンプレートノードを取得するという話をしました。
このテンプレートノードの情報を元にすると、CAはa/d zoneのNodeGroupが似ているNodeGroupと判定します。
この状態で3台追加でスケールアウトしないといけなったときに、CAは以下のようにスケールアウトするNodeGroupを選択します。
- どのNodeGroupをスケールアウトさせるかを計算し、a zoneのNodeGroupを選択する。
- a zoneのNodeGroupと似ているNodeGroupを探す。d zoneのNodeGroupが対象になる。
- a/d zoneのNodeGroupをバランスよくスケールアウトさせる
ということで、c zoneのNodeGroupはスケールアウトされず、アンバランスなノードの配置になってしまいます。
このようなことを回避するために CAの1.25から追加された --balancing-label
が使えます。
これは似ているNodeGroupを判定するためのラベルを任意で指定できます。
この設定がある場合、上記で記載した似ているNodeGroupを探すためのデフォルトの動作はしなくなります。
各NodeGroupに以下のようなラベルを持たせ、--balancing-label
で指定すれば各Zoneバランス良くNodeが配置されます。
- example.com/cpu: 2
- example.com/memory: 4