5

According to the AWS documentation on NAT Gateways, they cannot send traffic over VPC endpoints, unless it is setup in the following manner:

A NAT gateway cannot send traffic over VPC endpoints [...]. If your instances in the private subnet must access resources over a VPC endpoint [...], use the private subnet’s route table to route the traffic directly to these devices.

Following this example in the docs, I created the following configuration for my ECS app:

  1. VPC (vpc-app) with CIDR 172.31.0.0/16.
  2. App subnet (subnet-app) with the following route table:
    Destination     |  Target
    ----------------|-----------
    172.31.0.0/16   |   local  
    0.0.0.0/0       |  nat-main
  1. NAT Gateway (nat-main) in vpc-app in subnet default-1 with the following Route Table:
    Destination     |    Target
    ----------------|--------------
    172.31.0.0/16   |     local  
    0.0.0.0/0       |  igw-xxxxxxxx
  1. Security Group (sg-app) with port 443 open for subnet-app.
  2. VPC Endpoints (Interface type) with vpc-app, subnet-app and sg-app for the following services:
    com.amazonaws.eu-west-1.ecr.api  
    com.amazonaws.eu-west-1.ecr.dkr  
    com.amazonaws.eu-west-1.ecs  
    com.amazonaws.eu-west-1.ecs-agent  
    com.amazonaws.eu-west-1.ecs-telemetry  
    com.amazonaws.eu-west-1.s3 (Gateway)

It's also important to mention that I've enabled DNS Resolution and DNS Hostnames for vpc-app, as well as the Enable Private DNS Name option for the ecr-dkr and ecr-api VPC endpoints.

I've also tried working only with Fargate containers since they don't have the added complication of the ECS Agent, and because according to the docs:

Tasks using the Fargate launch type only require the com.amazonaws.region.ecr.dkr Amazon ECR VPC endpoint and the Amazon S3 gateway endpoint to take advantage of this feature.

This also doesn't work and every time my Fargate tasks run I see a spike in Bytes out to source under nat-main's Monitoring.

No matter what I try, the EC2 instances (and Fargate tasks) in the subnet-app are still pulling images using nat-main and not going to the local address of the ECR service.

I've restarted the ECS Agent and made sure to check all the boxes in the ECS Interface VPC Endpoints guide AND the ECR Interface Endpoints guide.

What am I missing here?

Any help would be appreciated.

guychouk
  • 671
  • 1
  • 8
  • 28

2 Answers2

5

After many hours of trial and error, and with lots of help from @jogold, the missing piece was found in this blog post:

The next step is to create a gateway VPC endpoint for S3. This is necessary because ECR uses S3 to store Docker image layers. When your instances download Docker images from ECR, they must access ECR to get the image manifest and S3 to download the actual image layers.

After I created the S3 Gateway VPCE, I forgot to add its address to subnet-app's routing table, so although the initial request to my ECR URI was made using the internal address, the downloading of the image from S3 still used the NAT Gateway.

After adding the entry, the network usage of the NAT Gateway dropped dramatically.

More information on how to setup Gateway VPCE can be found here.

guychouk
  • 671
  • 1
  • 8
  • 28
  • 1
    Great that you found a solution, will add a note on the S3 endpoint in my answer. – jogold Apr 29 '19 at 15:35
  • Since your first response is more extensive and includes the note on the S3 endpoint, I've decided to accept your answer as the correct one, thanks for all the help! – guychouk Apr 29 '19 at 18:33
3

Interface VPC endpoints work with DNS resolution, not routing.

In order for you configuration to work, you need to ensure that you checked Enable Private DNS Name when you created the endpoint. This enables you to make requests to the service using its default DNS hostname instead of the endpoint-specific DNS hostnames.

enter image description here

From the documentation:

When you create an interface endpoint, we generate endpoint-specific DNS hostnames that you can use to communicate with the service. For AWS services and AWS Marketplace partner services, you can optionally enable private DNS for the endpoint. This option associates a private hosted zone with your VPC. The hosted zone contains a record set for the default DNS name for the service (for example, ec2.us-east-1.amazonaws.com) that resolves to the private IP addresses of the endpoint network interfaces in your VPC. This enables you to make requests to the service using its default DNS hostname instead of the endpoint-specific DNS hostnames. For example, if your existing applications make requests to an AWS service, they can continue to make requests through the interface endpoint without requiring any configuration changes.

The alternative is to update your application to use your endpoint-specific DNS hostnames.

Note that to use private DNS names, DNS resolution and DNS hostnames must be enabled for your VPC:

enter image description here

Also note that in order to use ECR/ECS without a NAT gateway, you need to configure a S3 endpoint (gateway, requires route table update) to allow instances to download the image layers from the underlying private Amazon S3 buckets that host them. More information in Setting up AWS PrivateLink for Amazon ECS, and Amazon ECR

jogold
  • 6,667
  • 23
  • 41
  • Thank you for your reply, I did enable DNS resolution and DNS hostnames for the VPC, as well as the Enable Private DNS Name option for the ECR + ECR-API VPCEs. I've updated my question to include these enabled options as part of the configuration. What exactly do you mean by: *Interface VPC endpoints work with DNS resolution, not routing.* ? – guychouk Apr 29 '19 at 08:24
  • 1
    Can you run `dig +short ecr.eu-west-1.amazonaws.com` from inside your VPC? I mean that your app will still try to reach `ecr.eu-west-1.amazonaws.com` but it should resolve to the IP address of one of the ENI for the endpoint. It's not like S3 that must be configured in a route table. – jogold Apr 29 '19 at 08:28
  • Just ran your suggestion, I got a public IP and not an internal one, what can I try to find out why this is happening? – guychouk Apr 29 '19 at 10:40
  • Can you share a screenshot of the _Details_ tab of one of your endpoints? – jogold Apr 29 '19 at 10:47
  • Sure, here are the details: [ECR DKR VPCE](https://i.imgur.com/FOKwkaq.png) and [ECR API VPCE](https://i.imgur.com/pZXSCCU.png). I also ran `dig +short dkr.ecr.eu-west-1.amazonaws.com` and `dig +short api.ecr.eu-west-1.amazonaws.com` from inside the EC2 instance in `vpc-app`, and I **did** get a private IP address for both of them, what gives? :( – guychouk Apr 29 '19 at 10:56
  • I even went a step further and checked `dig +short .dkr.ecr.eu-west-1.amazonaws.com` which is the repository URI where my images are stored, and I got a private IP as well. – guychouk Apr 29 '19 at 11:09
  • The fact that when I look at *Packets out to destination (Count)* in the *Monitoring* section of the NAT Gateway, I clearly see that on average my count is approximately 1500, and as soon as I launch my Fargate task from the CLI with nothing but `/bin/echo hey` in the CMD of the Docker container, I see a spike to nearly 20,000! The image weighs nearly 400 MB, which explains the spike. It's costing me a fortune and I can't figure out why. – guychouk Apr 29 '19 at 12:32
  • 1
    A value greater than zero for `PacketsOutToDestination` indicates that there is traffic going **to** the internet from clients that are behind the NAT gateway (= upload). – jogold Apr 29 '19 at 12:42
  • 1
    You can confirm that your VPC endpoints for ECR are working by shutting down your NAT gateway temporarily and trying to pull/start a task. – jogold Apr 29 '19 at 12:44
  • I just ran the test you suggested and you're right, when shutting down the NAT, tasks can still pull images successfully, meaning the VPCEs are working properly. But one thing still bothers me: How come that when I had only one, simple EC2 instance with the same amount of traffic as my newly created ECS app, the costs were nearly nothing, and now they look like [this](https://i.imgur.com/AvxANzH.png)? Does it matter how many connections there are to the NAT? If I setup three Fargate tasks which run every 12 minutes, is it the same as three EC2 instances which are always running? – guychouk Apr 29 '19 at 13:15
  • The number of connections doesn't matter, you pay based on traffic (GB data processed). Something else must have changed in your traffic pattern... – jogold Apr 29 '19 at 13:31
  • @cudacoder I've run into an issue which may be the same as yours - I'm running Fargate tasks frequently and each time they are provisioned, they docker pull from ECR over my NAT gateway, which ends up being a lot of throughput whereas with EC2 based tasks, this doesn't happen because the images are cached locally. I'm actually thinking about switching to EC2 based tasks for this exact reason. – matthewcummings516 May 23 '19 at 14:57
  • @matthewcummings516 That does sound a lot like my situation, the thing is I'm running both EC2 based tasks AND Fargates for general scheduled tasks, but you don't have to switch if Fargate suits your case better than EC2 based tasks, simply make sure to follow the guides I've added to my question and make sure to check the S3 endpoints settings as jogold suggested, and you should be able to set it up pretty easily, also make sure to ask on [this](https://www.reddit.com/r/devops/comments/biebzp/aws_ecs_vpc_endpoints_and_nat_gateway_question/?utm_source=share&utm_medium=web2x) thread for help. – guychouk May 26 '19 at 17:13
  • @cudacoder that's funny, I ran across the S3 endpoint issue too, I forgot that I had commented here. . . it's definitely a bit wonky/counterintuitive. Also, fwiw I don't believe that EC2 based ECS jobs behave the way I expected, I think you need to set `ECS_IMAGE_PULL_BEHAVIOR` in the agent to `prefer-cached`. – matthewcummings516 May 27 '19 at 18:38