👻

AWS Load Balancer ControllerがIngressに対応するリソースをプロビジョニングする部分を読む

2022/12/23に公開

この記事はKubernetes Advent Calender 2022の23日目の記事です。

AWS Load Balancer Controllerとは

AWS Load Balancer Controller(以下ALBC)はIngress/Service type LoadBalancerをElastic Load Balancer(ELB)を使ってプロビジョニングしてくれるコントローラーです。

IngressリソースにはALB、Service type LBにはNLBをプロビジョニングしてくれます。
EKSを採用しているプロダクトでは広く使われていると思います。今回はALBCがIngressに合わせてAWSのリソースをプロビジョニングしている部分のコードをざっと追ってみます。


How it works - AWS Load Balancer Controller

Ingressリソースに対するALBCの操作

Ingressリソースが作成されるとALBCが作成イベントをwatchしてAWSのリソース作成を開始します。
Ingressには様々なアノテーションを設定することができ、IngressにWAFやACMの証明書を簡単に紐づけることができます。

ALBCははじめに、IngressにつけられたALBC用のアノテーションに対応するようにALBリソースを作成します。

ABLリソースを作成した後は、ALBからIngressのTargetへ通信ができるように、TargetGroupやALBのListernersを作成します。httpsを使用する場合にはアノテーションで指定された証明書もアタッチしてくれます。

パスごとのルーティング設定がある場合には、Listener Rulesの設定も行われます。また、Ingressの削除時にも作成時とは真逆に上記のリソースの削除もコントローラーが行ってくれます。

コードリーディング

今回コーディリーディングに使用したコミット: https://github.com/kubernetes-sigs/aws-load-balancer-controller/tree/edeb4f1c1312f2e2915ded595fcef8545501878a

起点になるReconcileはこの部分になります。ここから読んでいきます。

https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/edeb4f1c1312f2e2915ded595fcef8545501878a/controllers/ingress/group_controller.go#L114-L116

controllerの実装がいまいちわからない方は「つくって学ぶKubebuilder」という資料が非常に参考になるのでこちらを先に読むと理解が早いかもしれませんが、最初以外は特に登場しないので気にしなくても大丈夫です。

https://zoetrope.github.io/kubebuilder-training/

この記事ではIngressリソースに関連するコードにのみ焦点を当てていますが、Serviceリソースに対応するreconcile処理を見たい時にはServiceに対応したControllerのReconcile関数を確認するとServiceリソースに対するコントローラーの処理内容が書いてあります。(↓)

https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/edeb4f1c1312f2e2915ded595fcef8545501878a/controllers/service/service_controller.go#L86-L88

IngressGroupについて

groupReconcilerのgroupというのはIngressGroupのことです。IngressGroupは複数のIngressをまとめる単位で、Ingressにalb.ingress.kubernetes.io/group.nameのアノテーションを指定することで指定します。

同じIngressGroupに所属するIngressは同じALBに所属し、各IngressへのルーティングはListener Rulesがそれぞれ設定されます。

IngressGroup名はalb.ingress.kubernetes.io/group.nameというアノテーションで指定することができます。controllerでは以下の関数でIngressGroup名を取得しています。
https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/edeb4f1c1312f2e2915ded595fcef8545501878a/pkg/ingress/group_loader.go#L239-L261

AWSリソースのスタック計算とデプロイ

Ingressリソースに対応するAWSのリソースのスタックはこの関数の中で作成されています。スタックは前述したようにreconcile対象のIngressが所属するIngressGroupごとに行われるようになっています。

https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/edeb4f1c1312f2e2915ded595fcef8545501878a/pkg/ingress/model_builder.go#L105-L156

同一IngressGroupは同じLBにまとめられるので、該当のIngressGroupに所属する各Ingressに対して計算が行われています。設定している項目は多いですが、LBだけでなくListener、ListnerRuleもIngressに対応するように設定されています。

https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/edeb4f1c1312f2e2915ded595fcef8545501878a/pkg/ingress/model_builder.go#L208-L277

上記で計算されたスタックを元にAWSの各サービスのリソースの作成、アプデートを行っている部分は以下になります。各リソースに対してSynthesizerというinterfaceを満たすstructが用意されており、必要なSynthesizerをsliceにまとめてforで順番に回しています。この順番もリソース間の依存関係に沿って決まっていそうです。

https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/edeb4f1c1312f2e2915ded595fcef8545501878a/pkg/deploy/stack_deployer.go#L85-L122

AWSのリソースの操作が完了したあとで、新規に作成されたLBがあった場合はLBのDNSを取得してIngressのHostNameの更新を行なっています。taskの初期ではnilになっているので作成された新規作成されたLBがない場合は特に何もしないようです。

https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/edeb4f1c1312f2e2915ded595fcef8545501878a/controllers/ingress/group_controller.go#L199-L214

おわり

駆け足になりましたが、これでALBCがIngressに対応したAWSのリソースを作成している部分のコードを一通り見てみました。
意外とシンプルにまとまっているので尺がだいぶ短くなってしまいましたが、他のCustom ControllerもReconcileからの流れは同じ感じになっているものが多いので、もしOSSでクラスタにコントローラー入れてるけど何してるかよくわからないみたいな時はコードをパッとみて実装を確認できるのが良いですね。

Discussion