Attach Fargate in private subnet to ALB

TOC

Configure Fargate containers in private subnet to attach to ALB

I checked the following page for the three target types of ALB.

あわせて読みたい
Three target types of ALB (Instance, IP, Lambda) and Auto Scaling 【Configuration to check all target types of ALB】 There are three types of resources that can be specified as ALB targets. instanceThe targets are specified...

Specifically, they are instance, ip, and lambda.
In this article, we will check how to attach Fargate to ALB. The point is that we need to set the target type to ip.

Environment

Diagram of attaching Fargate in Private subnet to ELB.

Fargate configures the Auto Scaling settings.
The default number of tasks is 1, but you can set it to scale out to a maximum of 3, depending on the CPU usage.

CloudFormation template files

We will build the above configuration using CloudFormation. We have placed the CloudFormation template at the following URL.

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

Explanation of key points of template files

Please refer to the following page for information on how to attach resources in a private subnet to an ALB.

あわせて読みたい
Attaching instances in private subnet to ALB 【Configure instances in private subnets to be attached to ALB】 We will see how to attach an instance located in a private subnet to an ALB. The following m...

For more information on the basics of Fargate, please refer to the following page

あわせて読みたい
Introduction to Fargate with CloudFormation 【Configuration for Getting Started with Fargate with CloudFormation】 AWS Fargate is a serverless service that allows you to run Docker containers.In this i...

This page focuses on the differences from the above two pages and the configuration of the ALB target group when attaching Fargate to ALB.

Three VPC endpoints are required to access ECR from private subnets

The point is that Fargate in a private subnet needs to create a VPC endpoint as a route to access the ECR repository.

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)

In order for Fargate on a private subnet to access the ECR, three different VPC endpoints must be created.
Two endpoints related to the ECR and one endpoint related to S3.

Please see the following page for details.

あわせて読みたい
Create ECS (Fargate) in Private Subnet 【Create ECS (Fargate) in Private Subnet】 The following page shows how to create a Fargate type ECS container. https://awstut.com/en/2022/01/25/introduction...

To attach Fargate to ALB, set TargetGroup’s TargetType to “ip”

The ALB is defined in fa-019-alb.yaml. The key point of this template is the 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)

The TargetType property is important. It must be set to “ip”. The default value for this property is “instance”, but this value does not meet the requirements for attaching Fargate to ALB, as shown in the following quote.

For Target type, choose Instance or IP.

Important: If your service’s task definition uses the awsvpc network mode (required for the AWS Fargate launch type), you must choose IP as the target type. This is because tasks that use the awsvpc network mode are associated with an elastic network interface.

How can I create an Application Load Balancer and then register Amazon ECS tasks automatically?

Specify ALB to be attached in ECS service

Define the services, tasks and AutoScaling groups for the container to run in fa-019-fargate-container.yaml. One of the key points of this template is the configuration on how to attach Fargate to ALB. Attachment is done in the configuration on the ECS service resource side.

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)

In the LoadBalancers property, specify the ELB to be associated with this service.

Port Number Summary for ALB and Fargate

In addition to the Port property of the ALB TargetGroup and the ContainerPort property of the ECS service, the ECS task definition also has a configuration item for port information.

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 property. There are two parameters under it. As you can see, there are several parameters related to port numbers in multiple resources. If these values are not set in a consistent manner, it will not be possible to communicate properly. The following is a list of the parameters that were introduced this time.

In addition, the port setting in the container to be executed is also relevant. The Nginx we will run this time is set to listen on port 80 by default, so set all of the above values to 80.

AutoScaling group for Fargate tasks

Finally, let’s check the resources to scale the Fargate task. First, we define the target to be scaled.

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)

The scaling of the Farate task is configured for the ECS service. This is because the ECS service is responsible for adjusting the number of tasks. Specify the target service in the ResourceId property, and specify “ecs:service:DesiredCount” which means the desired number of tasks in the ScalableDimension property. In this case, one task will run by default and will be scaled out according to the CPU usage, so specify “1” for the MinCapacity property and “3” for the MaxCapacity property.

Next, we will define the policy that summarizes the scaling conditions.

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)

Since the condition is based on CPU usage, specify “TargetTrackingScaling” in the PolicyType property, which means a target tracking scaling policy. In the TargetTrackingScalingPolicyConfiguration property, define the target and the metrics to be tracked, since the metrics related to the CPU usage of the ECS service are prepared in advance by AWS. Since the metric for the CPU utilization of the ECS service is prepared in advance by AWS, we can specify “ECSServiceAverageCPUUtilization” in the PredefinedMetricType property.

Finally, prepare the permissions required to perform scaling as an IAM role

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)

In the AWS management policy, there is an “AmazonEC2ControllerServiceAutoscaleRole” that contains the minimum privileges required to execute ECS scaling. We will use this one this time. Incidentally, the following actions are permitted under this policy

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

Actions related to updating services and creating CloudWatch alarms for scaling are allowed.

Architecting

We will use CloudFormation to build this environment and check its actual behavior. In this article, we will create a CloudFormation stack in two parts.

Create ECR repository

First, create an ECR repository. Please refer to the separate page for details.

Create rest of CloudFormation stacks

Create a CloudFormation stack. Please continue to check the following pages for more information on creating stacks and how to check each stack.

あわせて読みたい
CloudFormation’s nested stack 【How to build an environment with a nested CloudFormation stack】 Examine nested stacks in CloudFormation. CloudFormation allows you to nest stacks. Nested ...

After checking the resources for each stack, the information for the main resources created this time is as follows

  • ECS cluster: fa-019-cluster
  • ECS service name: fa-018-container1-service
  • ALB name: fa-019-ALB

Check the created resources from the AWS Management Console. First, we will check the cluster.

An ECS cluster has been created.

You will see that the cluster has indeed been created. Check the details of the cluster.

One ECS service has been created in the ECS cluster.

You can see that there is one service running in the cluster. Check the details of the tasks running on each service.

One ECS task has been created for the ECS service.

At the moment, we can see that one task is running. Next, let’s check the details of ALB.

Public DNS name of ALB.

We now know the public DNS name that was set for the ALB. This time it was “fa-019-ALB-1804817577.ap-northeast-1.elb.amazonaws.com”. We will also check the TargetGroup of the ALB.

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

You can see that a single task has been registered as the target.

Verification 1: Accessing Fargate in private subnet from ALB

Now that everything is ready, let’s actually access the ALB from a browser.

Access Fargate containers in private subnet via ALB.

It was displayed correctly. We have confirmed that the Fargate in the private subnet is attached to the ALB. The access log is also written to CloudWatch Logs.

The access log will be written to CloudWatch Logs.

Verification 2: Scaling out Fargate

Check the scale-out. In this configuration, the trigger is an increase in CPU usage, so reload the page repeatedly to increase the load on the ECS service. After waiting for a while, the CPU usage will exceed the threshold (0.01%).

CPU utilization of ECS service.

Then the scaling will start. You can see the details in the Events tab.

Logging about scaling out ECS tasks in the ECS service.

The number of tasks has been scaled out to three. Check the number of active tasks.

The number of ECS tasks has increased to three.

Indeed, the number of tasks has increased to three. It has been successfully scaled. Finally, let’s check the CloudWatch Logs.

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

As you can see, a log stream has been created for each task. It means that logs are stored for each task.

Summary

We were able to attach Fargate in a private subnet to ALB.

We checked how to configure Fargate scale-out and its behavior.

TOC