Containers

Service connectivity inside and outside the mesh using AWS App Mesh (ECS/Fargate)

AWS Customer:
We want to pursue service mesh and establish a separate application-networking layer for efficiently handling our microservices applications. However, we want to meshify only a subset of our microservices and gradually add other services to the mesh as we get comfortable with the configurations. What are the recommended approaches to handle service to service communication ? Is it possible to connect to other AWS services outside the mesh ? For security and compliance reasons we want to enforce tight controls on inter-service calls by making declarative statements like, for example:

  • service A talks to service B, but not service C and service D
  • service B talks to database, but not service A and service D
  • service C talks to elastic cache
  • services D talks to external API

Is there a way to control the ingress and egress traffic by using App Mesh ?

While many of you are modernizing your existing applications and building microservices that need to work together as an application, it can get challenging to manage these services and have a consistent way to route and monitor traffic between them. Using a service mesh can enable you to manage service-to-service connectivity effectively without having to build language-specific SDK’s and tools for your production-ready applications.

In this post, I will demonstrate connectivity options between the services of a microservices application that is configured to use AWS App Mesh. When you mesh a microservices application, the infrastructure layer of the application is abstracted from your application code to provide a consistent network topology. By doing so, you can decouple the operations code from your services and keep them focused on your business logic. AWS App Mesh is a managed service mesh that can be used with Amazon Elastic Container Service (ECS), Amazon Elastic Kubernetes Service (EKS), Amazon EC2 instances, and with self-managed Kubernetes on EC2.

The four concepts I will cover in the post are:

  1. How to apply a mesh to an existing microservices application deployed on ECS with Fargate launch type
  2. Understand a new AWS App Mesh construct, Virtual Gateways that is now generally available and configure it to communicate to the resources that are inside your mesh
  3. How to connect to AWS services such as Amazon Aurora PostgreSQL and Amazon ElastiCache for Redis that are outside the mesh
  4. How to connect to external or open API’s from services inside the mesh.

Introduction to Yelb (demo application)

I will be using a sample application called Yelb for this post. Yelb is a purpose-built microservices application which has a frontend, application logic, and data storage components that you would usually find in typical enterprise grade business applications.

Yelb allows users to vote on a set of alternatives (restaurants) and populate the pie charts based on number of votes received. Additionally, Yelb keeps track of number of page views as well as printing the hostname of the application server on the UI. The front end component called yelb-ui is responsible for sending the JavaScript code to the browser. The application component called yelb-appserver is responsible for
a) reading and writing to a cache server, yelb-cache and
b) reading and writing the votes from the PostgreSQL DB server, yelb-db. Note that yelb-cache and yelb-db are hosted on EC2 instances for the base model of deployment as shown below.

Figure1. Yelb Basic Architecture

Architecture walkthrough

The components of Yelb are built using different technologies. This base architecture can be deployed as is with Docker images on the cloud and use orchestration tools to manage the lifecycle of the containers and make it a cloud native application. In the following sections of this blog, we will configure Yelb to run within AWS App Mesh, and modernize its data storage layer to use Amazon Aurora PostgreSQL (yelb-db) and Amazon ElastiCache for Redis (yelb-cache).

Imagine scaling this application to hundreds of services or thousands of instances. How would you address operational challenges such as establishing network resiliency within the services that you are running? How can you achieve better insights into any network problems and get the ability to re-route traffic after failures or code changes? The path that a single request follows through the service topology can be complex, and since containers make it easy for each service to be written in either a different language or an off-the-shelf software, the library approach is no longer feasible.

This combination of complexity and criticality motivates the need for a dedicated application networking layer for service-to-service communication. By having a dedicated layer, you get the ability to decouple the operations code from application code and take advantage of having consistent configuration across the microservices while building resilient architectures at scale. AWS built AWS App Mesh with this intent.

With AWS App Mesh you can achieve the resiliency, observability, and discovery needs of your services at scale and keep your applications small and business-focused.

Use Case Deep Dive

With this context, let’s spin up an AWS version of this model, modernize the above application and add AWS App Mesh as a managed application networking layer to make it an enterprise grade application with a simplified and a robust service- to-service communication model.

AWS App Mesh abstracts the mechanics of sending requests from yelb-appserver to yelb-cache, for example, and enables a logical networking boundary for the traffic between the services. This layer helps teams to handle service-to-service communication through lightweight proxies and handle any failures that may come along the way without having to make any changes to the application code.

In this post, we will be making the following changes to the Yelb application:

  1. Add AWS App Mesh configuration to the Yelb application. Each service component will be abstracted into a virtual service backed by a virtual node that acts as a logical pointer to every service that is discoverable. The data storage layer components are discoverable by DNS and the remaining components are discoverable using AWS CloudMap in this example.
  2. Migrate the the data storage layer components yelb-db and yelb-cache that were hosted on EC2 instances to Amazon Aurora PostgreSQL and Amazon ElastiCache for Redis. I will go through the connectivity options for the application as these services that are outside the mesh.
  3. While working on 1 and 2, let’s assume that the application development team got an additional requirement to add recipes on Yelb UI. For this, the application must be able to connect to an external API www.recipepuppy.com/api/ and provide quick links to popular recipes on the Yelb UI.

The above three changes are showcased in the below diagram. The places where you see the question mark will be the areas of focus for the remaining blog.

Figure 2. Yelb Application with AWS App Mesh and missing connectivity configuration.

Egress Options

Creating a mesh is the first step of the mesh setup. All the other components such as virtual service , virtual node , routers, routes, and gateways are going to be relative to the mesh that you create. A mesh is a logical boundary of all these constructs and has two egress filter rules: ALLOW_ALL or DROP_ALL

The first option is to set the egress filter on the mesh resource to ALLOW_ALL. This setting will allow any application service within the mesh to communicate with any destination IP address inside or outside of the mesh.

The second option, DROP_ALL, allows egress only from virtual nodes to other defined resources in the service mesh. AWS App Mesh allows network traffic to flow from a virtual node to any service that is discoverable by a service discovery method. There are two supported options for service discovery, DNS or AWS Cloud Map.

In the DNS mode, you simply provide the address of a service (yelb-db in this example) to connect to another service (yelb-appserver). AWS Cloud Map being a managed cloud resource discovery service, it constantly checks the health of registered resources and ensures IP addresses are kept up-to-date. It will notify AWS App Mesh of changes, which in turn, delivers this updated location information to the relevant backend consumers (specifically to Envoy proxies), thus ensuring that consumers always have current IP addresses for the resources they need to access.

AWS App Mesh provides an abstract resource model for configuring a service mesh. One of the features of AWS App Mesh is service discovery and the following illustrates the gateway virtual node leveraging service discovery so that traffic will be sent to the virtual nodes using the endpoints. How this actually works is described below.

Figure 3. AWS App Mesh abstract resource model for the Yelb App.

Demo walk through

Step 1: Environment setup

Using a Cloud9 environment
Follow the steps listed here to create a workspace and an IAM role, attach it to the workspace, and update the IAM settings for your workspace. If you do not opt to use Cloud9, simply start in your own environment below.

Start by cloning the git repository and setting up the basic building blocks required for this walk through:

git clone https://github.com/aws/aws-app-mesh-examples.git

cd aws-app-mesh-examples/blogs/ecs-service-connectivity/yelb/deployments/platformdeployment/AWS/ECS

# This script includes all the required tools (AWS CLI, Docker, Git, jq etc) 
sh ./startup.sh 

Step 2: Infrastructure setup

In this section, we will deploy Yelb application in an ECS cluster with Fargate launch type. Supporting infrastructure components such as security groups, service discovery resources, NLB (Network Load Balancer) and target groups will also be deployed. I have the below steps such that you can reuse your existing AWS resources such as VPC ID/subnets and don’t have to spin up new resources.

Figure 4. Yelb application with App Mesh

Please update the variables in settings.sample as shown below. Modify the settings to replace <> with the actual values. Then rename the file and deploy the application.

# Replace the below values tagged with <> with the actual values
export AWS_ACCOUNT_ID=<account-id>
export AWS_DEFAULT_REGION="<region>"
# Get the Envoy image URL from https://docs.aws.amazon.com/app-mesh/latest/userguide/envoy.html
export ENVOY_IMAGE="<ENVOY-IMAGE-URL>"
export VPC="<vpc-id>"
export SUBNET_1="<subnet-id>"
export SUBNET_2="<subnet-id>"
# Run the below command to rename the settings.sample file to .settings
mv sample.settings .settings

# Run the below command. This will build the docker images for yelb-ui and yelb-appserver. This will then deploy the yelb application should take about 5-10 minutes to complete
./deploy_yelb.sh

# Step4: Run the below to setup the db table for votes
./deploy_db.sh

Step 3a: Mesh components

Now you will create a mesh. A mesh is the logical boundary for network traffic between services that reside within the mesh. After creating a mesh, all of the other configuration components will be relative to the mesh.

# Create a mesh
aws appmesh create-mesh --mesh-name yelb
cd mesh/
# Please take a moment to read the script files for your understanding.
# Create App Mesh Components for Yelb-DB
sh ./yelb-appmesh-db.sh
# Create App Mesh Components for Yelb-Redis
sh ./yelb-appmesh-redis.sh
# Create App Mesh Components for External API (recipepuppy.com)
sh ./yelb-appmesh-recipe.sh
# Create App Mesh Components for Yelb-AppServer
sh ./yelb-appmesh-appserver.sh
# Create App Mesh Components for Yelb-UI
sh ./yelb-appmesh-ui.sh
# Create Virtual Gateway, Routes
sh ./yelb-appmesh-gateway.sh

In steps 3b, 3c, 3d, and 3e below, I will highlight some of the specifications to explain the inner workings of the App Mesh configurations specific to virtual gateways and connectivity options.

Please note that there is no action required for these steps. You can move to Step 5: testing if you executed the above mesh components.

Step 3b: Virtual gateway

Virtual gateways are a new feature available that allows resources outside your mesh to communicate to resources that are inside your mesh.

This construct allows you to specify ingress rules for traffic coming into your mesh. While typically you are concerned with east-west traffic within the mesh, or traffic between services, virtual gateways provide you the ability to define the path of inbound north-south traffic. In the below snippet, we specify the mesh name where the gateway will be created and configure the listeners that the mesh endpoint is expected to receive inbound traffic from.

# Create the Virtual Gateway 
{
   "meshName" : "yelb",
   "spec" : {
      "listeners" : [
         {
            "portMapping" : {
               "port" : 80,
               "protocol" : "http"
            }
         }
      ]
   },
   "virtualGatewayName" : "yelb-gateway"
}

Note that in the virtual gateway configuration above with “http” as the listener on which the incoming traffic is accepted. The virtual gateway is created to talk to the existing mesh ‘yelb’.

Virtual gateway route represents an AWS App Mesh GatewayRoute object associated with virtual gateways to route traffic to the targeted virtual services. In this case, there are two gateway routes, one for the UI as shown below.

# Create the Virtual Gateway Route for UI Server{{
{   
   "gatewayRouteName" : "yelbui-gatewayroute",
   "meshName" : "yelb",
   "spec" : {
      "httpRoute" : {
         "action" : {
            "target" : {
               "virtualService" : {
                  "virtualServiceName" : "yelb-ui"
               }
            }
         },
         "match" : {
            "prefix" : "/"
         }
      }
   },
   "virtualGatewayName" : "yelb-gateway"
}

And the second for the app server as shown below.

# Create the Virtual Gateway Route for App Server
{
   "gatewayRouteName" : "yelbapp-gatewayroute",
   "meshName" : "yelb",
   "spec" : {
      "httpRoute" : {
         "action" : {
            "target" : {
               "virtualService" : {
                  "virtualServiceName" : "yelb-appserver"
               }
            }
         },
         "match" : {
            "prefix" : "/api"
         }
      }
   },
   "virtualGatewayName" : "yelb-gateway"
}

💡 Notice the gateway will direct the traffic based on the match criteria in both the routes defined above.

Step 3c: Connecting to the Amazon Aurora PostgreSQL

The virtual node specification for yelb-db below showcases how to connect to AWS resources, Amazon Aurora PostgreSQL in this case, using DNS as the service discovery method.

#Virtual Node Spec File for Yelb-db
{
    "meshName": "yelb",
    "spec": {
        "listeners": [
            {
                "portMapping": {
                    "port": 5432,
                    "protocol": "tcp"
                }
            }
        ],
         "serviceDiscovery": {
                "dns": {
                    "hostname": "<Aurora PostgreSQL Writer Cluster Endpoint>"
                }
            }
    },
    "virtualNodeName": "yelb-db"
}

Step 3d: Connecting to the Amazon ElastiCache for Redis

Similarly, we are connecting to the ElastiCache Redis endpoint by configuring the DNS in the yelb-redis virtual node specification:

#Virtual Node Spec File for Yelb-redis
{
    "meshName": "yelb",
    "spec": {
        "listeners": [
            {
                "portMapping": {
                    "port": 6379,
                    "protocol": "tcp"
                }
            }
        ],
         "serviceDiscovery": {
                "dns": {
                    "hostname": "<Amazon ElastiCache Redis Primary Endpoint>"
                }
            }
    },
    "virtualNodeName": "yelb-redis-server"
}

Step 3e: Connecting to the external API

*Abstract model of the Yelb architecture after adding the mesh and the constructs to enable service-to-service connectivity in our use-cases.*

Figure 5. AWS App Mesh abstract resource model for the Yelb application.

As shown in the abstract model Figure 5 above, you can connect to external API’s by creating a virtual service talking to the application component’s virtual node and configuring the routes accordingly.

# VirtualService for Yelb-appserver to connect to www.recipepuppy.com
{
    "meshName": "yelb",
    "spec": {
        "provider": {
            "virtualNode": {
                "virtualNodeName": "yelb-recipe"
            }
        }
    },
    "virtualServiceName": "www.recipepuppy.com"
}

💡 Make sure that the virtualservicename and the dns name match.

The virtual node specification below defines the port mappings and the service discovery methods to connect to the external API’s, in this case www.recipepuppy.com

#Virtual Node Spec File for Yelb-recipe
{
    "meshName": "yelb",
    "spec": {
        "listeners": [
            {
                "portMapping": {
                    "port": 80,
                    "protocol": "http"
                }
            }
        ],
        "serviceDiscovery": {
                "dns": {
                    "hostname": "www.recipepuppy.com"
                }
        }
    },
    "virtualNodeName": "yelb-recipe"
}

Note: Yelb-UI application code is modeled in a way to capture the restaurant that you select and display the recipe for the most famous dishes of that specific restaurant. As you can see below, Yelb-appserver does a GET request, captures the href from the JSON response and sends it back as the recipe link to Yelb-UI.

API request/response to recipepuppy.com/api?

Step 4: Test the application

The URL of the public load balancer is available in the “Outputs” tab of the CloudFormation stack. I can open it in my browser to verify that the deployment worked fine. You may have to wait a minute for DNS propagation.

# Use the below CLI command to retrieve the Load Balancer URL
aws cloudformation describe-stacks --stack-name yelb-fargate --query "Stacks[0].Outputs[?OutputKey=='LoadBalancerUrl'].OutputValue" --output text

Yelb Application

Step 5: Cleanup Steps

# clean up mesh components
sh ./delete-mesh-components.sh

# delete cloudformation stack
aws cloudformation delete-stack --stack-name yelb-fargate

# delete ecr repositories
aws ecr delete-repository --repository-name yelb/yelb-appserver --force
aws ecr delete-repository --repository-name yelb/yelb-ui --force

Conclusion

From the above walkthroughs, you can see the flexibility of configuring inbound and outbound application traffic using AWS App Mesh on your container workloads. I hope you can use these illustrations to your solutions to expand on your application’s network resiliency journey. You can let us know what you think of this feature, review our roadmaps, and suggest new features on the AWS App Mesh Roadmap and the AWS Containers Roadmap, also hosted on GitHub.

Next Steps

Here are few links that you can check out for hands-on walkthroughs, examples project on Github. Also, please refer to the AWS App Mesh documentation for more information with the service as well as the App Mesh user guide, which provides sections on Getting Started, Best Practices, and Troubleshooting.

Mridula Grandhi

Mridula Grandhi

Mridula Grandhi is a Senior Technical Account Manager for AWS. Mridula is also a Containers enthusiast and works with AWS customers to design, deploy, and manage their AWS workloads/architectures. In her spare time, she likes to travel and listen to music. You can reach her on Twitter via @gmridula1 (DMs are open).