I am running a ECS task in a service. I would like to use it with a public domain name, so it needs a public static IP. I have already allocated an Elastic IP, and tried many ways how to eventually tie it with the task.
Here is what I have tried so far:
- Run a task with a public IP – works great, only the IP is not static (task’s public IP), so this solution is not acceptable.
- Run a task without the public IP – task doesn’t start because of this error.
- Set up a NAT with the EIP, assigned it explicitly with the custom RT to the subnet where the task is – doesn’t work.
- Set up a NLB with the EIP and created a service with the task, mapped to the NLB, following this guide. Doesn’t work. Tried ALB as well, no success.
VPC has a default RT in most cases.
What is the working ECS with EIP setup?
Please provide a detailed guide if possible.
Details on ECS with NLB/EIP setup
(following the above guide)
- Internet-facing, IPv4
- Network mapping: Selected one AZ with the subnet where the task is; Use an Elastic IP address (the allocated one)
- Security group: default with inbound HTTP allowed for all sources and outbound 443 for all sources (needed for access to ECR)
- Listener: TCP:80 (default) / Target group: type IP addresses; all other settings are set to default: TCP:80; IPv4, network – my VPC;
- Service setup: Fargate Latest; Application type: Service; task definition: my-server:latest; service type: Replica; Networking: only one subnet which is used for the NLB network mapping; Load Balancing – the target group which I created in the previous step.
Result:
- I can access HTTP by the public dynamic IP of the task.
- I can’t access HTTP by the EIP assigned to the NLB.
- The health check of the TG fails and the task is stopped.
Same attempt, but the service is configured without the public IP:
- the service doesn’t even start with this error: ResourceInitializationError: unable to pull secrets or registry auth (link above)
CloudFormation template
AWSTemplateFormatVersion: "2010-09-09"
Description: HelloWorld ECS Fargate Task with NLB and EIP
Resources:
HelloWorldVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
DependsOn: HelloWorldIGW
HelloWorldRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref HelloWorldVPC
HelloWorldRoute:
Type: AWS::EC2::Route
DependsOn: AttachGateway
Properties:
RouteTableId: !Ref HelloWorldRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref HelloWorldIGW
HelloWorldIGW:
Type: AWS::EC2::InternetGateway
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref HelloWorldVPC
InternetGatewayId: !Ref HelloWorldIGW
HelloWorldSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref HelloWorldVPC
CidrBlock: 10.0.0.0/24
AvailabilityZone: us-east-1a
HelloWorldSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref HelloWorldVPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: us-east-1b
Subnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref HelloWorldSubnet1
RouteTableId: !Ref HelloWorldRouteTable
Subnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref HelloWorldSubnet2
RouteTableId: !Ref HelloWorldRouteTable
HelloWorldNLB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: HelloWorldNLB
Subnets:
- !Ref HelloWorldSubnet1
- !Ref HelloWorldSubnet2
Scheme: internet-facing
LoadBalancerAttributes:
- Key: load_balancing.cross_zone.enabled
Value: true
IpAddressType: ipv4
SecurityGroups:
- !GetAtt HelloWorldNLBSecurityGroup.GroupId
HelloWorldNLBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: NLBSecurityGroup
VpcId: !Ref HelloWorldVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
HelloWorldECSCluster:
Type: AWS::ECS::Cluster
HelloWorldECSTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: HelloWorldTask
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: "256"
Memory: "512"
ExecutionRoleArn: "arn:aws:iam::USERID:role/ecsTaskExecutionRole"
ContainerDefinitions:
- Name: HelloWorldWebServerContainer
Image: any-image-exposing-port-80:latest
Essential: true
PortMappings:
- ContainerPort: 80
HelloWorldECSService:
Type: AWS::ECS::Service
Properties:
ServiceName: HelloWorldECSService
Cluster: !Ref HelloWorldECSCluster
TaskDefinition: !Ref HelloWorldECSTaskDefinition
LaunchType: FARGATE
DesiredCount: 2
NetworkConfiguration:
AwsvpcConfiguration:
Subnets:
- !Ref HelloWorldSubnet1
- !Ref HelloWorldSubnet2
SecurityGroups:
- !GetAtt HelloWorldECSSecurityGroup.GroupId
AssignPublicIp: ENABLED
HelloWorldECSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ECSSecurityGroup
VpcId: !Ref HelloWorldVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Outputs:
NLBDNSName:
Description: HelloWorldNetworkLoadBalancer
Value: !GetAtt HelloWorldNLB.DNSName
As the result, I have two tasks running in two subnets with public IP’s, by which they can be accessed. NLB has a public domain name, which doesn’t show anything on port 80.
2
Answers
Here is the CloudFormation template that produces a working stack:
The service must be associated to the target group, which is associated to the listener, which is associated to the load balancer. The Elastic IP must be mapped to the subnet in the load balancer settings.
In your ECS Service setup, did you set:
The above is an extract from my CloudFormation template. I strongly encourage you to transform your manual steps into a reproducible "Infrastructure As Code (IAC)" cloudformation template. It will be easier to reproduce your stack instead of relying on documents (click here, do this, do that). And will make it easier for others to help you debug and see what you’ve set for each AWS Resource (documentation as code). Cloudformation is rather quick to learn, totally worth the time. Hopefully the AssignPublicIp fixes the issue, otherwise I think you’ll get more answers by showing a Cloudformation template (easier to convey your full LB/SG/ECS/TaskDef/Cluster/Service config).
Also, if I may propose an improvement on your design. The DNS (Route53 or other) should point to the LoadBalancer, not directly to ECS. And the LB will then route to your ECS cluster/setup. Ideally, your ECS task would run in a private subnet (not in a public subnet), to reduce the attack surface. There are lots of Cloudformation stacks that you can run that have a VPC, public/private subnets, internet gateways, etc. You then just need to add your ECS cluster/service/taskdef.