Karpenter のベータAPIがリリースされたので調べてみた。


:::note
Karpenter は、AWSが開発するKubernetes 向けのノードライフサイクルマネージャーです。私はAWSで働いていますが、この記事に記載されている内容はAWSとしての主張ではなく、あくまでも個人の意見/感想であるということをご了承ください。間違いなどがありましたら、コメントいただけますと幸いです。
:::

以下のブログにあるように、ついにKarpenterは、betaになりました。おめでたい！

https://aws.amazon.com/jp/blogs/containers/karpenter-graduates-to-beta/

現在、Karpenterは、SIG Autoscaling のサブプロジェクトとして、Cloud Native Computing Foundation (CNCF) に寄贈されるプロセスにあります。 

そして、この beta がリリースされるにあたって、Kubernetes APIに対して大きく変更が行われました。
これまで、Karpenter を使っている方は、Provisioner　AWSNodeTemplate というCRを作成してConfigurationしていたわけなんですが、このCRがどちらも廃止されました。

代わりにNodePool・EC2NodeClassというカスタムリソースができました。
これまでと、設定はそれほど大きく変わらないんですが、APIの設計が刷新された格好です。

移行が大変だなと感じる方もいらっしゃるかもしれませんが、[karpenter-convert](https://github.com/aws/karpenter/tree/main/tools/karpenter-convert)というCLIツールを提供していて、既存のカスタムリソースの設定のまま移行することができます。
設定値の名前や内容は基本的には変わっていないので、それほど障壁になることはないのではないかと思います。

また、破壊的変更について批判的な意見が出ることも理解できますが、破壊的変更の利点と比較して考慮する必要があると思います。今回は、破壊的変更を1つのバージョンアップにまとめることで、この負担を最小限に抑えることができます。

では、Karpenterの変更内容について大きなものをピックアップして調べてみました。

## Admission Webhook を廃止して、CEL(Common Expression Language)の導入

まず、Karpenterチームが抱えていた課題として、Admission Webhookに関する問題報告が多かったことが前提としてあります。([#4415](https://github.com/aws/karpenter/issues/4415)、[#3598](https://github.com/aws/karpenter/issues/3598)、[#2902](https://github.com/aws/karpenter/issues/2902)、[#4154](https://github.com/aws/karpenter/issues/4154)、[#4106](https://github.com/aws/karpenter/issues/4016)、[#3224](https://github.com/aws/karpenter/issues/3224)、[#1729](https://github.com/aws/karpenter/issues/1729)など)

そこで、Admission WebHook に代わるよりシンプルな仕組みを求めていました。

https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/2876-crd-validation-expression-language/README.md
こちらの KEP に詳しくまとまっていますが、CRDで完結するようなValidationであれば、Validation WebHookを使わなくても、十分な検証ができるようになりました。

こちらは、Kubernetes 1.25 で、beta となりましたので、EKSでも利用可能になりました。ということで、したがって、Karpenter v1beta1 では CRD の OpenAPISpec に CEL を導入します。
ただし、現状は、EKS バージョンが 1.24 以下もサポート期間です。そのため現状のKarpenterは、Validation Webhookと両立をしています。サポート期間終了後は Webフックを廃止し、CEL のみで検証を行います。

実際の実装を見るとわかりやすいので、実装箇所もリンクしておきます。

- NodePools
    - https://github.com/aws/karpenter/blob/4ea4808997d03f50774674909462f008519e895c/pkg/apis/crds/karpenter.sh_nodepools.yaml#L237-L279
- NodeClaims
    - https://github.com/aws/karpenter/blob/4ea4808997d03f50774674909462f008519e895c/pkg/apis/crds/karpenter.sh_nodeclaims.yaml#L185-L225


より複雑なルールになってきた時に管理しやすいかはともかく、
サクッと型定義をするくらいの目的だとかなり簡単に書けます。

記法については、以下のページに記載されています。
https://github.com/google/cel-spec/blob/master/doc/langdef.md
https://kubernetes.io/docs/reference/using-api/cel/

## consolidationPolicy

これまでは、Karpenterには空であるノードをdisruptionする際に`ttlSecondsAfterEmpty`を決めることで、どのくらいの速度で、ノードを退避させるかを決めることができました。

`ttlSecondsAfterEmpty`は、ノードが空になってから、何秒経過したら実際にそのNodeを退避させるかを決めることができるspecです。

betaでは、この設定が、`consolidationPolicy`を利用することで実現できます。
`consolidationPolicy`は、`WhenEnpty`または、`WhenUnderutilized`の二種類から選択できます。
`WhenEmpty`を使用する場合、Karpenter はワークロードポッドを含まないノードのみをconsolidation対象と見なします。
また、Karpenter がのempty node を発見してからどのくらいの時間を待つ必要があるかについては、`consolidateAfter`で設定をすることができます。

`WhenUnderutilized`を使用する場合、Karpenter はすべてのノードをconsolidation対象として検討し、ノードが十分に活用されておらず、コスト削減のために変更できることがわかったら、ノードの削除または交換を試みます。

`consolidationPolicy`が設定されていない場合、Karpenterは暗黙的に`WhenUnderutilized`にデフォルトで設定します。Podの終了に大きく時間がかかる場合など、PodのChurnを避けたい場合は、明示的に`WhenEmpty`を指定する必要があります。

## NodeClassのAMI、サブネット、SGのセレクター

従来のセレクターの設定では、AND ロジックが適用されるため、テスト環境などの異なるクラスタに対して、異なる設定を注入する必要がありました。今回のアップデートでは、 OR ロジックを使って組み合わさるようになり、複数の設定を入れることができるので、マニフェストが共通化できます。

複数の AMI と一致する場合、Karpenter は次の制約のもとで、起動したワーカーノードのワークロードに最も適した AMI を自動的に判断します。
Karpenter はカスタム AMI がどのアーキテクチャと互換性があるかを自動的に判断し、InstanceType の要件に一致するイメージを使用します。
使用可能な AMI が複数見つかった場合、Karpenter は最新のものを選択します。
使用できる AMI が見つからない場合、ノードはプロビジョニングされません。

複数のサブネットがヒットする場合は、最適なAZに一致するサブネットが自動的に選択されます。1 つのAZに複数のサブネットが存在する場合、使用可能な IP アドレスが最も多いサブネットが使用されます。

複数のSGがヒットする場合には、セレクターと一致するすべてのセキュリティグループを使用します。

## `spec.launchTemplate`の削除

直接の起動テンプレート指定する方法では、多くの課題に直面していました。
Karpenterの半端なエクスペリエンスをユーザーに提供するのではなく、Karpenterが管理する起動テンプレートのみをサポートすることとなりました。
起動テンプレートを直接指定できませんが、AMIやユーザーデータなどはKarpenterの設定で自由に指定できます。

## Karpenter によって管理された インスタンスプロフィール

Karpenterによって起動するEC2インスタンスのインスタンスプロフィールを設定する方法は、`spec.instanceProfile`を設定する方法と、`spec.role`を設定する方法の二種類があります。

`spec.role`を利用すると、Karpenterがインスタンスプロフィールを管理します。つまり、指定された`role`がアタッチされた`instanceProfile`をアドホックにKarpenterが作成します。
逆に言えば、InstanceProfileを利用するときは、従来通り、InstanceProfileをKubernetes外で定義してそれを指定する必要があります。

`spec.role`を利用する場合、インターネット(IAM API)への経路と、そのロールを操作するための権限がKarpenterに必要になる点は注意する必要があります。