EKSをできるだけCloudFormationで構築した話

# 背景
業務でEKSを含むアーキテクチャの設計構築を任されました。しかしながら周りにEKS経験者がおらず独学で構築方法も学びました。そしてデプロイ後も保守性の高くなるようなEKSの構築方法に大分悩まされました。
そこで直面した課題や解決方法を共有します。


# はじめに
[具体的なソースコードはGitHubリポジトリで公開しています。](https://github.com/Makoto-Taguchi/EKS-CloudFormation-Setup)
ここでは、試行錯誤の道のりみたいなものを書きます。

# 今回のゴール
以下のようなEKS on Fargateを含むアーキテクチャを作成したい。
（パブリック/プライベートサブネットは2AZに跨ぐ必要あるのでそれぞれ2つずつ作っているが、図では簡略化）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577028/100cd9b0-99a0-4ad5-f865-230ed2bb38d3.png)


# EKSクラスターの構築アプローチ
EKSクラスターを構築する方法は大きく以下の2つあると考えられる。
### 1. EKS側からの構築
**・eksctlコマンド**
EKS関連のインフラ（クラスター、Fargateプロファイル、VPC等）はeksctlで簡単に構築することができる。一番楽に作るとすれば、以下のコマンドだけでEKSの所属するVPC、サブネット、NAT Gatewayなど色々なものが自動的に作れる。

```
$ ekctl create cluster
```

**・kubectl**
EKS内のアプリケーションはマニフェストで構成管理し、kubectlコマンドでデプロイする。

```
$ kubectl apply -f manifest.yml
```

### 2. AWS側からの構築
・CloudFormation等のIaCで構築する。EKSクラスター自体やFargateプロファイルはCFnテンプレートで構築できるようサポートされている。（[ドキュメント](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_EKS.html)）

・一方、EKS内のアプリケーションはさすがにCFnで作るわけにはいかず、マニフェストで作成・デプロイする必要がある。



# EKS側からの構築に頼る場合の問題点
### ALBを前段に置く時
EKSクラスターにインターネットから直接アクセスさせるようなことは少なく、クラスターの前段にALBを配置してロードバランシングさせるような構成が多いと思いはず。
この時、AWS Load Balancer ControllerをクラスターにインストールしてEKS側からALBもデプロイすることが一般的な構築方法だと考えられる。（[ドキュメントにもこの構築方法あり](https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-alb-ingress-controller-fargate/)）
この手法の場合、Ingressマニフェストを記述しkubectl applyするとALBが作成される。そうするとALBが自動的にEKSクラスタと紐付けられる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577028/aac2d7c5-7740-9961-0828-f5573dae3397.png)


最初はこの、**「EKS側からの構築」**アプローチで進めていたが、問題が発生した。

### ALB(Ingress)とそれにアタッチするリソースの結合が保てない
要件の一つとして、セキュリティのためALBにWAFをアタッチする必要があった。この時、一つの疑問が生じた。
「Ingressマニフェストを更新してEKS側からALBを更新しても、WAFはアタッチは保持されるんだろうか…🤔」
そこで検証してみたが、不安は的中した。。
```$ kubectl apply```でIngressをデプロイし直すとWAFは外れたり外れなかったりした。
これでは**EKSのバージョンアップ等、運用中に更新が入った時にWAFが外れてしまい、知らず知らずのうちに当初の構成と違うものになる恐れがある**。
（バグの可能性あり、今後改善されるかもしれないが）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577028/c5af3140-97e2-c4af-e087-ef2a4badfd9e.png)


現実的な構成として、ALBに関連付けられるリソースはWAF以外にもRoute53, ACM, セキュリティグループなどたくさんある。これらとの結合を保持させるようEKS側で実装させるのは難易度が高い。。

（ただ、WAFのアタッチだけなら[Ingressマニフェストのannotation](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.1/guide/ingress/annotations/#wafv2-acl-arn)で記述して保持することができます）



# CloudFormationで構築してみた
そこで、「ALBをIngressマニフェストでEKS側から構築するんじゃなくて、CloudFormationでAWS側から構築してEKSと結合できないか？」と考えた。これが実現できればCloudFormationの記述でALBに関連づけられるリソース群（WAF, Route53, SG等）との結合保持も担保できる。
しかも現場でよく使っているCFnテンプレートを簡単に使いまわすことができ、Ingressマニフェストを書く必要なくなり工数削減にもなる。

しかし、ここでも問題が発生した…

### 1. 問題点：TargetGroupが設定できない
CloudFormationで作成したALBと、EKS側で（マニフェストで）作成したEKSアプリケーションの結合がうまくできなかった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577028/7e68ee13-c768-91f7-db9b-1a38cd12dae7.png)

EKS on Fargateを用いている場合、ターゲットグループでIPタイプでバックエンドを指定する必要がある。
AWS Load Balancer ControllerでALBをデプロイした際は、この結合をEKS側がよしなにやっており、起動中のPodのIPアドレスを自動でターゲッティングしてくれていた。
しかし、今回ターゲッティングは自動でしてくれない。PodのIPは動的に変わるから手動で指定することも不可能。


### 2. 解決策：TargetGroupBindingを使用
何か解決策はないか…。とネットを彷徨っていたら、バッチリのものを見つけました。
[TargetGroupBinding](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.1/guide/targetgroupbinding/targetgroupbinding/)
これはAWS Load Balancer Controllerのカスタムリソースで、ターゲットグループARNと、ALBにアタッチされているセキュリティグループを書いたマニフェストをデプロイすることで、以下のように実行中のPodを自動でターゲッティングしてくれるというもの。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577028/a3d1c991-9970-3d06-7e07-6ce4cf108016.png)

これにより、AWS LoadBalancerControllerは使うものの、ALB自体ではなくPodターゲッティングだけEKS側に任せて、ALBはCloudFormationで管理できるようになった。

#　まとめ
・ALB-EKSを連携する場合、AWS LoadBalancer ControllerでALBをデプロイする方法が一般的だと思われるが、その時にはALBと他リソース（WAF, Route53等）のアタッチ保持問題が発生する。
・そこで、**CloudFormation等AWS側でALBをデプロイし、TargetGroupBindingによりEKSと結合させる手法をとった**。
**・これにより、以下2点の問題が解決できた。
1. ALBとそれにアタッチするリソースとの結合をEKSアップデートがあっても保持させる。
2. 従来の（EKSがないアーキテクチャでの）オペレーション通り、大半のリソースはCloudFormationで管理し、アプリケーションに関連する部分だけK8sマニフェストで記述して構築、管理の工数を削減。**


#　最後に
今回の構成のCloudFormationやマニフェストファイル、デプロイ手順は[私のGitHubリポジトリで公開しています](https://github.com/Makoto-Taguchi/EKS-CloudFormation-Setup)。
参考までにどうぞ。

[参考1：AWS Load Balancer ControllerのTargetGroupBindingを試す](https://qiita.com/k-sasaki-hisys-biz/items/895cd2e3dd9baff45bd8)
[参考2:TargetGroupBinding を使って AWS EKS で Kubernetes を無停止かつ DNS 切り替え無しでバージョンアップしました](https://tech.recruit-mp.co.jp/infrastructure/post-21469/)