ALB・Fargate(ECS)構成にSorryコンテンツを配置する

ALB・Fargate構成にSorryコンテンツを配置する

ALB・Fargate(ECS)構成にSorryコンテンツを配置する

ALBおよびFargate(ECS)でシステムを構成している場合、システムメンテナンス等の理由で、一時的にFargateを停止する場合があります。
メンテナンス中にユーザからアクセスがあった場合は、ユーザにその旨を伝える必要があります。

今回はLambda関数でSorryコンテンツを用意し、これをALBに関連付けます。
そしてメンテナンス時は、ユーザからのトラフィックをこちらにルーティングするように設定します。

構築する環境

Diagram of create Sorry contents for ALB and Fargate system

ALBに2つのコンテンツを関連づけます。

1つ目はFargateです。
ECRリポジトリからイメージをプルし、コンテナを生成します。

2つ目はLambda関数です。
Sorryコンテンツとして動作します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-fa/tree/main/086

テンプレートファイルのポイント解説

本ページはALB・Fargate構成において、Sorryコンテンツを配置する方法を取り上げます。

プライベートサブネット内のFargate(ECS)をALBにアタッチする方法については、以下のページをご確認ください。

https://awstut.com/2022/01/29/attach-fargate-in-private-subnet-to-elb

ALBのパスベースルーティングで、複数のターゲットグループにトラフィックを転送する方法については、以下のページをご確認ください。

https://awstut.com/2022/02/23/fowarding-traffic-to-multiple-target-groups-with-path-based-routing-in-alb

Lambda等のコンテンツをALBにアタッチする方法については、以下のページをご確認ください。

https://awstut.com/2022/02/26/three-target-types-of-alb

Lambda関数

Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      Architectures:
        - !Ref Architecture
      Code:
        ZipFile: |
          import json

          def lambda_handler(event, context):
            return {
              'statusCode': 503,
              'isBase64Encoded': False,
              'headers': {
                'Content-Type': 'text/html; charset=utf-8'
              },
              'body': 'Sorry Contents from Lambda Function.'
            }
      FunctionName: !Sub "${Prefix}-function"
      Handler: !Ref Handler
      Runtime: !Ref Runtime
      Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)

特別な設定は不要です。
ステータスコード503と共に、「Sorry Contents from Lambda Function.」を返すシンプルな関数です。

ポイントはLambda関数のリソースベースのポリシーです。

Resources:
  Permission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref Function
      Action: lambda:InvokeFunction
      Principal: elasticloadbalancing.amazonaws.com
Code language: YAML (yaml)

本関数をELBが呼び出すことを許可する内容です。
後述のALBがSorryコンテンツを返すための設定です。

ALB

ターゲットグループ

Resources:
  ALBTargetGroupFargate:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      ...

  ALBTargetGroupLambda:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckEnabled: false
      Name: !Sub "${Prefix}-ALBTargetGroupLambda"
      Targets:
        - Id: !Ref FunctionArn
      TargetType: lambda
Code language: YAML (yaml)

ALB関係のリソースとして、ターゲットグループを定義します。

2つのターゲットグループが必要となります。

1つ目はFargate用のグループです。
こちらの説明は割愛します。

2つ目はLambda関数用のグループです。
Targetsプロパティに先述のLambda関数のARNを、TargetTypeプロパティに「lambda」を指定します。
これでALBに着信したトラフィックのルーティング先として、Lambda関数を設定することができました。

リスナールール

Resources:
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref ALBTargetGroupFargate
          Type: forward
      LoadBalancerArn: !Ref ALB
      Port: !Ref HTTPPort
      Protocol: HTTP

  ALBListenerRule1:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - TargetGroupArn: !Ref ALBTargetGroupFargate
          Type: forward
      Conditions:
        - Field: path-pattern
          PathPatternConfig:
            Values:
              - /*
      ListenerArn: !Ref ALBListener
      Priority: 1

  ALBListenerRule2:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - TargetGroupArn: !Ref ALBTargetGroupLambda
          Type: forward
      Conditions:
        - Field: path-pattern
          PathPatternConfig:
            Values:
              - /*
      ListenerArn: !Ref ALBListener
      Priority: 2
Code language: YAML (yaml)

2つのルールを作成します。

1つ目はFargate用です。
全てのパスにマッチするように設定した上で、Priorityプロパティに「1」を指定します。
これでこのルールが優先的に採用されます。

2つ目はLambda関数用です。
こちらも全てのパスにマッチするように設定した上で、Priorityプロパティに「2」を指定します。
優先度の関係で、通常時は1つ目のFargate用のルールが適用されます。
メンテナンス時は、これらの優先度を手動で調整することで、SorryコンテンツであるLambda関数に、トラフィックをルーティングさせます。

(参考)アプリコンテナ

index.html

<html>
  <head>
  </head>
  <body>
    <h1>fa-086 index.html</h1>
  </body>
</html>
Code language: HTML, XML (xml)

シンプルなHTMLファイルです。

Dockerfile

FROM nginx:latest

COPY index.html /usr/share/nginx/html

EXPOSE 80
Code language: Dockerfile (dockerfile)

nginxをベースとし、ルートのHTMLファイルを先述のものに差し替えたイメージを作成します。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

まずECRリポジトリ用CloudFormationスタックを作成し、作成されたECRにDockerイメージをプッシュします。
この手順の詳細は、以下のページをご確認ください。

https://awstut.com/2022/01/25/introduction-to-fargate

イメージの準備が完了した後に、残りのスタックを作成します。
ネストされたスタックの作成および各スタックの確認方法については、以下のページをご確認ください。

https://awstut.com/2021/12/02/cloudformation-nested-stacks

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • ALBのDNS名:fa-086-ALB-2131954533.ap-northeast-1.elb.amazonaws.com
  • ECRリポジトリ:fa-086
  • ECSクラスター:fa-086-cluster
  • ECSサービス:fa-086-service
  • Lambda関数:fa-086-lambda

AWS Management Consoleから各リソースを確認します。
ALBを確認します。

Detail of ALB 1.

ALBが正常に作成されていることがわかります。

ALBのターゲットグループも確認します。

Detail of ALB 2.
Detail of ALB 3.

2つのターゲットグループが作成されていることがわかります。
それぞれFargateおよびLambda関数用のグループです。

リスナールールを確認します。

Detail of ALB 4.

Fargate向けのルールが最優先で、その次がLambda関数向けのルールであることがわかります。

動作確認

正常時

準備が整いましたので、ALBにアクセスします。
先ほど確認したALBのドメイン名にアクセスします。

Detail of ALB 5.

ALB経由でFargate上のコンテナにアクセスできました。
つまり最優先のFargate向けのルールが適用されたということがわかります。

メンテナンス時

続いてメンテナンス時を想定し、Sorryコンテンツを表示させます。

まずAWSマネージメントコンソールから、手動でALBのリスナールールを変更します。

Detail of ALB 6.

優先順位を変更しました。
Lambda関数向けのルールを最優先に設定しました。

改めてALBのドメイン名にアクセスします。

Detail of ALB 7.

SorryコンテンツであるLambda関数の文字列が返ってきました。
このようにリスナールールを変更することによって、メンテナンス時にSorryコンテンツを出力することができます。

まとめ

ALBおよびFargate構成において、Lambda関数を用いたSorryコンテンツを配置する方法を確認しました。
リスナールールの優先順位を変更することによって、メンテナンス時に、Sorryコンテンツを表示できることを確認しました。