How to assign a public IP to container running in AWS ECS cluster in EC2 mode
Asked Answered
E

4

21

I am trying to implement a multi-service ECS cluster using service discovery between the services. I'm attempting to follow the tutorial Creating an Amazon ECS Service That Uses Service Discovery Using the Amazon ECS CLI. However, it doesn't include a complete working example

What I've done is define two services, defined by using:

  • docker-compose.yml
  • ecs-params.yml

I can easily bring up the ECS cluster and the two services. Everything looks right. But one of the services needs a public IP address. So in the corresponding ecs-params.yml file, I put assign_public_ip: ENABLED. But no public IP address gets assigned. In the ECS console, the service details says Auto-assign public IP DISABLED, and for the Task it lists a private IP address and no public IP address.

Unfortunately, it seems this might not be possible according to the documentation on Task Networking with the awsvpc Network Mode:

The awsvpc network mode does not provide task ENIs with public IP addresses for tasks that use the EC2 launch type. To access the internet, tasks that use the EC2 launch type should be launched in a private subnet that is configured to use a NAT gateway. For more information, see NAT Gateways in the Amazon VPC User Guide. Inbound network access must be from within the VPC using the private IP address or routed through a load balancer from within the VPC. Tasks launched within public subnets do not have access to the internet.

Question: How can I work around this limitation of AWS ECS EC2 launch type?

I don't understand why the EC2 launch type would not support public IP addresses? Or - do I use a different networking mode and then a public IP address would be assigned? Why isn't the AWS documentation be clearer about this?

Source Code

The cluster is created using:

ecs-cli up --cluster-config ecs-service-discovery-stack --ecs-profile ecs-service-discovery-stack --keypair notes-app-key-pair --instance-type t2.micro --capability-iam --force --size 2

There are two services defined, as suggested by the above tutorial. The backend (a simple Node.js app in a container) and frontend (a simple NGINX server configured to proxy to the backend) services are each in their own directory. In each directory is docker-compose.yml and ecs-params.yml files.

The frontend service is brought up using:

ecs-cli compose --project-name frontend service up --private-dns-namespace tutorial --vpc ${VPC_ID}  --enable-service-discovery --container-port 80 --cluster ecs-service-discovery-stack --force-deployment

Its docker-compose.yml is:

version: '3'

services:
    nginx:
        image: USER-ID.dkr.ecr.REGION.amazonaws.com/nginx-ecs-service-discovery
        container_name: nginx
        ports:
            - '80:80'
        logging:
            driver: awslogs
            options: 
                awslogs-group: simple-stack-app
                awslogs-region: REGION
                awslogs-stream-prefix: nginx

And the ecs-params.yml is:

version: 1
task_definition:
    task_execution_role: ecsTaskExecutionRole
    ecs_network_mode: awsvpc
    task_size:
        mem_limit: 0.5GB
        cpu_limit: 256
run_params:
    network_configuration:
        awsvpc_configuration:
            subnets:
                - "subnet-00928d3fc1339b27b"
                - "subnet-0ad961884e5f93fb1"
            security_groups:
                - "sg-0c9c95c6f02597546"
        # assign_public_ip: ENABLED

The backend service is brought up using a similar command and similar docker-compose.yml and ecs-params.yml files.

Erysipeloid answered 27/3, 2020 at 2:4 Comment(2)
Are your sure your subnets are public, and they have auto-assign public ip enabled?Superaltar
same problem here :|Kopeck
S
21

You are right, when using EC2 launch type, it is not possible to assign public IP to ECS tasks.

With respect to network modes other than awsvpc, they will not help either:

  • If the network mode is set to none, the task's containers do not have external connectivity and port mappings can't be specified in the container definition.
  • If the network mode is bridge, the task utilizes Docker's built-in virtual network which runs inside each container instance.
  • If the network mode is host, the task bypasses Docker's built-in virtual network and maps container ports directly to the Amazon EC2 instance's network interface. In this mode, you can't run multiple instantiations of the same task on a single container instance when port mappings are used.

Option A - Load Balancer

If you would like your tasks to be accessed from internet, you may consider creating ECS service with Load Balancer integrated for the client to be able to route request to your task. Note that services with tasks that use the awsvpc network mode only support Application Load Balancers and Network Load Balancers.

Option B - Fargate launch type

A different option is to configure the ECS task to receive public IP addresses using Fargate launch type:

When you create ECS services/tasks using Fargate launch type, you can choose whether to associate a public IP to the ENI which ECS task using. You can refer to how to Configure a Network to know how to configure public IP with a Fargate Type service in ECS. Base on this configuration, once the task running, the ENI which the task use should have public IP, it would able to let you access the task over the internet directly.

Shikoku answered 27/3, 2020 at 12:26 Comment(0)
S
2

I am using cdk v2. I am only using public subnets and I'm using my own private ecr image. The ONLY thing that worked for me was:

assignPublicIp: true

One more trick unrelated to this post was deploying the CDK stack with 0 tasks while troubleshooting or else you run into circuitBreaker with the ECS service if it's not right the first time - even if it's set to false in code.

Example CDK with typescript:

// Moodle ECS Service
    const moodleService = new ecs.FargateService(this, 'moodle-service-pub', {
      cluster: cluster,
      taskDefinition: moodleTaskDefinition,
      desiredCount: props.serviceReplicaDesiredCount,
      capacityProviderStrategies: [ // Every 1 task which uses FARGATE, 3 tasks will use FARGATE_SPOT (25% / 75%)
        {
          capacityProvider: 'FARGATE_SPOT',
          weight: 0
        },
        {
          capacityProvider: 'FARGATE',
          weight: 1
        }
      ],
      vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
      assignPublicIp: true,
      enableECSManagedTags: true,
      maxHealthyPercent: 200,
      minHealthyPercent: 50,
      healthCheckGracePeriod: cdk.Duration.seconds(props.serviceHealthCheckGracePeriodSeconds),
      circuitBreaker: { rollback: false } // you can change this to true for faster automatic rollback
    });
Scatology answered 23/12, 2023 at 2:22 Comment(1)
It works only for Fargate backed ECS, not for EC2 backed.Motherhood
W
1

You can set the following:

Network Mode to NetworkMode.HOST

Leave assignPublicIp to false

Leave vpcSubnets as undefined Leave securityGroups as []

You can find the public IP and the public DNS as follows:

AWS Console -> Amazon Elastic Container Service -> Clusters -> cluser_name -> Infrastructure -> Container instance -> container_instance_id -> Public IP or Public DNS respectively

An example can be found here: https://github.com/LajosPolya/aws-cdk-templates/blob/main/deploy-ecs-with-ec2/lib/deploy-ecs-with-ec2-stack.ts#L9

This uses the container host's public IP. More info can be found here under the "Using a public subnet and internet gateway" heading.

Also, more info here: https://github.com/aws/aws-cdk/issues/13348

Webb answered 9/5, 2023 at 3:34 Comment(0)
W
0

The solution is to use host mode instead of awsvpc mode, and make sure that the host ec2 instance is inside the VPC.

Westering answered 7/3, 2023 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.