プライベートサブネット内のFargateコンテナをALBにアタッチ

目次

プライベートサブネット内のFargateコンテナをALBにアタッチするための構成

以下のページで、ALBの3種類のターゲットタイプを確認しました。

あわせて読みたい
ALBの3つのターゲットタイプ(Instance, IP, Lambda) + Auto Scaling 【ALBの全ターゲットタイプを確認する構成】 ALBのターゲットとして指定できるリソースは3種類です。 instanceインスタンス ID で指定されたターゲット。 ipターゲット...

具体的には、instance, ip, lambdaです。
今回はFargateをALBにアタッチする方法を確認します。
ポイントはターゲットタイプをipに設定する必要があるという点です。

構築する環境

Diagram of attaching Fargate in Private subnet to ELB.

FargateはAuto Scalingの設定を行います。
デフォルトのタスク数は1ですが、CPUの使用率に応じて、最大3つまでスケールアウトするように設定します。

環境構築用のCloudFormationテンプレートファイル

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

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

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

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

あわせて読みたい
プライベートサブネット内のインスタンスをALBにアタッチする 【プライベートサブネット内のインスタンスをALBにアタッチする構成】 プライベートサブネット内に設置されたインスタンスを、ALBにアタッチする方法を確認します。 AWS...

Fargateの基本については、以下のページをご確認ください。

あわせて読みたい
CloudFormationでFargate入門 【CloudFormationでFargateに入門するための構成】 AWS FargateはサーバーレスでDockerコンテナを実行することができるサービスです。今回はFargate入門ということで、C...

本ページでは、上記の2ページとは異なる点と、FargateをALBにアタッチする際の、ALBターゲットグループの設定にフォーカスします。

プライベートサブネットからECRにアクセスするためには、3つのVPCエンドポイントが必要

ポイントは、プライベートサブネット内のFargateが、ECRリポジトリにアクセスするための経路としてのVPCエンドポイントを作成する必要があるという点です。

Resources:
  ECRDkrEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroup
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.dkr"
      SubnetIds:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      VpcEndpointType: Interface
      VpcId: !Ref VPC

  ECRApiEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroup
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.api"
      SubnetIds:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      VpcEndpointType: Interface
      VpcId: !Ref VPC

  S3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: "*"
            Action:
              - s3:*
            Resource:
              - !Sub "arn:aws:s3:::prod-${AWS::Region}-starport-layer-bucket/*"
      RouteTableIds:
        - !Ref PrivateRouteTable
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
      VpcId: !Ref VPC
Code language: YAML (yaml)

プライベートサブネットのFargateがECRにアクセスするためには、3種類のVPCエンドポイントを作成する必要があります。
ECRに関するエンドポイントが2つ、S3に関するエンドポイントが1つです。

詳細は以下のページをご確認ください。

あわせて読みたい
プライベートサブネットにECS(Fargate)を作成する 【プライベートサブネットにECS(Fargate)を作成する】 以下のページでFargateタイプのECSコンテナを作成する方法をご紹介しました。 https://awstut.com/2022/01/25/int...

ALBにFargateをアタッチするためには、TargetGroupのTargetTypeをipにする

ポイントは、TargetGroupです。

Resources:
  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: !Ref VPC
      Name: !Sub ${Prefix}-ALBTargetGroup
      Protocol: HTTP
      Port: !Ref HTTPPort
      HealthCheckProtocol: HTTP
      HealthCheckPath: /
      HealthCheckPort: traffic-port
      HealthyThresholdCount: !Ref HealthyThresholdCount
      UnhealthyThresholdCount: !Ref UnhealthyThresholdCount
      HealthCheckTimeoutSeconds: !Ref HealthCheckTimeoutSeconds
      HealthCheckIntervalSeconds: !Ref HealthCheckIntervalSeconds
      Matcher:
        HttpCode: !Ref HttpCode
      TargetType: ip
Code language: YAML (yaml)

TargetTypeプロパティが重要です。
「ip」に設定する必要があります。
本プロパティのデフォルト値は「instance」ですが、この値は、以下の引用の通り、ALBにFargateをアタッチする際の条件を満たしません。

[Target type] (ターゲットタイプ) で、[Instance] (インスタンス) または [IP] を選択します。

重要: サービスのタスク定義で awsvpc ネットワークモード (AWS Fargate 起動タイプで必須) を使用している場合は、ターゲットタイプとして [IP] を選択する必要があります。これは、awsvpc ネットワークモードを使用するタスクが Elastic Network Interface に関連付けられているためです。

Application Load Balancer を作成し、Amazon ECS タスクを自動的に登録する方法を教えてください。

ECSサービスでアタッチするALBを指定する

コンテナ用のサービス、タスクおよびAutoScalingグループを定義します。

本テンプレートのポイントの1つは、ALBにFargateをアタッチする方法に関する設定です。
アタッチはECSサービスリソース側の設定で行います。

Resources:
  Service:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref Cluster
      DesiredCount: 1
      LaunchType: FARGATE
      LoadBalancers:
        - ContainerName: !Sub "${Prefix}-${ServiceName}-container"
          ContainerPort: !Ref HTTPPort
          TargetGroupArn: !Ref ALBTargetGroup
      NetworkConfiguration:
        AwsvpcConfiguration:
          SecurityGroups:
            - !Ref ServiceSecurityGroup
          Subnets:
            - !Ref PrivateSubnet1
            - !Ref PrivateSubnet2
      ServiceName: !Sub "${Prefix}-${ServiceName}-service"
      TaskDefinition: !Ref TaskDefinition
Code language: YAML (yaml)

LoadBalancersプロパティで、本サービスに関連付けるELBを指定します。

ALBおよびFargateのポート番号まとめ

ALBのTargetGroupのPortプロパティ、ECSサービスのContainerPortプロパティに加えて、ECSタスク定義にもポート情報の設定項目があります。

Resources:
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${MyRepository}:latest"
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref LogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: !Ref Prefix
          Name: !Sub "${Prefix}-${ServiceName}-container"
          PortMappings:
            - ContainerPort: !Ref HTTPPort
              HostPort: !Ref HTTPPort
      Cpu: !Ref ServiceCpu
      ExecutionRoleArn: !Ref FargateTaskExecutionRole
      Memory: !Ref ServiceMemory
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      TaskRoleArn: !Ref TaskRole
Code language: YAML (yaml)

PortMappingsプロパティです。
配下の2つのパラメータがあります。
このように複数のリソースでポート番号に関するパタメータが存在します。
これらの値を統一的に設定しなければ、正常に通信することはできません。
以下に今回登場したパラメータを整理します。

加えて実行するコンテナ内のポート設定も関係してきます。
今回実行するNginxは、デフォルトで80番ポートでリッスンする設定ですので、上記の値を全て80に統一します。

Fargateタスク用のAutoScalingグループ

最後にFargateタスクのスケーリングさせるためのリソースを確認します。
まずスケーリングする対象をターゲットとして定義します。

Resources:
  ServiceScalableTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    DependsOn:
      - Service
    Properties:
      MaxCapacity: 3
      MinCapacity: 1
      ResourceId: !Select [5, !Split [":", !Ref Service]]
      RoleARN: !GetAtt ServiceTaskScalingRole.Arn
      ScalableDimension: ecs:service:DesiredCount
      ServiceNamespace: ecs
Code language: YAML (yaml)

FarateタスクのスケーリングはECSサービスに対して設定します。これはECSサービスがタスク数を調整する働きを行なっているためです。ResourceIdプロパティで対象のサービスを指定し、ScalableDimensionプロパティでタスクの希望数を意味する「ecs:service:DesiredCount」を指定します。今回はデフォルトで1つのタスクが動作し、CPUの使用率に応じてスケールアウトさせる予定ですので、MinCapacityプロパティに「1」、MaxCapacityプロパティに「3」を指定します。

次にスケーリングの条件などをまとめたポリシーを定義します。

Resources:
  ServiceScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    DependsOn:
      - Service
      - ServiceScalableTarget
    Properties:
      PolicyName: ServiceScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ServiceScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        DisableScaleIn: false
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageCPUUtilization
        ScaleInCooldown: 0
        ScaleOutCooldown: 0
        TargetValue: !Ref ServiceScalingTargetValue
Code language: YAML (yaml)

CPUの使用率を条件としますので、PolicyTypeプロパティにターゲット追跡スケーリングポリシーを意味する「TargetTrackingScaling」を指定します。TargetTrackingScalingPolicyConfigurationプロパティにて、ターゲットや追跡するメトリクスを定義します。ECSサービスのCPU使用率に関するメトリックはAWS側で事前に用意されているため、PredefinedMetricTypeプロパティに「ECSServiceAverageCPUUtilization」を指定することができます。

最後にスケーリングを実行するために必要な権限を、IAMロールとして用意します。

Resources:
  ServiceTaskScalingRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole
Code language: YAML (yaml)

AWS管理ポリシーには、ECSのスケーリングを実行するため必要な最低限の権限をまとめた「AmazonEC2ContainerServiceAutoscaleRole」が用意されてあります。今回はこちらを使用します。ちなみに本ポリシーでは、以下のアクションが許可されます。

  • ecs:DescribeServices
  • ecs:UpdateService
  • cloudwatch:DescribeAlarms
  • cloudwatch:PutMetricAlarm

サービスの更新や、スケーリングのためのCloudWatchアラームの作成に関するアクションが許可されます。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
今回は2回に分けてCloudFormationスタックを作成します。

ECRリポジトリを作成する

まずECRリポジトリを作成します。詳細は別ページをご確認ください。

残りのCloudFormationスタックを作成する

CloudFormationスタックを作成します。引き続きスタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

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

  • ECSクラスター:fa-019-cluster
  • ECSサービス名:fa-018-container1-service
  • ALB名:fa-019-ALB

作成されたリソースをAWS Management Consoleから確認します。まずクラスターを確認します。

An ECS cluster has been created.

確かにクラスターが作成されていることがわかります。クラスターの詳細を確認します。

One ECS service has been created in the ECS cluster.

クラスター内にサービスが1つ動作していることがわかります。それぞれのサービス上で動作しているタスクの詳細を確認します。

One ECS task has been created for the ECS service.

現時点では、1つのタスクが実行されていることがわかります。次にALBの詳細を確認します。

Public DNS name of ALB.

ALBに設定されたパブリックDNS名がわかりました。今回は「fa-019-ALB-1804817577.ap-northeast-1.elb.amazonaws.com」でした。ALBのTargetGroupも確認します。

The Fargate task is registered in the group whose target type is ip.

1つのタスクがターゲットとして登録されていることがわかります。

検証1:プライベートサブネット内のFargateにALBからアクセスする

準備が整いましたので、実際にブラウザからALBにアクセスします。

Access Fargate containers in private subnet via ALB.

正常に表示されました。プライベートサブネット内のFargateがALBにアタッチされていることが確認できました。CloudWatch Logsにもアクセスログが書き込まれます。

The access log will be written to CloudWatch Logs.

検証2:Fargateをスケールアウトさせる

スケールアウトを確認します。今回の構成では、CPUの使用率の増加がトリガーとなりますので、ページを繰り返しリロードし、ECSサービスの負荷を高めます。しばらく待つと、CPU使用率が閾値(0.01%)を超えてきます。

CPU utilization of ECS service.

するとスケーリングが開始されます。Eventsタブで詳細を確認することができます。

Logging about scaling out ECS tasks in the ECS service.

タスク数が3つにスケールアウトされました。アクティブなタスク数を確認します。

The number of ECS tasks has increased to three.

確かにタスク数が3つに増えています。正常にスケールされました。最後にCloudWatch Logsを確認します。

A CloudWatch Logs log streams is created for each ECS task.

ご覧の通り、タスクごとにログストリームが作成されています。ログはタスクごとに保存されるということです。

まとめ

プライベートサブネット内のFargateをALBにアタッチすることができました。

Fargateのスケールアウトの設定方法や挙動を確認しました。

目次