Containers
How To Expose Multiple Applications on Amazon EKS Using a Single Application Load Balancer
Introduction
Microservices architectures are default for cloud-native applications. Greater granular scalability, isolation of functions, and the possibility of having independent teams working on specific application functionalities are just some of the reasons that such an architectural pattern has been widely adopted. Together with the fact that new applications are being borne in the cloud, you have the perfect match between modular and scalable architectures, with technologies offered by the cloud, that makes it easy to deploy, operate, and manage your microservices.
One pattern, several implementation options
At AWS, there are at least two common paths to be followed for microservices architecture implementation:
1) Move different components of an application or service into a container or a set of containers (also known as containerization) and delegate their management to a container orchestrator, like Amazon Elastic Kubernetes Service (Amazon EKS) and Amazon Elastic Container Service (Amazon ECS).
2) Move different components of an application or service into serverless functions, and delegate their management to AWS Lambda.
The next step is to expose each one of those microservices, regardless of whether they are containers or functions, through an endpoint so a client or an API can send requests and get responses. Usually, you’ll want every microservice to have its own endpoint. For example, every suffix (“/”) at the end of a web address will point to a different microservice:
www.example.com/service1 > microservice1
www.example.com/service2 > microservice2
www.example.com/service3 > microservice3
…
This type of load balancing or routing is known as path-based routing (routing based in the URL path).
One of the most important benefits of this approach is the low cost and simplicity that it has to expose dozens of applications. Both the Application Load Balancer (ALB) and the Amazon API Gateway support this feature. Therefore, with a single ALB or a single API Gateway, it is possible to expose your microservices running as containers with Amazon EKS or Amazon ECS or as serverless functions with AWS Lambda.
In this blog post, we are going to demonstrate how to expose your applications that are running as containers and being orchestrated by Amazon EKS through an Application Load Balancer.
Architecture and Components
Amazon EKS Cluster is the Kubernetes cluster where the application will run. Amazon EKS is an AWS service that removes the complexity of managing a Kubernetes’ control plane, which is made of API servers and etcd nodes, allowing developers to focus on the data plane, which is the actual servers (data nodes) running the application.
Auto Scaling Group is an AWS feature that allows Amazon EC2 instances to scale in and out, triggered by a monitoring metric. To scale the cluster, you’ll need to use the Cluster Autoscaler, which uses the Auto Scaling group on your behalf. For this demo, this feature will be only used to keep a minimum number of EC2 instances running in high availability. AWS Fargate is another option to run your pods at scale, instead of running those on EC2 instances. By running your containers as Fargate pods, you can also have your data plane fully managed by AWS.
Application Load Balancer (ALB) automatically distributes traffic between multiple targets like EC2 instances, containers, or IP addresses in one or more Availability Zones. An ALB works at layer 7 (Application) of the OSI model (HTTP/HTTPS) and supports, among many other features, path conditions, allowing you to define rules that can route requests based on the URL path (also known as path-based routing). ALB is one of the three load balancing options supported by Elastic Load Balancing.
Ingress is a Kubernetes resource that manages external traffic to the cluster, typically HTTP. An Ingress usually provides load balancing, SSL, and virtual hosting. In order to have the Ingress features in a cluster, you need to install an Ingress Controller.
AWS Load Balancer Controller is a controller that helps manage Elastic Load Balancers for Kubernetes clusters. For this scenario, we are using the Ingress kind to automatically provision an ALB and configure the routing rules needed for this ALB to be defined via Kubernetes manifests.
The final goal is to have different applications answering requests through different paths, but with a single ALB.
Requirements
To implement this solution, you must have the following prerequisites:
- An Amazon EKS cluster provisioned with node groups, if you choose to run your workloads on EC2 instances, or a Fargate profile if you choose to run them on AWS Fargate
- The AWS Load Balancer Controller configured in your cluster, so that an Application Load Balancer is created when you create an Ingress in Kubernetes
- The container images of your applications must be available in an Amazon ECR repository or some other repository of your choice. In this blog post, we will use container images registered in Amazon ECR, and we will demonstrate the step-by-step image creation process as well as its submission to the repository.
Environment creation
1. Application and Docker image creation process
Let’s first create our example applications and their respective Dockerfiles. In our case, as this is a demo environment, we will use an extremely simple HTML application that displays a yellow background and another one that displays a green background, just to simulate two microservices.
A) Create a directory called green and another one called yellow.
B) Copy the code from each application and save it as index.html in their respective directories.
Green application:
Yellow application:
C) Let’s now create the Dockerfiles in the same directories as the index.html files.
Understand the file: We will use an image base of Nginx with Alpine, we will create a directory for the application, and we will copy the index.html file to this directory. We will also expose our application on port 80 (HTTP).
Green application:
Yellow application:
D) Let’s now create an Amazon ECR repository for each application and upload the respective Docker images.
Follow this documentation to create the green and yellow repositories for each of the applications.
E) Having created the repositories, access them and select the View push commands button.
Follow the step-by-step guide that will be displayed for your operating system to create the Docker image and upload it to the repository. In this example, we are using us-east-1 as the Region. Change the Region code if you are not using N. Virginia.
Remember to perform this procedure for both applications.
2. Defining the environment configuration file in Kubernetes
Let’s now create a Kubernetes configuration file defining the objects in our environment.’
A) Create a file named color-app.yaml in your favorite text editor, edit it to your needs if necessary, and execute the command in your terminal. If you’re not using Amazon ECR, change the image data to the URL of your Docker image repository:
Understand the file: First, we will create a namespace called color-app and two Deployments, one for each application. In these deployments, we will define two replicas, we will add some labels referencing each application, we will indicate the image, and we will define limits for memory and CPU resources.
B) Let’s now add two Services of the NodePort type to our file, one for each application. Services of the NodePort type serve to expose applications in the cluster so that they can be accessed by Ingress, which in our case is done through the Application Load Balancer (ALB) that is automatically created by the AWS Load Balancer Controller, mentioned in the prerequisites. Note that in Services, we are identifying each application through labels and selectors:
C) Finally, let’s define our Ingress. As mentioned earlier, the Ingress function will be performed by ALB. With this condition, you have the advantage of not having to manage your Ingresses through Pods in your cluster. You will not consume cluster resources for that, and you will also be able to use the powerful features of ALB, such as automatic scalability, advanced security, and functionalities such as path-based routing (URL), which we’re demonstrating in this blog post.
Understand the file: Note that we are defining Ingress annotations so that the Ingress is provisioned through a public ALB, traffic is routed directly to the Pods, and ALB health check characteristics are configured. Within the “spec” parameter, we are defining specific URL paths for each application and routing traffic to their respective Services.
3. Creating the environment in the Amazon EKS cluster
After creating the yaml file, we will deploy our application on the Amazon EKS cluster, through kubectl.
A) Go to the directory where the color-app.yaml file was saved and run the following command from a workstation that has access to the cluster through kubectl.
kubectl apply -f color-app.yaml
If you have questions about how to configure this access, follow this documentation for RBAC configuration and this one for local context configuration.
The environment will be provisioned in a few minutes.
B) Once ALB is provisioned, you can check the settings automatically made in ALB by going to the AWS Management Console in Amazon EC2 > Load Balancers > select the ALB > click the Listeners tab > click View/edit rules.
C) After the ALB is provisioned, run the command below and copy the DNS entry assigned to it. Paste the URL into your browser using /green or /yellow at the end, as shown in the images below:
kubectl get ingress color-app-ingress -n color-app -o=jsonpath="{'http://'}{.status.loadBalancer.ingress[].hostname}{'\n'}"
/green
/yellow
Next steps
Based on this example, you can now use the yaml file for your own workload. You can rename the Kubernetes labels and components (namespace, Deployments, Services, and Ingress) for your environment and use your own application’s Docker image to replace it in Deployment. Also, this walkthrough can be performed on a Kubernetes cluster that runs purely on Amazon EC2 instead of Amazon EKS, if that is your case.
Conclusion
In this blog post, we saw that there are different ways to approach a microservices architecture within AWS. We then explained some main concepts of the approach using containers. Finally, we demonstrated, in a step-by-step procedure, how to implement it in a simple and cost-effective way using Amazon EKS with a single Application Load Balancer.
If you wish, you could also achieve the same results by using several Ingress objects pointing to the same ALB using the annotation “alb.ingress.kubernetes.io/group.name”. In this case, you would create individual Ingresses and add a common name to this annotation.
By using this approach, different teams can be completely independent from each other because they can deploy and manage their own services and ingresses while relying on the ALB.