Amazon Web Services ブログ

Amazon EKS 上の複数のアプリケーションを 1 つの Application Load Balancer で外部に公開する方法

この記事は How To Expose Multiple Applications on Amazon EKS Using a Single Application Load Balancer (記事公開日: 2022 年 2 月 14 日) を翻訳したものです。

導入

マイクロサービスアーキテクチャは、クラウドネイティブアプリケーションの選択肢の 1 つとなってきました。よりきめ細かいスケーラビリティ、機能の分離、そして特定のアプリケーションの機能を担当する独立したチームを置くことができるといった理由から、このアーキテクチャパターンは広く採用されつつあります。新しいアプリケーションがクラウドで生まれる事実と、モジュラーアーキテクチャとスケーラブルアーキテクチャ、そしてクラウドが提供する技術が完璧に一致して、マイクロサービスの展開、運用、管理を容易にします。

1 つのパターン、複数の実装オプション

AWS では、マイクロサービスアーキテクチャを実装するために従うべき原則が、少なくとも 2 つあります。

1) アプリケーションまたはサービスの異なる複数のコンポーネントを、1 つまたは複数のコンテナに移動し (コンテナ化としても知られる手法です)、Amazon Elastic Kubernetes Service (Amazon EKS)Amazon Elastic Container Service (Amazon ECS) のようなコンテナオーケストレーターに管理を委譲する方法

2) アプリケーションまたはサービスの異なる複数のコンポーネントを、サーバーレス関数に移動し、AWS Lambda に管理を委譲する方法

次のステップは、これらのマイクロサービスを外部に公開します。マイクロサービスはコンテナかサーバーレス関数かに関係なく、クライアントアプリケーションや API がリクエストを送信しレスポンスを受け取ることができるエンドポイントを提供します。例えば下記の例のように、全てのマイクロサービスに自身のエンドポイントを持たせ、Web URL の最後の「/」以降で各マイクロサービスを指せるようにします。

www.example.com/service1 > microservice1
www.example.com/service2 > microservice2
www.example.com/service3 > microservice3

このタイプのロードバランシングまたはルーティングは、パスベースルーティング (URL パスに基づくルーティング) として知られています。

このアプローチの最も重要な利点の 1 つは、多くのマイクロサービスアプリケーションを、低コストかつ簡単に外部に公開できる点です。Application Load Balancer (ALB)Amazon API Gateway のどちらも、この機能をサポートしています。そのため、自身のマイクロサービスを、Amazon EKS または Amazon ECS で動作するコンテナ、または AWS Lambda で動作するサーバーレス関数として、1 つの ALB または 1 つの API Gateway を使用して外部に公開することができます。

この記事では、コンテナとして動作し Amazon EKS によってオーケストレーションされているマイクロサービスアプリケーションを、Application Load Balancer を通じて外部に公開する方法を紹介します。

アーキテクチャとコンポーネント

Amazon EKS クラスターは、アプリケーションが動作する Kubernetes のクラスターです。Amazon EKS は、API サーバー群と etcd ノード群とで構成される Kubernetes のコントロールプレーンを管理する複雑さを取り除いた、AWS のサービスです。Amazon EKS により開発者は、アプリケーションが動作する実際のサーバー群 (データノード) であるデータプレーンに集中することができます。

Auto Scaling グループは、モニタリングしているメトリクスをトリガーに EC2 インスタンスをスケールイン・スケールアウトできる AWS の機能です。クラスターをスケールするために、ユーザーの代わりに Auto Scaling グループを使用する、 Kubernetes の Cluster Autoscaler が必要です。このデモでは、この機能は HA 構成で動作する必要最小限の EC2 インスタンスを維持するためだけに使用します。AWS Fargate は、EC2 インスタンスの代わりに Pod をスケールさせられる、もう 1 つのオプションです。コンテナを Fargate 上の Pod として動作させる方法でも、データプレーンを AWS で完全に管理できます。

Application Load Balancer (ALB) は、1 つまたは複数のアベイラビリティゾーンにある EC2 インスタンス、コンテナ、IP アドレスといった複数のターゲット間で、トラフィックを自動的に分散させます。ALB は、HTTP/HTTPS といった OSI 参照モデルのレイヤー 7 (アプリケーション層) で動作します。また ALB は、(パスベースルーティングとしても知られる) URL パスに基づいてリクエストをルーティングできるルールを定義することができるパスの条件のような、他のたくさんの機能をサポートします。ALB は、Elastic Load Balancing でサポートされている、3 つのロードバランスのオプションの 1 つです。

Ingress は、一般的に HTTP によるクラスター外からクラスター内へのトラフィックを管理する、Kubernetes のリソースです。Ingress は通常、ロードバランシング、SSL、バーチャルホスト機能を提供します。この Ingress の機能をクラスター内に持つためには、Ingress コントローラーをインストールする必要があります。

AWS Load Balancer Controller は、Kubernetes クラスター向けの Elastic Load Balancing の管理を支援するコントローラーです。このデモシナリオでは、上述の Ingress を Kubernetes のマニフェストで定義して、ALB の自動的なプロビジョニング、およびこの ALB に必要なルーティングルールの設定を行います。

このデモの最終的なゴールは、下図の yellow・green のように、1 つの ALB で、異なるマイクロサービスアプリケーションへの異なるパスを通じてのリクエストに応答できるようにすることです。

要件

このソリューションを実装するためには、以下の前提条件が必要です。

  • マネージド型ノードグループでプロビジョニングされた Amazon EKS クラスター (ワークロードを EC2 インスタンス上で実行したい場合)、または Fargate プロファイル (ワークロードを AWS Fargate 上で実行したい場合)。
  • クラスター内に設定された AWS Load Balancer Controller。これにより、Kubernetes の Ingress を作成した際に、Application Load Balancer を作成することができます。
  • アプリケーションのコンテナイメージが、Amazon ECR リポジトリまたは他の選択されたリポジトリで利用可能な状態になっていること。この記事では、Amazon ECR に登録したコンテナイメージを使用します。加えて、コンテナイメージの作成からリポジトリへの登録までの手順を、1 つ 1 つ紹介します。

環境構築

1. アプリケーションと Docker イメージの作成手順

はじめに、サンプルアプリケーションおよびそれぞれの Dockerfile を作成しましょう。今回はデモなので、単に 2 つのマイクロサービスをシミュレートするために、1 つは黄色の背景色を表示する、もう 1 つは緑色の背景色を表示する、非常にシンプルな HTML アプリケーションを使用します。

A) 各アプリケーション用に、「green」と「yellow」のディレクトリを作成します。

mkdir green/ yellow/

B) 以下のコードをコピー・実行して、Green・Yellow の各アプリケーションのディレクトリに index.html ファイルを作成します。

Green アプリケーション:

echo '<html style="background-color: green;"></html>' > green/index.html

Yellow アプリケーション:

echo '<html style="background-color: yellow;"></html>' > yellow/index.html

C) Dockerfile を、index.html ファイルと同じディレクトリに作成しましょう。

Dockerfile の解説: まず、Alpine Linux 上で動作する nginx のイメージベースを使用します。次に、アプリケーション用のディレクトリを作成し、そこに index.html ファイルをコピーします。最後に、80 番ポート (HTTP) を開放します。

Green アプリケーション:

cat <<EOF > green/Dockerfile
FROM public.ecr.aws/nginx/nginx:1.20-alpine
RUN mkdir -p /usr/share/nginx/html/green
COPY ./index.html /usr/share/nginx/html/green/index.html
EXPOSE 80
EOF

Yellow アプリケーション:

cat <<EOF > yellow/Dockerfile
FROM nginx:alpine
RUN mkdir -p /usr/share/nginx/html/yellow
COPY ./index.html /usr/share/nginx/html/yellow/index.html
EXPOSE 80
EOF

D) Green・Yellow の各アプリケーション用にそれぞれ Amazon ECR リポジトリを作成し、そこにそれぞれの Docker イメージをアップロードしましょう。

Green・Yellow の各アプリケーション用に Amazon ECR にリポジトリを作成するには、こちらのドキュメントの手順に従います。

aws ecr create-repository --repository-name green
aws ecr create-repository --repository-name yellow

E) リポジトリを作成したら、Amazon ECR コンソールを使用して作成したリポジトリにアクセスし、「View push commands」 (日本語では「プッシュコマンドの表示」) ボタンを選択してください。

下図のように、使用する OS に合った手順に 1 つずつ従って、Docker イメージを作成し、それを Amazon ECR リポジトリにアップロードします。この例では、us-east-1 リージョン (バージニア北部) を使用しています。他のリージョンを使用する場合は、リージョンコードを変更してください。

export AWS_REGION=$(aws ec2 describe-availability-zones --output text --query 'AvailabilityZones[0].[RegionName]')
export AWS_REGISTRY_ID=$(aws ecr describe-registry --query registryId --output text)
export AWS_ECR_REPO=${AWS_REGISTRY_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ECR_REPO

cd yellow/
docker build . -t yellow
docker tag yellow:latest $AWS_ECR_REPO/yellow:latest
docker push $AWS_ECR_REPO/yellow:latest
cd ..

cd green/
docker build . -t green
docker tag green:latest $AWS_ECR_REPO/green:latest
docker push $AWS_ECR_REPO/green:latest
cd ..

上記の手順は、Green・Yellow の両アプリケーションでそれぞれ実施してください。

2. Kubernetes の環境設定ファイルを定義する

次は、Kubernetes の設定ファイルを作成し、このデモで必要なオブジェクトを定義しましょう。

A) 好みのテキストエディターで、color-app.yaml という名前のファイルを作成します。必要に応じてニーズに合わせて下記内容を編集し、下記コマンドをターミナルから実行します。Amazon ECR を使用していない場合は、イメージのデータを、ご自身の Docker イメージのリポジトリの URL へ変更してください。

cat <<EOF > color-app.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: color-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: green-app
  namespace: color-app
  labels:
    app: green-app
spec:
  selector:
    matchLabels:
      app: green-app
  replicas: 2
  template:
    metadata:
      labels:
        app: green-app
    spec:
      containers:
      - name: green-container
        image: $AWS_ECR_REPO/green:latest
        ports:
            - containerPort: 80
        resources:
          limits:
            memory: "100Mi"
            cpu: "200m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: yellow-app
  namespace: color-app
  labels:
    app: yellow-app
spec:
  selector:
    matchLabels:
      app: yellow-app
  replicas: 2
  template:
    metadata:
      labels:
        app: yellow-app
    spec:
      containers:
      - name: yellow-container
        image: $AWS_ECR_REPO/yellow:latest
        ports:
            - containerPort: 80
        resources:
          limits:
            memory: "100Mi"
            cpu: "200m"
EOF

上記ファイルの解説: はじめに、color-app という namespace と、Green・Yellow の各アプリケーション用に 2 つの Kubernetes Deployment を作成します。これらの Deployment ではそれぞれ、2 つのレプリカを定義し、各アプリケーションを参照するためのラベルを追加し、コンテナイメージを指定し、そしてメモリと CPU リソースの上限を定義しています。

B) 次に、color-app.yaml ファイルに、Green・Yellow の各アプリケーション用に、NodePort タイプの 2 つの Kubernetes Service を追加しましょう。NodePort タイプの Service は、Ingress でアクセスできるように、クラスター内のアプリケーションを外部に公開します。このデモでは、上述の前提条件で触れた AWS Load Balancer Controller によって自動生成された Application Load Balancer (ALB) を通じてアクセスできます。ここで留意すべき点は、Service では各アプリケーションを、ラベルとセレクターで識別する点です。

cat <<EOF >> color-app.yaml
---
apiVersion: v1
kind: Service
metadata:
  namespace: color-app
  name: green-service
  labels:
    app: green-app
  annotations:
    alb.ingress.kubernetes.io/healthcheck-path: /green/index.html
spec:
  type: NodePort
  selector:
    app: green-app
  ports:
    - port: 80
      targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: color-app
  name: yellow-service
  labels:
    app: yellow-app
  annotations:
    alb.ingress.kubernetes.io/healthcheck-path: /yellow/index.html
spec:
  type: NodePort
  selector:
    app: yellow-app
  ports:
    - port: 80
      targetPort: 80
EOF

C) 最後に、Ingress を定義しましょう。先に触れたように、Ingress の機能は、ALB で実行されます。この条件下では、クラスター内の Pod を通じて Ingress を管理せずに済むメリットがあります。クラスターリソースを消費せずに済むことに加え、この記事で説明しているように、自動的なスケーラビリティ、高度なセキュリティ、(URL の) パスベースルーティングといった、ALB の強力な機能を利用できます。

cat <<EOF >> color-app.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: color-app-ingress
  namespace: color-app
  labels:
    app: color-app
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
    alb.ingress.kubernetes.io/healthcheck-port: traffic-port
    alb.ingress.kubernetes.io/healthcheck-interval-seconds: '15'
    alb.ingress.kubernetes.io/healthcheck-timeout-seconds: '5'
    alb.ingress.kubernetes.io/success-codes: '200'
    alb.ingress.kubernetes.io/healthy-threshold-count: '2'
    alb.ingress.kubernetes.io/unhealthy-threshold-count: '2'
spec:
  rules:
    - http:
        paths:
          - path: /yellow
            pathType: Prefix
            backend:
              service:
                name: yellow-service
                port:
                  number: 80
          - path: /green
            pathType: Prefix
            backend:
              service:
                name: green-service
                port:
                  number: 80
EOF

上記ファイルの解説:ここで留意すべき点は、Ingress をパブリックな ALB を通じてプロビジョニングし、トラフィックを Pod へ直接ルーティングし、そして ALB のヘルスチェック機能を設定するために、Ingress アノテーションを定義している点です。「spec」パラメーター内では、Green・Yellow の各アプリケーション固有の URL パスを定義し、それぞれの Service にトラフィックをルーティングしています。

3. Amazon EKS クラスター内に環境を構築する

color-app.yaml ファイルを作成したら、kubectl を使用して、Green・Yellow の各アプリケーションを Amazon EKS クラスターにデプロイします。

A) color-app.yaml ファイルが保存されているディレクトリへ移動し、kubectl を使用して Amazon EKS クラスターへアクセスできるワークステーションから、以下のコマンドを実行します。

kubectl apply -f color-app.yaml

この Amazon EKS クラスターへのアクセス設定方法にご不明な点がある場合は、RBAC の設定に関するこちらのドキュメント、およびローカルコンテキストの設定に関するこちらのドキュメントの手順に従います。

環境は、数分でプロビジョニングされます。

B) ALB がプロビジョニングされたら、自動的に行われた ALB の設定を、下図のように AWS マネジメントコンソールを使用して、Amazon EC2 > Load Balancers (日本語では「ロードバランサー」) > 当該の ALB を選択 > Listeners (日本語では「リスナー」) タブをクリック > View/edit rules (日本語では「ルールの表示/編集」) リンクをクリックすることで確認できます。

C) ALB がプロビジョニングされたら、下記コマンドを実行し、ALB にアサインされた DNS エントリ (URL) を取得・コピーします。その URL をブラウザのアドレスバーにペーストし、最後に /green または /yellow を追記すると、下記スクリーンショットのように表示されるはずです。

kubectl get ingress color-app-ingress -n color-app -o=jsonpath="{'http://'}{.status.loadBalancer.ingress[].hostname}{'\n'}"

/green

/yellow

次のステップ

今回の例に基づいて、ワークロードを実行するための yaml ファイルを使用できるようになりました。環境に応じて、Kubernetes のラベルと (namespace、Deployment、Service、Ingress などの) 各種コンポーネントの名前を変更できます。また、Deployment のイメージを置き換えて、ご自身のアプリケーションの Docker イメージを使用できます。加えて、このウォークスルーは、Amazon EKS ではなく、Amazon EC2 上で実行する Kubernetes クラスター上でも動作させることができます。

結論

この記事でははじめに、AWS にマイクロサービスアーキテクチャのさまざまな実現方法があることを確認しました。次に、コンテナを使用したアプローチの、いくつかの主要なコンセプトを説明しました。最後に、Amazon EKS と 1 つの Application Load Balancer を使用して、シンプルでコスト効率良くマイクロサービスを実装する方法を、少しずつ手順を追って紹介しました。

もしお望みでしたら、「alb.ingress.kubernetes.io/group.name」アノテーションを使用して、同一の ALB を指す複数の Ingress オブジェクトを使用することでも同じ結果が得られます。今回の例は、個別の Ingress を作成し、共通の名前をこのアノテーションに追加することで実現できます。

このアプローチを採用することで、複数の異なるチームは、ALB を頼りに各チームが自身の Service と Ingress をデプロイ・管理できるため、お互いに完全に独立できます。

翻訳はプロフェッショナルサービスの伊藤が担当しました。原文はこちらです。