Configure Fargate containers in private subnet to attach to ALB
I checked the following page for the three target types of ALB.
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
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.
For more information on the basics of Fargate, please refer to the following page
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.
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.
- Port property of AWS::ElasticLoadBalancingV2::TargetGroup: Destination port number when the ALB sends traffic to the target (ECS service)
- ContainerPort property of AWS::ECS::Service LoadBalancers: the port number that ECS listens on when receiving traffic from ALB.
- ContainerPort property of AWS::ECS::TaskDefinition PortMapping: Port number to be assigned to the container; set to the same value as the ContainerPort property of AWS::ECS::Service LoadBalancers. It must be set to the same value as the ContainerPort property of AWS::ECS::Service LoadBalancers.
- HostPort property of AWS::ECS::TaskDefinition PortMapping: The port number to be assigned to the host where the container will be launched. in the case of Fargate (when the network mode is awsvpc), it must be set to the same value as the AWS::ECS:: In the case of Fargate (when the network mode is awsvpc), it must be set to the same value as the ContainerPort property of AWS::ECS:: TaskDefinition PortMapping.
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.
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.
You will see that the cluster has indeed been created. Check the details of the cluster.
You can see that there is one service running in the cluster. Check the details of the tasks running on each service.
At the moment, we can see that one task is running. Next, let’s check the details 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.
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.
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.
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%).
Then the scaling will start. You can see the details in the Events tab.
The number of tasks has been scaled out to three. Check the number of active tasks.
Indeed, the number of tasks has increased to three. It has been successfully scaled. Finally, let’s check the CloudWatch Logs.
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.