背景
業務でEKSを含むアーキテクチャの設計構築を任されました。しかしながら周りにEKS経験者がおらず独学で構築方法も学びました。そしてデプロイ後も保守性の高くなるようなEKSの構築方法に大分悩まされました。
そこで直面した課題や解決方法を共有します。
はじめに
具体的なソースコードはGitHubリポジトリで公開しています。
ここでは、試行錯誤の道のりみたいなものを書きます。
今回のゴール
以下のようなEKS on Fargateを含むアーキテクチャを作成したい。
(パブリック/プライベートサブネットは2AZに跨ぐ必要あるのでそれぞれ2つずつ作っているが、図では簡略化)
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テンプレートで構築できるようサポートされている。(ドキュメント)
・一方、EKS内のアプリケーションはさすがにCFnで作るわけにはいかず、マニフェストで作成・デプロイする必要がある。
EKS側からの構築に頼る場合の問題点
ALBを前段に置く時
EKSクラスターにインターネットから直接アクセスさせるようなことは少なく、クラスターの前段にALBを配置してロードバランシングさせるような構成が多いと思いはず。
この時、AWS Load Balancer ControllerをクラスターにインストールしてEKS側からALBもデプロイすることが一般的な構築方法だと考えられる。(ドキュメントにもこの構築方法あり)
この手法の場合、Ingressマニフェストを記述しkubectl applyするとALBが作成される。そうするとALBが自動的にEKSクラスタと紐付けられる。
最初はこの、**「EKS側からの構築」**アプローチで進めていたが、問題が発生した。
ALB(Ingress)とそれにアタッチするリソースの結合が保てない
要件の一つとして、セキュリティのためALBにWAFをアタッチする必要があった。この時、一つの疑問が生じた。
「Ingressマニフェストを更新してEKS側からALBを更新しても、WAFはアタッチは保持されるんだろうか…🤔」
そこで検証してみたが、不安は的中した。。
$ kubectl apply
でIngressをデプロイし直すとWAFは外れたり外れなかったりした。
これではEKSのバージョンアップ等、運用中に更新が入った時にWAFが外れてしまい、知らず知らずのうちに当初の構成と違うものになる恐れがある。
(バグの可能性あり、今後改善されるかもしれないが)
現実的な構成として、ALBに関連付けられるリソースはWAF以外にもRoute53, ACM, セキュリティグループなどたくさんある。これらとの結合を保持させるようEKS側で実装させるのは難易度が高い。。
(ただ、WAFのアタッチだけならIngressマニフェストのannotationで記述して保持することができます)
CloudFormationで構築してみた
そこで、「ALBをIngressマニフェストでEKS側から構築するんじゃなくて、CloudFormationでAWS側から構築してEKSと結合できないか?」と考えた。これが実現できればCloudFormationの記述でALBに関連づけられるリソース群(WAF, Route53, SG等)との結合保持も担保できる。
しかも現場でよく使っているCFnテンプレートを簡単に使いまわすことができ、Ingressマニフェストを書く必要なくなり工数削減にもなる。
しかし、ここでも問題が発生した…
1. 問題点:TargetGroupが設定できない
CloudFormationで作成したALBと、EKS側で(マニフェストで)作成したEKSアプリケーションの結合がうまくできなかった。
EKS on Fargateを用いている場合、ターゲットグループでIPタイプでバックエンドを指定する必要がある。
AWS Load Balancer ControllerでALBをデプロイした際は、この結合をEKS側がよしなにやっており、起動中のPodのIPアドレスを自動でターゲッティングしてくれていた。
しかし、今回ターゲッティングは自動でしてくれない。PodのIPは動的に変わるから手動で指定することも不可能。
2. 解決策:TargetGroupBindingを使用
何か解決策はないか…。とネットを彷徨っていたら、バッチリのものを見つけました。
TargetGroupBinding
これはAWS Load Balancer Controllerのカスタムリソースで、ターゲットグループARNと、ALBにアタッチされているセキュリティグループを書いたマニフェストをデプロイすることで、以下のように実行中のPodを自動でターゲッティングしてくれるというもの。
これにより、AWS LoadBalancerControllerは使うものの、ALB自体ではなくPodターゲッティングだけEKS側に任せて、ALBはCloudFormationで管理できるようになった。
# まとめ
・ALB-EKSを連携する場合、AWS LoadBalancer ControllerでALBをデプロイする方法が一般的だと思われるが、その時にはALBと他リソース(WAF, Route53等)のアタッチ保持問題が発生する。
・そこで、CloudFormation等AWS側でALBをデプロイし、TargetGroupBindingによりEKSと結合させる手法をとった。
**・これにより、以下2点の問題が解決できた。
- ALBとそれにアタッチするリソースとの結合をEKSアップデートがあっても保持させる。
- 従来の(EKSがないアーキテクチャでの)オペレーション通り、大半のリソースはCloudFormationで管理し、アプリケーションに関連する部分だけK8sマニフェストで記述して構築、管理の工数を削減。**
# 最後に
今回の構成のCloudFormationやマニフェストファイル、デプロイ手順は私のGitHubリポジトリで公開しています。
参考までにどうぞ。
参考1:AWS Load Balancer ControllerのTargetGroupBindingを試す
参考2:TargetGroupBinding を使って AWS EKS で Kubernetes を無停止かつ DNS 切り替え無しでバージョンアップしました