yoshikenがプログラミングや日記、ポエムを書いていくページです


AWS EKSのAWS ALB Ingress Controllerを破壊的変更を加えずにAWS Load Balancer ControllerにアップデートかつELBをまとめる方法

AWS ALB Ingress Controller to AWS Load Balancer Controller自体は割りと簡単

前提

現在面倒をみているプロダクトがEKSv1.19→v1.21にアップデートをした。事業部専属というわけではなくてSREとして別プロダクトばっかりみてたらサポート残り1ヶ月の状態で「あ、やべ。残り一ヶ月でEKS強制アプデ入るわ」となり、やらなくては~となったがプロダクトのフロントエンドの子が「やってみたいです!」というので他のレイヤーに対する熱意◎でまかせたら、会社都合でその子が部署異動で「開発環境はアプデ終わりましたが本番はできませんでした…後はよろしくお願いいたします…」とバトンを渡され虚構に陥りながら開発のリハ無しに本番のアップデートをした。まぁよくある話っすね

基本的には弊社のSREの方針としては運用は各事業部に任せることが前提とはいいつつほぼほぼノールックだったのは反省点。

で、昔に1.19に上げた時に各APIリソースのアップデートもそこまでしておらず、残り時間と加味してリソースのアップデートの検証もできなかったので最新の1.22ではなく現行のリソースで最大ギリアップデート可能な1.21にした。

1.22のアップデートに向けて早めに手を打とうとAPIリソースのアップデートを隙あらばやっていたが、ALB Ingress ControllerがAWS Load Balancer Controllerというものに変わるらしく、今まで地味に不便だった1service⇔1ELBをNservice⇔1ELBにできるのでついでにやってしまおう。そう、ダウンタイムゼロで。

という前提の元、話を進めていきます。

まずはともあれIngressのアップデート

extensions/v1beta1 から networking.k8s.io/v1 にアップデートしないと1.22にアップデートできないうえ、AWS Load Balancer Controllerが使えない。

と言うこと自体簡単だが地味にハマるポイントがあり、k8s公式ドキュメントのパスタイプを見ると / ですべてのリクエストを受け入れる設定になる。

https://kubernetes.io/ja/docs/concepts/services-networking/ingress/#%E3%83%91%E3%82%B9%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%97

だがAWS ALB Ingress Controllerは /* じゃないとすべてのリクエストを受け入れてくれないわけですよ。

なので公式ドキュメントどおりに / にすると / しか受け付けないELBが爆誕します。 (e.g. example.com は受け付けるが exmaple.com/hoge は受け付けなくなる

ぐっとこらえて /* のままアップデートしましょう

サンプルコードはこんな感じです

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example.ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:123456789:certificate/hogehogehoge-fugafugafuga-piyopiyopiyopiyo-poyopoyopoyopoyo-pukapukapuka
spec:
  rules:
    - host: "example.com"
      http:
        paths:
          - path: /*
            pathType: Prefix
            backend:
              service:
                name: ssl-redirect
                port:
                  name: use-annotation
          - path: /*
            pathType: Prefix
            backend:
              service:
                name: example-service
                port:
                  number: 80

rules 配下の host は必須かと言われると必須ではないんですが、AWS Load Balancer ControllerでELBまとめるときに必要なので今のうちに書いておくと忘れなくて良いです。

AWS Load Balancer Controller インストール

基本的に公式ドキュメント通りにやれば問題ないです。(それはそう

ただPolicy作成時に既存リソースを破壊させないようにちょこっと弄る必要があります

流れは

  1. EKSのOIDC発行(すでに発行済みならスルー
  2. AWS Load Balancer Controller用のIAM policyを公式ドキュメントどおりにDL
  3. 既存リソースを破壊しないように弄る *1
  4. Policy作成
  5. AWS Load Balancer Controller用のIAM作成
  6. AWS ALB Ingress Controllerの削除
  7. AWS Load Balancer Controllerをインストール(fargate化を少しでも考えてるなら大人しくhelmにしましょう

こんな感じ。

んで、普通に公式ドキュメントどおりにやるとAWS ALB Ingress Controllerで作成されたリソースを書き換えようとするんですが、今回は既存リソースをそのままにしたかったのでちょこっとPolicyをいじります。

公式の

curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.2/docs/install/iam_policy.json

となっているPolicyに次のような感じで追加します

``` [ .json .code] "Condition": { "Null": { "aws:ResourceTag/kubernetes.io/cluster/CLUSTER-NAME": "false" } }

これで既存リソースはいじられないようになります(たぶん

意味は「aws policy Condition」あたりでぐぐってください

### まとめたELBを作成

簡単! `annotations` に `alb.ingress.kubernetes.io/group.name` 追加するだけ!!!!!!!!!!

```{ .yaml .code }
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mamadays.server.loadbalancer
  annotations:
    alb.ingress.kubernetes.io/load-balancer-attributes: access_logs.s3.enabled=true,access_logs.s3.bucket=elb-log-s3-bucket,access_logs.s3.prefix=log-s3-prefix
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:123456789:certificate/hogehogehoge-fugafugafuga-piyopiyopiyopiyo-poyopoyopoyopoyo-pukapukapuka
    alb.ingress.kubernetes.io/group.name: matometa-elb

正直開発環境のELBが無駄に立ち上がってるのは地味に費用面でイラッとしてたので助かる。

一応デメリットがあって、今のところlogのs3のprefixがserviceで分けられないので面倒ではある。

弊社だとelbのlogはs3に置いて、athenaで分析してるんですがscan量増えて料金増えるな~と思いつつ減らしたELB分考えれば+かなと判断してやってます。

ちなみにさっきの rules 配下の host にhostname書いておかないと大惨事になりますのでちゃんとつけましょう

route53で切り替え

これで AWS ALB Ingress Controllerで作成されたELBsとAWS Load Balancer Controllerで作成されたまとめられたELBが立っているのでroute53の加重ルーティングあたりで切り替えていきましょう。

FAQ

自分で疑問に思ったり同僚に質問されたこと

Q. AWS ALB Ingress Controller消したら既存のELBとか削除されないの?

A. あいつらの仕事はkind Ingressを監視してリソースの追加と削除するだけで、作ったリソース自体を常時監視している(紐づいている)わけではないので問題なし

Q. kustomizeで管理してるからmanifestで管理したいんだけど。

A. https://github.com/jetstack/cert-manager/releases/download/v1.5.4/cert-manager.yaml と https://github.com/kubernetes-sigs/aws-load-balancer-controller/releases/download/v2.4.2/v2_4_2_full.yaml を見て「管理できる!」って自信持ったらやってください。僕は諦めました。あとfargate対応考えるとねぇ。

Q. なんかAWS ALB Ingress Controllerで作成されたingressが消せないんだけど。消せないっていうかレスポンス帰ってこないんだけど。

A. わかる。僕も半日ぐらい潰れました

結論から言うと finalizers をemptyにするとOK

簡単にいうと旧ingressが無のfinalizersを掴んでて、旧ingressを消そうとすると、旧ingressが「無を消してからワイ消えますわ!」って状態になり無は無なので無を消せずにレスポンス帰ってこないってわけです。

https://github.com/kubernetes/kubernetes/issues/95983

なんで、finalizersを空で上書きすることで「ワイ何も持ってないのでそのまま消えますね~」って感じで消えてくれる

$kubectl get ingress [ingress-loadbalancername] -o yaml -n development
(略)
finalizers:
  - ingress.k8s.aws/resources
(略)

みたいな感じで残ってるので $kubectl patch ingress [ingress-loadbalancername] -n development -p '{"metadata":{"finalizers":[]}}' --type=merge おりゃ!ってすればemptyで上書きします。

一応 -wait false つければレスポンス待たずに消えるらしいのでお好みで。

ちなみにsystem:admin権限でも消せなかったので特級呪物って呼んでました