ECS Service Discovery with CloudMap

August 13, 2024

teaser.png

In modern application development, single-service architectures are becoming less common. Instead, there is a growing need for multiple services to communicate with each other. This post explores how CloudMap can be used to facilitate communication between ECS services. We’ll examine how this approach works with ECS Fargate and synchronous HTTP communication.

What is ECS Fargate

ECS Fargate is a serverless compute engine that allows you to run containers without managing the underlying infrastructure. It automatically handles resource allocation and scaling, enabling developers to focus on application development rather than server management. Fargate also optimizes costs and enhances security by providing efficient, isolated environments for each container.

What is CloudMap

AWS CloudMap is a service discovery tool that helps applications dynamically find and connect with the resources they need, such as databases, microservices, or other endpoints. It allows you to register any resource with a unique name and automatically updates location data as the resources change, ensuring seamless communication between services. CloudMap simplifies service discovery and improves the reliability of distributed systems by keeping track of all your application’s components in real-time.

How do they play together?

If you were to deploy a sidecar with Fargate, you might choose to place the sidecar container within the same task as the main container. This configuration allows you to communicate with the sidecar using localhost:port since both tasks share the same container environment.

However, this approach isn’t always ideal for various reasons. Scalability can be a concern, and ownership might also play a role, especially if another team manages the sidecar.

To better illustrate how CloudMap can address these challenges, let’s consider a more complex scenario involving multiple services. Imagine we’re tasked with building an API to request weather data by location. Our team is responsible for aggregating data from various APIs, with the humidity team handling the humidity-provider and the temperature team managing the temperature-provider, while we are tasked to manage the weather-aggregator.

Again, we could go the simple way and expose each of the services publicly with its own Load Balancer, but sometimes you would like to have only one entry point or not everything publicly exposed. This entry point would be the weather-aggregator which is responsible for aggregating data from the humidity-provider and the temperature-provider.

Checking the CDK documentation of the aws_ecs.Cluster, you will find that there is a property called defaultCloudMapNamespace.
The CDK documentation says The service discovery namespace created in this cluster., basically something like a Top-Level-Domain. In our context, we give it the name weather-api, because everything in this namespace is connected to weather data.

The second thing to look for is the cloudMapOptions property on the FargateService. According to the CDK CloudMap documentation, once you configure this correctly, you will expose your container on an internal domain, e.g., name.cloudmapnamespace

In our example, we would now create these three services:

  • The weather-aggregator, internally reachable via weather-aggregator.weather-api
  • The humidity-provider, internally reachable via humidity-provider.weather-api
  • The temperature-provider, internally reachable via temperature-provider.weather-api

Since we also connected the weather-aggregator as a target to our load balancer, we are also able to connect to the weather-aggregator via a public DNS name.

This is how the architecture looks once finally deployed:

diagram.svg

How does CloudMap know about the addresses?

Whenever you create a new task in Fargate, each of those tasks will get a private IP within your VPC. Since you enabled Namespaces on your Cluster, container events are captured and processed by CloudMap.

Whenever there is a new container, it gets registered; if a container becomes unhealthy or is terminated, it gets unregistered.

But where does it get registered or unregistered?

Here comes the cool part: CloudMap creates a Private hosted zone in Route53, which looks like the following:

private-dns-single.png

If I now increase the Desired tasks to 3, the Private DNS records will get immediately updated once the tasks are ready.

private-dns-multiple.png

Since this is a private hosted zone, it is only available in our VPC, and requests to a URL, for example, weather-aggregator.weather-api, will get resolved to the correct private IP of the Fargate Task.

Conclusion

Try it out! CloudMap is really handy when it comes to service-to-service communication!

The example project can be found here on GitHub.

Please be aware that if you deploy this repository, you are incurring costs on your AWS account, so once you are done with testing, you should destroy the stack.

photo of Geri

Geri is a Senior Cloud Consultant at superluminar.
He is passionate about clean code, well architected applications and new technologies.

You can follow on Geri on LinkedIn.
Or follow his cats on Instagram.