ALB Controllerを使うとK8sのIngressリソースと連動してALBをデプロイできます。しかし、Ingressリソースを消してしまうとALBも一緒に消えてしまいます。ALBのホスト名を外部DNSに登録している場合など、ALBを極力消したくないこともあります。
ALB CotrollerでALBを管理さsつつ削除させない方法をいくつか考えました。
まとめ
以下3つの方法を考えました。
-
削除保護の設定: annotationsで
alb.ingress.kubernetes.io/load-balancer-attributes: deletion_protection.enabled=true
を設定する方法です。K8sリソースの削除を実行してもIngressおよびALBは削除されません。一見これで十分なような気がしますが削除を実行したコマンドが返ってこなくなる問題があります。 -
グループの利用: annotationsの
alb.ingress.kubernetes.io/group.name
を活用する方法です。デプロイ用とルール設定用でIngressリソースを分けつつ同じIngressグループを指定します。ルール設定用のIngressを削除してもALBは削除されずルールだけ消えます。 - 外部管理LB: あらかじめALBを作成しておきCRDのTargetGroupBindingでターゲットを管理する方法です。ALBはK8sの管理外になるためK8sの操作ではALBは消えなくなります。
個人的には1と2を組み合わせて使うのが安全で使いやすいやり方だと思います。具体的にはデプロイ用のIngressに削除保護を設定しルール設定用のIngressでルールを設定します。こうすることでALBを使いまわしつつ気軽にIngressを消したりできます。
3の方法はALBやルールの管理をK8s外で管理する必要があったり、Podとの通信許可(セキュリティグループ)にも気を配る必要があります。しかし、異なるEKS間でALBを共有したり、既存のALBを使用したいユースケースに使えます。
1. 削除保護の設定
AWSのリソースには削除を防止する削除保護の設定があります。Ingressのannotationsでalb.ingress.kubernetes.io/load-balancer-attributes: deletion_protection.enabled=true
を設定するとALB ControllerでデプロイするALBに削除保護が設定されます。ALB Controllerのドキュメントにある説明はこちらです。
設定例
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test
annotations:
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/load-balancer-attributes: deletion_protection.enabled=true
spec:
ingressClassName: alb
rules:
- http:
paths:
- backend:
service:
name: test
port:
number: 80
path: /
pathType: Exact
動作確認
上記applyすると以下のようにデプロイされます。
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
test alb * internal-k8s-default-test-0beab48a0d-1864872286.ap-northeast-1.elb.amazonaws.com 80 13s
Ingressを削除します。すると以下のようになるものの、プロンプトは返って来なくなります。(10分ほど待っても返って来ません。)
$ kubectl delete ingress test
ingress.networking.k8s.io "test" deleted
ALB Controllerのログを確認すると削除保護により削除できなかった旨を確認できます。
$ kubectl logs -n kube-system aws-load-balancer-controller-7db9f97875-5z766
...
{"level":"error","ts":1739089983.5857038,"logger":"controller-runtime.manager.controller.ingress","msg":"Reconciler error","name":"test","namespace":"default","error":"deletion_protection is enabled, cannot delete the ingress: test"}
Ingressを確認するとまだ残っています。
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
test alb * internal-k8s-default-test-0beab48a0d-1864872286.ap-northeast-1.elb.amazonaws.com 80 20m
Ingressを消したい場合、annotationsのalb.ingress.kubernetes.io/load-balancer-attributes
を削除します。
$ kubectl annotate ingress test alb.ingress.kubernetes.io/load-balancer-attributes-
するとしばらくするとIngressが消えます。
$ kubectl get ingress
No resources found in default namespace.
このようにdeletion_protection.enabled=true
を設定することでIngressおよびALBの削除を防ぐことができます。しかし、プロンプトが返ってこないのは少々面倒です。
2. グループの利用
ALB ControllerのIngressにはannotationsでalb.ingress.kubernetes.io/group.name
を設定できます。同じグループ名を指定したIngressはALBを共有します。これを使いALBのデプロイ用とルール設定用の2つのIngressを用意します。ALB Controllerのドキュメントにある説明はこちらです。
設定例
以下2つのIngressを準備します。
デプロイ用
pathは何かしらの設定が必要だったのでダミーのパスを設定しています。common
というグループに属します。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: deploy
annotations:
alb.ingress.kubernetes.io/group.name: common
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/actions.response-503: >
{"type":"fixed-response","fixedResponseConfig":{"contentType":"text/plain","statusCode":"503","messageBody":"503 error text"}}
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /dummy
pathType: Exact
backend:
service:
name: dummy
port:
name: response-503
ルール用
バックエンドへのルート設定をします。こちらもcommon
というグループに属します。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test
annotations:
alb.ingress.kubernetes.io/group.name: common
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
rules:
- http:
paths:
- backend:
service:
name: test
port:
number: 80
path: /
pathType: Exact
動作確認
上記applyすると以下のようにデプロイされます。Ingressは2つありますがADDRESSで同じALBを使用していることがわかります。
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
deploy alb * internal-k8s-common-5ba7cb9a7a-1608177685.ap-northeast-1.elb.amazonaws.com 80 26s
test alb * internal-k8s-common-5ba7cb9a7a-1608177685.ap-northeast-1.elb.amazonaws.com 80 27s
ルール設定用のtest
を削除します。プロンプトはすぐに返ってきます。
$ kubectl delete ingress test
ingress.networking.k8s.io "test" deleted
確認するとdeploy
は残っています。
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
deploy alb * internal-k8s-common-5ba7cb9a7a-1608177685.ap-northeast-1.elb.amazonaws.com 80 6m2s
このようにcommon
グループに属するIngressがすべて消えるまでALBは残り続けます。deploy
のIngressにはさらに1. 削除保護の設定の設定をしておけばより安全になります。
3. 外部管理LB
ALB ControllerのIngressをデプロイすると内部的にはCRDのTargetGroupBindingリソースも作られます。TargetGroupBindingはALBにアタッチされているターゲットグループを管理するもので、Ingressのターゲットに指定したServiceの背後にいるPodのIPアドレスを自動で登録します。これによりPodが入れ替わっても引き続きALB経由でアクセスできます。
TargetGroupBindingリソースを手動で作成すると既存ALBのターゲットグループをALB Controllerで管理できます。ALB Controllerのドキュメントにある説明はこちらです。TargetGroupBindingリソース自体はALBをデプロイしていないためTargetGroupBindingリソースを消してもALBは削除されません。
しかし、Ingressを使わないためIngressで使用できるALB Controllerのannotationsは使えません。また、IngressだとPodのセキュリティグループも自動で更新されますがそれもされなくなります。ALB関連リソース(ALB、ターゲットグループ、リスナー、セキュリティグループ)はK8s外部で管理します。
設定例
以下のように事前にALBやリスナーを用意します。作成したTargetGroupのARNを確認します。
resource "aws_lb" "this" {
name = "alb-test"
internal = true
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = data.terraform_remote_state.eks.outputs.vpc.private_subnets
}
resource "aws_security_group" "alb" {
name = "alb-test"
description = "alb-test"
vpc_id = data.terraform_remote_state.eks.outputs.vpc.vpc_id
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_lb_target_group" "this" {
name = "alb-test"
port = 80
protocol = "HTTP"
target_type = "ip"
vpc_id = data.terraform_remote_state.eks.outputs.vpc.vpc_id
}
resource "aws_lb_listener" "this" {
load_balancer_arn = aws_lb.this.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.this.arn
}
}
K8sマニフェストは以下の通りです。targetGroupARN
に作成したターゲットグループのARNを指定します。
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: tgb-test
spec:
serviceRef:
name: tgb-test
port: 80
targetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:targetgroup/alb-test/c43cacc256114e34
動作確認
リソースデプロイ後、TargetGroupBinding、Ingress、Podを確認します。TargetGroupBindingはありますがIngressはありません。
$ kubectl get TargetGroupBinding,ingress,pod -o wide
NAME SERVICE-NAME SERVICE-PORT TARGET-TYPE ARN AGE
targetgroupbinding.elbv2.k8s.aws/tgb-test tgb-test 80 ip arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:targetgroup/alb-test/c43cacc256114e34 17m
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/tgb-test-59df87c7df-659lp 1/1 Running 0 17m 10.0.1.253 ip-10-0-1-86.ap-northeast-1.compute.internal <none> <none>
ALBとターゲットグループを確認すると以下の通りです。ターゲットはPodのIPアドレスになっています。ヘルスがunhealthy
になっていますがPod(Node)にアタッチされたセキュリティグループにALBのセキュリティグループからのインバウンドを許可していないからです。別途セキュリティグループの許可を追加するとhealthy
になります。
$ aws elbv2 describe-load-balancers --region=ap-northeast-1 | jq '.LoadBalancers[].LoadBalancerName'
"alb-test"
$ aws elbv2 describe-target-health --target-group-arn="arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:targetgroup/alb-test/c43cacc256114e34" --region=ap-northeast-1
{
"TargetHealthDescriptions": [
{
"Target": {
"Id": "10.0.1.253",
"Port": 80,
"AvailabilityZone": "ap-northeast-1a"
},
"HealthCheckPort": "80",
"TargetHealth": {
"State": "unhealthy",
"Reason": "Target.Timeout",
"Description": "Request timed out"
}
}
]
}
Podを削除してIPアドレスを変えてみます。
$ kubectl delete pod tgb-test-59df87c7df-659lp
pod "tgb-test-59df87c7df-659lp" deleted
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tgb-test-59df87c7df-4z8sc 1/1 Running 0 27s 10.0.1.138 ip-10-0-1-86.ap-northeast-1.compute.internal <none> <none>
ターゲットグループを確認するとターゲットのIPアドレスが更新されています。(ターゲットグループからPodのアドレスが消えるまで少し時間がかかります。)
$ aws elbv2 describe-target-health --target-group-arn="arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:targetgroup/alb-test/c43cacc256114e34" --region=ap-northeast-1
{
"TargetHealthDescriptions": [
{
"Target": {
"Id": "10.0.1.138",
"Port": 80,
"AvailabilityZone": "ap-northeast-1a"
},
"HealthCheckPort": "80",
"TargetHealth": {
"State": "healthy"
}
}
]
}
TargetGroupBindingを消します。
$ kubectl delete TargetGroupBinding tgb-test
targetgroupbinding.elbv2.k8s.aws "tgb-test" deleted
ALBとターゲットグループは引き続きのこります。(ターゲットグループからPodのアドレスが消えるまで少し時間がかかります。)
$ aws elbv2 describe-load-balancers --region=ap-northeast-1 | jq '.LoadBalancers[].LoadBalancerName'
"alb-test"
$ aws elbv2 describe-target-health --target-group-arn="arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:targetgroup/alb-test/c43cacc256114e34" --region=ap-northeast-1
{
"TargetHealthDescriptions": []
}
このようにTargetGroupBindingを使うと既存のALBやターゲットグループを ALB Controllerで管理でき、TargetGroupBindingを消しても既存のAWSリソースは消えません。