Amazon Web Services ブログ
Kubernetes アプリケーションの公開 Part 2: AWS Load Balancer Controller
この記事は Exposing Kubernetes Applications, Part 2: AWS Load Balancer Controller (記事公開日: 2022 年 11 月 22 日) を翻訳したものです。
はじめに
連載「Kubernetes アプリケーションの公開」では、Kubernetes クラスターで実行されているアプリケーションを、外部からのアクセスのために公開する方法に焦点を当てます。
Part 1 では、Kubernetes クラスターでインバウンドトラフィックの制御を定義する 2 つの方法である Service と Ingress リソースタイプについて探りました。Service と Ingress コントローラーによるこれらのリソースタイプの処理について説明し、その後、いくつかのコントローラーの実装バリエーションの利点と欠点について概要を説明しました。
今回、Part 2 では、Service と Ingress 両方のコントローラーの AWS のオープンソース実装である AWS Load Balancer Controller の概要を説明します。このコントローラーのセットアップ、設定、想定されるユースケース、制限事項について説明します。
Part 3 では、Ingress コントローラーのまた別のオープンソース実装である NGINX Ingress Controller について、AWS Load Balancer Controller とのいくつかの違いも含めて、同様のウォークスルーを行います。
AWS Load Balancer Controller の概要
連載の Part 1 では、Kubernetes アプリケーションを公開するための 2 つのアプローチに焦点を当てました。トラフィックをアプリケーションの Pod に直接ルーティングする「外部ロードバランサー」と、アプリケーションへの単一エントリーポイントとして機能し Pod にトラフィックをルーティングする「クラスター内リバースプロキシ」です。
AWS Load Balancer Controller は、以下の図で模式的に示すような「外部ロードバランサー」のアプローチをとります。
なお、AWS Load Balancer Controller には、Service コントローラーも含まれています。その使用例については、連載の Part 3 で紹介します。
以下の図では、Ingress のバックエンドにあるアプリケーションを公開するためのステップバイステップのプロセスを示しています。
alb-ingress-controller
が Ingress イベントを監視します。(訳注: これは以前のバージョンでの名前で、現在のバージョンではaws-load-balancer-controller
です。)- Ingress オブジェクトごとに ALB が管理されます。 必要に応じて作成、設定、および削除されます。
- ターゲットグループが、
instance
(ServiceA および ServiceB) またはip
(ServiceC) モードで作成されます。 - ALB リスナーが作成され、設定されます。
- ルールは、リスナーからターゲットグループにトラフィックを転送するように設定されます。
Part 1 では、Elastic Load Balancing のようなマネージドで可用性が高くスケーラブルなロードバランサーサービスに作業をアウトソーシングするメリットについて説明しました。この記事 (Part 2) では、セットアップ、設定、コード例についてウォークスルーを行い、AWS Load Balancer Controller の使い方を説明します。
ウォークスルー
前提条件
1. AWS アカウントへのアクセスの取得
AWS アカウントと、AWS コマンドラインインターフェイス (AWS CLI) や同様のツールを使用して、ターミナルから AWS と通信できる必要があります。
以下のコード例では、AWS アカウント ID やリージョンなど、置き換えることを前提とした文字列がいくつか含まれています。これらは、あなたの環境に合った値で置き換えてください。
2. AWS Load Balancer Controller 用の AWS IAM ポリシーの作成
この手順のステップ 2 と 3 のみを実行して、AWSLoadBalancerControllerIAMPolicy
を作成します。IRSA (IAM Roles for Service Accounts) を使用して、コントローラーに IAM アクセス許可を提供します。
なお、OIDC IAM プロバイダーの登録と AWS Load Balancer Controller の Service Account の作成、および IAM ロールの作成は、後続の設定で eksctl
が自動的に行うため、明示的に行う必要はありません。
3. クラスターの作成
eksctl を使用して Amazon EKS クラスターをプロビジョニングします。eksctl はクラスター自体の作成に加えて、VPC、サブネット、セキュリティグループといった必要なネットワークリソースもプロビジョニングおよび設定します (eksctl のインストール手順については、こちらを参照してください) 。
以下の eksctl
設定ファイルは、Amazon EKS クラスターとその設定を定義します。
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: aws-load-balancer-controller-walkthrough
region: ${AWS_REGION}
version: '1.27'
iam:
withOIDC: true
serviceAccounts:
- metadata:
name: aws-load-balancer-controller
namespace: kube-system
attachPolicyARNs:
- arn:aws:iam::${AWS_ACCOUNT}:policy/AWSLoadBalancerControllerIAMPolicy
managedNodeGroups:
- name: main-ng
instanceType: m5.large
desiredCapacity: 1
privateNetworking: true
上記のコードを config.yml
ファイルに記述してください。
AWS_REGION
および AWS_ACCOUNT
環境変数を定義してください。その後、クラスターを作成します。
envsubst < config.yml | eksctl create cluster -f -
なお、このウォークスルーでは、Kubernetes バージョン 1.23 の Amazon EKS プラットフォームバージョン eks.3
を使用しています。(訳注: 翻訳時には、Kubernetes バージョン 1.27 の Amazon EKS プラットフォームバージョン eks.4
を使用して動作を確認しています。)
シンプルにするため、上記の構成では、セキュリティやモニタリングなど、Kubernetes クラスターのプロビジョニングと管理の多くの側面は考慮していません。より詳細な情報とベストプラクティスについては、Amazon EKS と eksctl のドキュメントを参照してください。
クラスターが稼働していることを確認します。
kubectl get nodes
kubectl get pods -A
上記のコマンドは、1 つの Amazon EKS ノードと 4 つの実行中の Pod を返すはずです。
4. Helm のインストール
コントローラーのインストールと設定には、Kubernetes において一般的なパッケージマネージャーである Helm を使用します。こちらの手順に従って Helm をインストールしてください。
AWS Load Balancer Controller のインストール
1. CustomResourceDefinition (CRD) のインストール
以下のコマンドで、コントローラーが機能するために必要なカスタムリソース定義 (CRD) をインストールします。
kubectl apply -k \
"github.com/aws/eks-charts/stable/aws-load-balancer-controller//crds?ref=master"
2. Helm を使用したコントローラーのインストール
ここでは eksctl によって自動的に作成された Service Account を使用していることに注意してください。
helm repo add eks https://aws.github.io/eks-charts
helm upgrade -i aws-load-balancer-controller eks/aws-load-balancer-controller \
--namespace kube-system \
--set clusterName=aws-load-balancer-controller-walkthrough \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller
kubectl -n kube-system rollout status deployment aws-load-balancer-controller
kubectl get deployment -n kube-system aws-load-balancer-controller
テスト用 Service のデプロイ
1. Namespace の作成
kubectl create namespace apps
2. Service のマニフェストファイルの作成
以下のコードを service.yml
ファイルに記述します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${SERVICE_NAME}
namespace: ${NS}
labels:
app.kubernetes.io/name: ${SERVICE_NAME}
spec:
selector:
matchLabels:
app.kubernetes.io/name: ${SERVICE_NAME}
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: ${SERVICE_NAME}
spec:
terminationGracePeriodSeconds: 0
containers:
- name: ${SERVICE_NAME}
image: hashicorp/http-echo
imagePullPolicy: IfNotPresent
args:
- -listen=:3000
- -text=${SERVICE_NAME}
ports:
- name: app-port
containerPort: 3000
resources:
requests:
cpu: 0.125
memory: 50Mi
---
apiVersion: v1
kind: Service
metadata:
name: ${SERVICE_NAME}
namespace: ${NS}
labels:
app.kubernetes.io/name: ${SERVICE_NAME}
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: ${SERVICE_NAME}
ports:
- name: svc-port
port: 80
targetPort: app-port
protocol: TCP
http-echo イメージを使用する上記の Service は、${SERVICE_NAME}
変数で定義した Service 名でリクエストに応答します。シンプルにするため、レプリカは 1 つとしています。
3. サービスのデプロイと検証
以下のコマンドを実行します (これからこの記事を通して、これらの Service を使用します) 。
SERVICE_NAME=first NS=apps envsubst < service.yml | kubectl apply -f -
SERVICE_NAME=second NS=apps envsubst < service.yml | kubectl apply -f -
SERVICE_NAME=third NS=apps envsubst < service.yml | kubectl apply -f -
SERVICE_NAME=fourth NS=apps envsubst < service.yml | kubectl apply -f -
SERVICE_NAME=error NS=apps envsubst < service.yml | kubectl apply -f -
すべてのリソースがデプロイされていることを確認しましょう。
kubectl get pod,svc -n apps
以下のような出力が得られるはずです。
シンプルな Ingress のデプロイ
1. Ingress のマニフェストファイルの作成
以下のコードを ingress.yml
ファイルにコピーしてください。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${NS}-ingress
namespace: ${NS}
annotations:
alb.ingress.kubernetes.io/load-balancer-name: ${NS}-ingress
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/healthcheck-path: /healthz
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /first
pathType: Prefix
backend:
service:
name: first
port:
name: svc-port
- path: /second
pathType: Prefix
backend:
service:
name: second
port:
name: svc-port
Ingress の概要については、連載の Part 1 をご参照いただくか、Kubernetes の公式ドキュメントで概要や完全な仕様を調べることができます。
上記のコードは、AWS Load Balancer Controller に追加の情報を提供するためにいくつかのことを行っています。
ingressClassName
をalb
に設定することで、AWS Load Balancer Controller がこの Ingress リ ソースを処理することを指示します- アノテーションを介して以下を定義します
- コントローラーが作成するロードバランサーの名前
- ロードバランサーのターゲットタイプが
ip
であること (すなわち、Pod の IP アドレスをターゲットとして登録します) - ロードバランサーが
internet-facing
であること - ヘルスチェックのパス
Kubernetes 1.19 より前では、kubernetes.io/ingress.class: alb
という形式の Ingress アノテーションを介して IngressClass を定義することもできましたが、これは非推奨となりました。このアノテーションはまだ使用可能ですが、ingressClassName
が優先されます。
利用可能なアノテーションの完全なリストについては、こちらを参照してください。
2. Ingress リソースのデプロイ
次のコマンドを実行します。
NS=apps envsubst < ingress.yml | kubectl apply -f -
しばらくすると、デプロイされた Ingress リソースの状態を確認できます。
kubectl get ingress -n apps
次のような出力が得られます (ID とリージョンはプレースホルダに置き換えてあります) 。
Application Load Balancer の URL を環境変数に格納します。
export ALB_URL=$(kubectl get -n apps ingress/apps-ingress -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
数分後、ロードバランサーのプロビジョニングが完了し、リクエストを送ることができるようになります。
curl ${ALB_URL}/first
curl ${ALB_URL}/second
以下のような結果が得られれば、Service と Ingress は正常に機能しています。
AWS Load Balancer Controller は、Ingress リソースでの定義に従って、apps-ingress
という名前の Application Load Balancer をプロビジョニングし、リスナールールを設定し、ターゲットグループとそのターゲットを定義します。
AWS Application Load Balancer Controller のベストプラクティスでは、アベイラビリティゾーンをまたいで、少なくとも 2 つのパブリックサブネットと 2 つのプライベートサブネットを構成することが必要です。Amazon EKS クラスターのノードはプライベートサブネットに存在し、パブリックサブネットは、ロードバランサーがプライベートサブネットの IP にトラフィックをルーティングするための内部リソースをプロビジョニングするために使用されます。
コントローラーは、タグに基づいてこれらのサブネットを自動検出できます。
- パブリックサブネットには、
kubernetes.io/role/elb
キーに1
または''
を値として指定します - プライベートサブネットには、
kubernetes.io/role/internal-elb
キーに1
または''
を値として指定します
これらのタグの作成は、クラスターのプロビジョニングに使用した eksctl
ツールが自動的に行ってくれるため、明示的にタグを作成する必要はありません。また、alb.ingress.kubernetes.io/subnets
アノテーションを使用して、関連するサブネットを明示的に設定することもできます。
ターゲットグループのいずれかを開くと、登録された Pod の IP アドレスとポート、そして Ingress 定義の alb.ingress.kubernetes.io/healthcheck-path
アノテーションで定義したターゲットのヘルスチェック設定が、プロビジョニングされたターゲットグループに反映されていることが確認できます。また、コントローラーはクラスターセキュリティグループを更新し、ロードバランサーから Pod へのトラフィックを許可しています。
リスナー設定では、特定のパスのいずれにもルーティングされないリクエストに対する、デフォルトの「キャッチオール」ルールがあることに気づいたかもしれません。このルールは固定の 404
レスポンスを返します。
追加のアノテーションを介してこれをさらに設定するか、Ingress 設定を介してデフォルトのバックエンド (Default Backend) を明示的に定義できます。
Default Backend
1. Ingress 定義の更新
ingress.yml
ファイルに defaultBackend
の設定を追加します。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${NS}-ingress
namespace: ${NS}
annotations:
alb.ingress.kubernetes.io/load-balancer-name: ${NS}-ingress
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/healthcheck-path: /healthz
spec:
ingressClassName: alb
defaultBackend:
service:
name: error
port:
name: svc-port
rules:
- http:
paths:
- path: /first
pathType: Prefix
backend:
service:
name: first
port:
name: svc-port
- path: /second
pathType: Prefix
backend:
service:
name: second
port:
name: svc-port
2. Ingress の更新
NS=apps envsubst < ingress.yml | kubectl apply -f -
わずかな時間の後、Application Load Balancer のリスナールールの設定が適切に更新されます。
以下のコマンドを実行することで、リスナールールの設定を検証しましょう。
curl ${ALB_URL}
curl ${ALB_URL}/first
curl ${ALB_URL}/first/something
curl ${ALB_URL}/second
curl ${ALB_URL}/third
curl ${ALB_URL}/something
次の出力が得られます。
ホストベースのルーティング
上記の Ingress の定義では、どのルールにも host
設定を指定していませんでした。つまり、Host
ヘッダーにかかわらず、Application Load Balancer に送信されるすべてのリクエストが、これらのルールにマッチすることになります。
Ingress 定義では、パスベースルーティングとホストベースルーティングを組み合わせることができます。
1. Ingress 定義の更新
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${NS}-ingress
namespace: ${NS}
annotations:
alb.ingress.kubernetes.io/load-balancer-name: ${NS}-ingress
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/healthcheck-path: /healthz
spec:
ingressClassName: alb
defaultBackend:
service:
name: error
port:
name: svc-port
rules:
- host: a.example.com
http:
paths:
- path: /first
pathType: Prefix
backend:
service:
name: first
port:
name: svc-port
- path: /second
pathType: Prefix
backend:
service:
name: second
port:
name: svc-port
- host: '*.example.com'
http:
paths:
- path: /third
pathType: Prefix
backend:
service:
name: third
port:
name: svc-port
- path: /fourth
pathType: Prefix
backend:
service:
name: fourth
port:
name: svc-port
ワイルドカードを使用して、a.example.com
以外の *.example.com
へのすべてのリクエストをルーティングします (ワイルドカードに関するその他の考慮事項はこちらを参照してください) 。
2. Ingress の更新
NS=apps envsubst < ingress.yml | kubectl apply -f -
ロードバランサーの設定が期待どおりに更新されます。
curl
を使用して構成をテストしましょう (2 番目のリクエストのドメインが正しくないことに注意してください) 。
curl ${ALB_URL}/first -H 'Host: a.example.com'
curl ${ALB_URL}/first -H 'Host: a.example.net'
curl ${ALB_URL}/third -H 'Host: b.example.com'
curl ${ALB_URL}/fourth -H 'Host: c.example.com'
以下のような結果が期待されます。
もちろん、本番環境では、上記のサブドメインの DNS レコードを ALB の DNS 名 にポイントします。
これ以降のコード例は説明のみを目的としており、ウォークスルーの一部ではありません。必要に応じてデプロイし、その影響を調査することができます。
複数の Ingress リソース
これまで、ALB を使用して、同じ Namespace にあるバックエンドの Service にトラフィックをルーティングする単一の Ingress リソースを扱ってきました。多くの Namespace にまたがって複数の Ingress リソースが必要な場合はどうすればよいでしょうか?それぞれに ALB をプロビジョニングする必要があるのでしょうか?
答えは「No」です。
複数の Ingress リソースが同じロードバランサーの下で処理されるように設定することができます。これは、すべての Ingress リソースに alb.ingress.kubernetes.io/group.name アノテーションで同じ値を設定することで実現できます。alb.ingress.kubernetes.io/group.order アノテーションを追加することで、マージ順序を制御することもできます。
このような集約のユースケースとして考えられるのは、中央でプロビジョニングされた同じロードバランサーを引き続き使用しながら、チーム間の依存関係を減らし、それぞれが一部のサービスに責任を持つようにすることです。
例えば、次のような Ingress リソースについて考えてみましょう (わかりやすくするために簡略化しています) 。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: apps-ingress
namespace: apps
annotations:
alb.ingress.kubernetes.io/load-balancer-name: apps-ingress
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/healthcheck-path: /healthz
alb.ingress.kubernetes.io/group.name: common-ingress-group
spec:
ingressClassName: alb
defaultBackend:
service:
name: error
port:
name: svc-port
rules:
- host: a.example.com
...
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ops-ingress
namespace: ops
annotations:
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/group.name: common-ingress-group
alb.ingress.kubernetes.io/group.order: '10'
spec:
ingressClassName: alb
rules:
- host: a.example.com
...
- host: b.example.com
...
これらの Ingress 定義は同じロードバランサーの下にマージされます。
2 番目の Ingress リソースに対して定義した order
は 10
で、1 番目の Ingress リソースのデフォルトの order
である 0
より大きく (すなわち優先度が低く) なっています。したがって、2 番目の Ingress からの a.example.com へのルートは、ルールリストの下位にあります (/first
がルール #1 では first.apps
Service にポイントされ、ルール #3 では third.ops
Service にポイントされているのを確認してください) 。
両方の Ingress リソースで target-type
を定義する必要があること、および、Default Backend を定義できるのは 1 回だけであることに注意してください。
IngressClass
上記のすべての Ingress リソース定義における ingressClassName
プロパティは、クラスターの IngressClass リソースの指定です。alb
はコントローラーの ingressClass
設定値のデフォルト値であり、コントローラーとともに alb
IngressClass がインストールされています。
これは次のような定義です。
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: alb
...
spec:
controller: ingress.k8s.aws/alb
同じように独自の IngressClass を定義することもできます。
デフォルトの IngressClass
Ingress リソース内で IngressClass を指定することができますが、デフォルトの IngressClass を定義して、指定を不要にすることもできます。これは、IngressClass の定義に ingressclass.kubernetes.io/is-default-class
アノテーションを追加することで可能です。
IngressClass を指定してないすべての Ingress リソースは、上記の controller フィールドで定義されたコントローラーによって処理されます。
IngressClassParams
コントローラーと一緒にインストールした CRD (Custom Resource Definition) の中に、IngressClassParams があります。これは、コントローラーに追加のパラメータを渡すためのもので、これらのパラメータの一部は、以前はアノテーションを介して処理されていました。 実際、group
と scheme
のアノテーションもこの中に移動できます。加えて、Namespace に基づいて、どの Ingress リソースに特定の IngressClassParams の使用を許可するかを制御する namespaceSelector
を指定できます。
apiVersion: elbv2.k8s.aws/v1beta1
kind: IngressClassParams
metadata:
name: alb-ingress-class-params
...
spec:
namespaceSelector:
matchLabels:
team: some-team
group:
name: common-ingress-group
scheme: internet-facing
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: alb
....
spec:
controller: ingress.k8s.aws/alb
parameters:
apiGroup: elbv2.k8s.aws
kind: IngressClassParams
name: alb-ingress-class-params
コントローラーのスコープの制限
ウォークスルーの冒頭で AWS Load Balancer Controller をデプロイした際、処理対象の Ingress と Service のリソースを明示的に制限しませんでした。
もうひとつの方法として、特定の Namespace の Ingress と Service リソースを監視するようにコントローラーのスコープを指定することができます。
helm upgrade -i aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=aws-load-balancer-controller-walkthrough \
--set serviceAccount.create=false \
--set watchNamespace=apps \
--set serviceAccount.name=aws-load-balancer-controller
AWS Load Balancer Controller は、watchNamespace
設定値を指定して特定の Namespace を監視するか、省略することですべての Namespace を監視することができます。現在のところ、複数の特定の Namespace を監視するオプションはありません。
複数の AWS Load Balancer Controller インスタンス
上記のようにコントローラーを特定の Namespace に制限できる機能を考慮すると、AWS Load Balancer Controller の複数のインスタンスを、それぞれ特定の Namespace に制限し、異なる構成でデプロイすることが合理的である可能性があります。現在、AWS Load Balancer Controller は複数のインスタンスをサポートしていませんが、この Issue を追跡することで進捗を確認できます。
クリーンアップ
ウォークスルーで作成したリソースを削除するには、以下のコマンドを実行します。
NS=apps envsubst < ingress.yml | kubectl delete -f -
helm uninstall -n kube-system aws-load-balancer-controller
envsubst < config.yml | eksctl delete cluster -f -
aws iam delete-policy --policy-arn arn:aws:iam::${AWS_ACCOUNT}:policy/AWSLoadBalancerControllerIAMPolicy
まとめ
AWS Load Balancer Controller は、受信トラフィック処理を可用性と拡張性の高いマネージドサービスである AWS Application Load Balancer にオフロードすることで、運用の複雑さを軽減します。Ingress リソースをロードバランサーのプロビジョニングと設定に変換することで、インフラストラクチャのプロビジョニングプロセスとは独立して、受信トラフィック処理のあらゆる側面を制御する機能をアプリケーションに提供します。
AWS Load Balancer Controller は Ingress コントローラーだけでなく、Service コントローラーも提供するため、Kubernetesアプリケーションを外部トラフィックに公開するための完全なソリューションです。
翻訳はプロフェッショナルサービスの杉田が担当しました。原文はこちらです。