これまで AWS をお使いのみなさまは、一般的に Flagger やサービスメッシュ、CI/CD などのソリューションを使用して、Blue/Green デプロイメント、A/B テスト、トラフィック管理をしてきました。AWS Load Balancer Controller ( 旧称:ALB Ingress Controller ) がリリースされたことで、EKS ユーザーは AWS Application Load Balancer をネイティブサポートした Kubernetes Ingress リソースを介して Blue/Green デプロイメント、A/B テスト、カナリアデプロイをすることが可能になりました。
このブログでは、AWS Application Load Balancer の加重ターゲットグループの概念、高度なリクエストルーティング、そして Kubernetes Ingress リソースを介してそれらの設定を管理する方法を紹介します。
ソリューション概要
加重ターゲットグループ
AWS ユーザーが Blue/Green デプロイやカナリアデプロイ、A/B テスト戦略を採用できるように、AWS は 2019/11 に Application Load Balancer の加重ターゲットグループを発表しました。複数のターゲットグループが、あるリスナールールの同じ転送アクションに関連づけられ、各ターゲットグループの重みを指定できます。これにより、開発者がアプリケーションの異なるバージョンに対して通信をどのように分散するかコントロールできます。例えば、8 対 2 の重み付けがされた 2 つのターゲットグループを持つリスナールールを定義した場合、ロードバランサーは通信を 80 %対 20 %の割合で各ターゲットグループにルーティングします。
高度なリクエストルーティング
加えて、加重ターゲットグループに関して、AWS は 2019 年に高度なリクエストルーティング機能を発表しています。高度なリクエストルーティングでは、開発者が標準 及び カスタム HTTP ヘッダーやメソッド、リクエストパス、クエリ文字列、ソース IP アドレスに基づいてルール ( 及びトラフィックルーティング ) を作成することができます。この新機能により、ルーティングのためのプロキシフリートが不要になるためアプリケーションを簡素化し、ロードバランサーで不要なトラフィックをブロックし、A/B テストの実装が可能になります。
AWS Load Balancer Controller
AWS Load Balancer Controller は Kubernetes クラスターが Elastic Load Balancing を管理するのを助けるコントローラーです。Kubernetes Ingress リソースに従い、Application Load Balancer のプロビジョニングします。プロビジョニングされた Application Load Balancer の動作をカスタマイズするため、アノテーションを Kubernetes Ingress オブジェクトに追加します。これにより、開発者は Application Load Balancer を構成し、Kubernetes のネイティブセマンティックスを用い、Blue/Green デプロイメント、カナリアデプロイメント、A/B デプロイメントを実現します。例えば、下記の Ingress アノテーションは、2 つのアプリケーションバージョン間で通信を分散するための Application Load Balancer の設定です。
annotations:
...
alb.ingress.kubernetes.io/actions.blue-green: |
{
"type":"forward",
"forwardConfig":{
"targetGroups":[
{
"serviceName":"hello-kubernetes-v1",
"servicePort":"80",
"weight":50
},
{
"serviceName":"hello-kubernetes-v2",
"servicePort":"80",
"weight":50
}
]
}
}
ウォークスルー
前提条件
- AWS Application Load Balancer、Amazon EKS、Kubernetes に対する十分な理解
- Amazon EKS コマンドラインツールである eksctl
- Kubernetes コマンドラインツールである kubectl 及び helm
eksclt を用いたEKSクラスターの作成
下記のコマンドを利用して Amazon EKS クラスターを作成します。クラスター名 dev
を独自の名前に書き換えることは可能です。region-code
を Amazon EKS がサポートされる他のリージョンに書き換えることもできます。
より詳細は、こちらを参照ください。
$ eksctl create cluster --name dev --region ap-southeast-2
......
2021-12-31 16:15:04 [ℹ] using region ap-southeast-2
2021-12-31 16:15:05 [ℹ] setting availability zones to [ap-southeast-2a ap-southeast-2c ap-southeast-2b]
2021-12-31 16:15:05 [ℹ] subnets for ap-southeast-2a - public:192.168.0.0/19 private:192.168.96.0/19
2021-12-31 16:15:05 [ℹ] subnets for ap-southeast-2c - public:192.168.32.0/19 private:192.168.128.0/19
2021-12-31 16:15:05 [ℹ] subnets for ap-southeast-2b - public:192.168.64.0/19 private:192.168.160.0/19
......
2021-12-31 16:31:36 [ℹ] node "ip-192-168-23-165.ap-southeast-2.compute.internal" is ready
2021-12-31 16:31:36 [ℹ] node "ip-192-168-79-33.ap-southeast-2.compute.internal" is ready
2021-12-31 16:31:38 [ℹ] kubectl command should work with "/Users/<username>/.kube/config", try 'kubectl get nodes'
2021-12-31 16:31:38 [✔] EKS cluster "dev" in "ap-southeast-2" region is ready
AWS Load Balancer Controller のインストール
本ドキュメントに記載されているように、最新バージョンの AWS Load Balancer Controller をインストールします。
AWS Load Balancer Controller がデプロイされたことを確認してください。
$ kubectl get deployment -n kube-system aws-load-balancer-controller
NAME READY UP-TO-DATE AVAILABLE AGE
aws-load-balancer-controller 2/2 2 2 5m34s
サンプルアプリケーションバージョン 1 とバージョン 2 のデプロイ
使用するサンプルアプリケーションは、hello-kubernetes です。カスタムメッセージを含む 2 つのバージョンのアプリケーションをデプロイし、サービスタイプを ClusterIP
に設定します。
$ helm install --create-namespace --namespace hello-kubernetes v1 \
./hello-kubernetes/deploy/helm/hello-kubernetes \
--set message="You are reaching hello-kubernetes version 1" \
--set ingress.configured=true \
--set service.type="ClusterIP"
NAME: v1
LAST DEPLOYED: Sat Jan 1 15:12:57 2022
NAMESPACE: hello-kubernetes
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ helm install --create-namespace --namespace hello-kubernetes v2 \
./hello-kubernetes/deploy/helm/hello-kubernetes \
--set message="You are reaching hello-kubernetes version 2" \
--set ingress.configured=true \
--set service.type="ClusterIP"
NAME: v2
LAST DEPLOYED: Sat Jan 1 15:13:26 2022
NAMESPACE: hello-kubernetes
STATUS: deployed
REVISION: 1
TEST SUITE: None
Ingress のデプロイと Blue/Green デプロイメントのテスト
Ingress のアノテーション(alb.ingress.kubernetes.io/actions.${action-name}
) は、Application Load Balancer のリスナーに対するカスタムアクションを構成する方法を提供します。例えば、リダイレクトアクションやフォワードアクションなどです。フォワードアクションの場合、異なる重みを持つ複数のターゲットグループがアノテーションで定義されます。AWS Load Balancer Controller は、ターゲットグループをプロビジョニングし、通信を転送するためにアノテーションに従ってリスナールールを設定します。例えば、下記の Ingress リソースは 全通信を hello-kubernetes-v1
サービス ( 重み: 100 対 0 ) にフォワードする Application Load Balancer を構成します。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "hello-kubernetes"
namespace: "hello-kubernetes"
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/actions.blue-green: |
{
"type":"forward",
"forwardConfig":{
"targetGroups":[
{
"serviceName":"hello-kubernetes-v1",
"servicePort":"80",
"weight":100
},
{
"serviceName":"hello-kubernetes-v2",
"servicePort":"80",
"weight":0
}
]
}
}
labels:
app: hello-kubernetes
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: blue-green
port:
name: use-annotation
アノテーションの action-name
は Ingress ルール内の serviceName
に一致する必要があり、servicePort
は前のコードスニペットと同様に use-annotation
と記載する必要がある点に注意してください。
下記の Ingress リソースをデプロイし、AWS Load Balancer Controller によって Application Load Balancer が作成と設定がされるまで 2 分程度待ちます。
ロードバランサーエンドポイントからのレスポンスが、常にアプリケーションバージョン 1 からであることを確認してください。
$ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/hello-kubernetes configured
$ while true; do curl -s k8s-hellokub-hellokub-1c21b68bea-597504338.ap-southeast-2.elb.amazonaws.com | grep version; sleep 1; done
You are reaching hello-kubernetes version 1
You are reaching hello-kubernetes version 1
You are reaching hello-kubernetes version 1
Blue/Green デプロイメント
Blue/Green デプロイメントをするために、すべての通信がバージョン 2 に流れるように Ingress のアノテーションをアップデートします。
下記の Ingress リソースをデプロイします。
alb.ingress.kubernetes.io/actions.blue-green: |
{
"type":"forward",
"forwardConfig":{
"targetGroups":[
{
"serviceName":"hello-kubernetes-v1",
"servicePort":"80",
"weight":0
},
{
"serviceName":"hello-kubernetes-v2",
"servicePort":"80",
"weight":100
}
]
}
}
ロードバランサーエンドポイントからのレスポンスが、常にアプリケーションバージョン 2 からであることを確認してください。
$ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/hello-kubernetes (http://ingress.networking.k8s.io/hello-kubernetes) configured
$ while true; do curl -s k8s-hellokub-hellokub-1c21b68bea-597504338.ap-southeast-2.elb.amazonaws.com (http://k8s-hellokub-hellokub-1c21b68bea-597504338.ap-southeast-2.elb.amazonaws.com/) | grep version; sleep 1; done
You are reaching hello-kubernetes version 2
You are reaching hello-kubernetes version 2
You are reaching hello-kubernetes version 2
Ingress のデプロイとカナリアデプロイメントのテスト
すべての通信をバージョン 2 へ向ける代わりに、バージョン 2 の重みを段階的に増やすことで、通信を緩やかにバージョン 2 へシフトできます。これにより、より多くの通信をバージョン 2 に流す前に、プロダクションの通信の一部でバージョン 2 を検証できます。下記の例では、通信の 10 %がバージョン 2 へ、残りの 90 %がバージョン1へ流れています。
下記の Ingress リソースをデプロイします。
alb.ingress.kubernetes.io/actions.blue-green: |
{
"type":"forward",
"forwardConfig":{
"targetGroups":[
{
"serviceName":"hello-kubernetes-v1",
"servicePort":"80",
"weight":90
},
{
"serviceName":"hello-kubernetes-v2",
"servicePort":"80",
"weight":10
}
]
}
}
ロードバランサーからのレスポンスを確認してください。
$ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/hello-kubernetes configured
$ while true; do curl -s k8s-hellokub-hellokub-1c21b68bea-597504338.ap-southeast-2.elb.amazonaws.com | grep version; sleep 1; done
You are reaching hello-kubernetes version 1
You are reaching hello-kubernetes version 2
You are reaching hello-kubernetes version 1
You are reaching hello-kubernetes version 1
You are reaching hello-kubernetes version 1
Argo Rolluots
プロダクション環境でカナリアデプロイメントをする時、通常、少しずつ通信を切り替えます。その際、ある程度の自動化が施されます。このプロセスには、さまざまなパフォーマンス監視システムも組み込まれ、各ステップでエラーが発生してないか、またエラーが許容閾値以下であるか確認します。これを実現するためには、Argo Rollouts などのプログレッシブデリバリーの仕組みが非常に有益です。
Argo Rollouts は、AWS Load Balancer Controller のアノテーションベースのトラフィックシェーピング機能を利用して、更新中に徐々に通信を新しいバージョンのアプリケーションにシフトするためのファーストクラスのサポートを提供します。加えて、Argo Rollouts は、主要な KPI を検証するためにさまざまなプロバイダーからのメトリックを照会して解釈し、更新中の自動プロモーションまたはロールバックを促進できます。より詳細な情報は、こちらをご参照ください。
Ingress のデプロイとA/Bテスト
Ingress の spec で設定するホストやパスのルーティング条件に加え、Ingress アノテーション alb.ingress.kubernetes.io/conditions.${conditions-name}
を用い、ルーティング条件を設定できます。また、http-header
, http-request-method
, query-string
及び source-ip
を基準としたルーティングを設定することもできます。これによって開発者は、サービスメッシュ等の個別のルーティング機構をセットアップ 及び 管理する必要なく、A/B テストを実装するためにこれらのルーティングオプションを利用することができます。
AWS Load Balancer Controller は、特定のバックエンドへの通信の一部を転送するために、アノテーションに従ってリスナールールを設定します。下記の例では、全てのリクエストがデフォルトでアプリケーションバージョン 1 へ転送されます。リクエストのカスタム HTTP ヘッダーに HeaderName=HeaderValue1
が含まれる場合、下記の Ingress リソースは、アプリケーションバージョン 2 へ通信を転送します。
下記の Ingress リソースをデプロイしてください。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "hello-kubernetes"
namespace: "hello-kubernetes"
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/conditions.ab-testing: >
[{"field":"http-header","httpHeaderConfig":{"httpHeaderName": "HeaderName", "values":["HeaderValue1"]}}]
alb.ingress.kubernetes.io/actions.ab-testing: >
{"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"hello-kubernetes-v2","servicePort":80}]}}
labels:
app: hello-kubernetes
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ab-testing
port:
name: use-annotation
- path: /
pathType: Prefix
backend:
service:
name: hello-kubernetes-v1
port:
name: http
ロードバランサーエンドポイントからのレスポンスを確認してください。
$ kubectl apply -f ingress-ab.yaml
ingress.networking.k8s.io/hello-kubernetes configured
$ while true; do curl -s k8s-hellokub-hellokub-1c21b68bea-597504338.ap-southeast-2.elb.amazonaws.com | grep version; sleep 1; done
You are reaching hello-kubernetes version 1
You are reaching hello-kubernetes version 1
You are reaching hello-kubernetes version 1
$ while true; do curl -s -H "HeaderName: HeaderValue1" k8s-hellokub-hellokub-1c21b68bea-597504338.ap-southeast-2.elb.amazonaws.com | grep version; sleep 1; done
You are reaching hello-kubernetes version 2
You are reaching hello-kubernetes version 2
You are reaching hello-kubernetes version 2
クリーンアップ
VPC 削除を妨げる VPC 内の孤立したリソースが発生しない様、Ingress リソースを削除する必要があります。これにより最初に Application Load Balancer が削除されます。
$ kubectl delete ing hello-kubernetes -n hello-kubernetes
ingress.extensions "hello-kubernetes" deleted
$ eksctl delete cluster --name dev
2022-01-03 19:33:48 [ℹ] eksctl version 0.79.0
2022-01-03 19:33:48 [ℹ] using region ap-southeast-2
2022-01-03 19:33:48 [ℹ] deleting EKS cluster "dev"
......
2022-01-03 19:37:59 [ℹ] will delete stack "eksctl-dev-cluster"
2022-01-03 19:37:59 [✔] all cluster resources were deleted
まとめ
Blue/Green デプロイメント、カナリアデプロイメント、A/B テストを採用するためのさまざまな方法があります。このブログでは、AWS Load Balancer Controller を使用して Kubernetes Ingress リソースを管理することにより、AWS Application Load Balancer で同様のデプロイメント戦略の実現する方法にを示しました。
翻訳はソリューションアーキテクト祖父江が担当しました。原文はこちらです。