EKS Cluster v1.22以降の作り方がCDKで変わった話

:::note info
この記事は[AWS CDK Advent Calendar 2022](https://qiita.com/advent-calendar/2022/cdk)を後から埋めています。
:::

# はじめに

CDKのConstrunctsを利用してEKS Clusterを起動する方法は、今までこんな設定が最小だったのですが、

```ts:CDK v2.47.0 以前
// provisiong a cluster
const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_21,
});
```


2022/10/28(＝CDK v2.48.0)以降のQuickStartでは、下のように外部モジュールをインポートして`kubectlLayer`を指定する方式となっています。
- v2.55.1現在ではv1.24相当の記述に置き換わっていますが、Optionやモジュールのインポート方法は同様です。


```ts:CDK v2.48.0 以降
import { KubectlV22Layer } from '@aws-cdk/lambda-layer-kubectl-v22';

// provisiong a cluster
const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_22,
  kubectlLayer: new KubectlV22Layer(this, 'kubectl'),
});
```

https://github.com/aws/aws-cdk/pull/22604/


今回はこのアップデートの裏にある

1. `kubectlLayer`とは何か？ `eks.Cluster`に必須なのか？
2. どうして2021.XXリリースのEKS v.1.22が2022.11まで指定できなかったか

という２点について把握している範囲でご紹介していきます。

## 1. `kubectlLayer`とは何か？ 

唐突ですが、AWS Lambdaには、複数のFunction間で外部ライブラリ、独自モジュールなどを共有することを主な目的とする、`Lambda Layers`という機能があります。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html

`kubectlLayer`はKubernetesクラスターを制御する標準のコマンドラインツールである`kubectl`と、Kubernetesのパッケージマネージャーである`helm`をLambda Layersで用いるためにプリセットされた、CDK独自のクラスです。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.lambda_layer_kubectl.KubectlLayer.html

とはいえ、この説明では、「LambdaからK8s(EKS)を操作するための仕組みが、どうしてEKS Clusterの構築に必要なのか」という点がわかりません。もう少し掘り下げてみましょう。

## 2 `eks.Cluster`に必須なのか？ 

CDKでは下記のように「EKS Clusterのみ」を作成する場合は、kubectlLayerの設定なしでも構築が成功します。

```ts
    const vpc = new ec2.Vpc(this, 'vpc', {
      natGateways: 0,
    })
    const cluster = new eks.Cluster(this, 'Cluster',{
      version: eks.KubernetesVersion.V1_24,
      // kubectlLayer: new KubectlV24Layer(this, 'kubectl'),
    })
```

構築の際、暗黙的にKubectlLayer入りのLambdaも展開されます。ちなみにkubectlLayerをコメントアウトしているので、この場合はDefault値のKubectl(v1.20)が設定されます。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_eks.Cluster.html#kubectllayer


このリソースはどんな場合に利用されるのでしょうか？

例えばCDKでは上述のクラスター定義に加えて、以下のような定義もできます。

```ts
// apply a kubernetes manifest to the cluster
cluster.addManifest('mypod', {
  apiVersion: 'v1',
  kind: 'Pod',
  metadata: { name: 'mypod' },
  spec: {
    containers: [
      {
        name: 'hello',
        image: 'paulbouwer/hello-kubernetes:1.5',
        ports: [ { containerPort: 8080 } ],
      },
    ],
  },
});
```

これは、CDKで表現したコードを元にAWSのAPIでなく、EKS Clusterが持つAPI(kube-apiserver)へアクセスし、設定を更新するという意味になります。

今回はCDKで表現したManifestをクラスターへ適応させるものでしたが、本例に限らずEKS ClusterにおいてKubernetes固有のエコシステムを用いた設定を行う場合は、AWSのAPIでなくKubernetesクラスターのAPI経由での設定が必要です。したがってCDKで定義できる一部の設定作業に必要なkubectlを導入するためのKubectlLayerと、その実行基盤であるLambdaが、EKS Clusterの設定用にデプロイされているわけです。

ちなみにimportした外部モジュールをkubectlLayerとして明示的に指定する作法はv1.22からですが、その前バージョンであるv1.21は2023.2にEOL予定です。

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/kubernetes-versions.html


すなわち、本ブログの公開日時2022/12/22から2か月後には、このkubectlLayerを指定する仕様への対応が必須になります。どうして唐突にこんな仕様になったのか？と次の疑問が湧いてきます。


## 3. EKS v1.22が指定できていなかったわけ

そもそも2022/4/4に登場したv1.22をCDKが半年近く扱えていなかった理由は何なのでしょうか。この問いについてはこちらのIssueが参考になります。

https://github.com/aws/aws-cdk/issues/20263


- KubectlはVersionを持ち、前後1つ違うVersionのKubernetes Clusterに対応している。
    - 標準の`lambda-layer-kubectl`はkubectl v1.20が組み込まれており、k8s v1.19~1.21までに対応されている。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.lambda_layer_kubectl-readme.html

- 一方でAWS CDKとしてパッケージのサイズを減らす取り組みをしており、標準モジュールとしてkubectl v1.22をバンドルすることがためらわれていた。
- 
https://github.com/aws/aws-cdk-rfcs/pull/430

- このため今後の対応も含めて、以下の課題を解決する方針を検討しなくてはならない。
    - `lambda-layer-kubectl`にkubectl v1.22を導入する＝CDKとしてのパッケージサイズを肥大化させるので難しい
    - kubectl v1.22に対応しないままEKS v1.22へ対応する＝Kubectl＋Lambdaを前提とした、CDKでのEKS Cluster管理ができなくなる破壊的変更となってしまう。
        - CDK v2からは原則、破壊的変更は避ける開発方針。 
  
そんなこんなで2022/5月から水面下での検討が続いていた様子ですが、ここでEKSのライフサイクルを思い出してみましょう。

- Release: v1.22: 2022/4, v.1.23: 2022/8, v1.24: 2022/11
- EOL: v 1.19: 2022/8, v 1.20: 2022/11

つまり2022/8のとある時点では、CDKにおけるEKS Clusterはv 1.20 or v1.21しか扱えない状況だったのです。このまま11月を迎えるとv1.21しか使えなくなる、そもそもこんな状況で今後EKSを扱うチームはCDKを扱うのはかなり厳しくなるんじゃないか、というコメントが殺到しました。(まあ当然かと……)

## 4、現在の状況
それを受けて、というわけかどうかは定かではありませんが、v1.20がEOLになるギリギリ直前の10月末に、v1.22がリリースできるアップデートが入りました。これが冒頭で紹介したCDK v2.48.0です。


これがきっかけで、
- KubectlLayer部分は外部モジュールとして、以下に切り出されました。
https://github.com/cdklabs/awscdk-asset-kubectl

- KubectlLayerを明示的に指定するのがほぼ必須となりました。

というのが本件の顛末です。CDK v2.54時点ではEKS v1.24まで対応しており、しばらくは安心してEKSをCDK管理できるようになりました。めでたしめでたし。

この状況が解決するまでは静観していた、Amazon EKS Blueprints for CDKもそろそろ触ってみようかなと思っています。


https://github.com/aws-quickstart/cdk-eks-blueprints


# おわりに

CDKユーザとしては、とりあえず以下を意識しておくと良さそうです。

- EKSを管理する際は今後もCDKを利用できそう。ただし、外部モジュールがほぼ必須。
- 更新状況は要注意。Kubectlは1バージョン違いまでなら吸収できるので、万が一、EKS v1.25がすぐにリリースされなくても、しばらくはこんな記述で許容可能。