Lab 3 : How to deploy your apps

Objective: In this lab, I learned how to deploy applications using different orchestration methods. I explored server orchestration, virtual machine orchestration, container orchestration and serverless deployment in order to understand how applications can be deployed, updated and scaled. This lab really helped me compare and understand when to use these different approaches.


Part 1 : Server Orchestration with Ansible

The goal of this part is to orchestrate servers. This means :

  • Provision EC2 instances and configure them automatically
  • Deploy an application
  • Add a load balancer
  • Perform rolling updates

Step 1 : Set up the Ansible Environment

I created the necessary directory structure and I installed the required Ansible collections and configured AWS credentials for Ansible using the option 1 presented in the lab : aws configure.

Step 2 : Creating EC2 instances with Ansible

After I successfully ran the Ansible playbook to create the EC2 instance, I verified the instances in AWS management console :

EC2 instances created by Ansible

Instances running in console

I can confirm the three instances are running successfully.

Step 3 : Configuring Dynamic Inventory

(done)

Step 4 : Deploying the Sample Node.js Application

After I ran the application deployment playbook, I retrieved the public IPs and tested the application :

Public IPs retrieved

App running on all 3 instances

Here we can see that the Node.js sample application was successfully deployed and is running on all the three EC2 instances. This was verified by accessing each instance on port 8080. We can see each access allowed to receive the “Hello, World!” response which was expected.

Step 5 : Setting up Nginx as a Load Balancer

Like we saw in previous chapters, a load balancer is a system that distributes the incoming traffic evenly on multiple servers to ensure that one server doesn’t become overwhelmed. One benefit of this approach is that if one server fails, the load balancer can redirect the traffic to other servers. In this step we use Nginx as a load balancer.

After I ran the playbook to create the EC2 instance for Nginx and ran the Nginx playbook, I retrieved the Nginx public IP and tested the load balancer :

Nginx load balancer test

Nginx response

It seems like Nginx is listening on port 80 and not 8080 which can indeed be checked in the nginx.conf.j2 file :

server {
    listen                 80;
    listen                 [::]:80;
    location / {
        proxy_pass http://backend;
    }
}

I can also check directly in the EC2 dashboard that the instance was created and is running :

Nginx EC2 instance

Step 6 : Implementing Rolling Updates

What we did in this step is apply rolling updates to our sample application by using Ansible. By setting serial: 1 in the playbook, Ansible updates one EC2 instance at a time instead of updating all the instances at the same time. This approach is very interesting to make sure that the original version of the application remains available throughout the update process : while one instance is being updated, the other instances continue to serve traffic.

Rolling update in progress

Here we are continuously sending requests to the Nginx load balancer to continuously test the application. Because we updated the app to return “DevOps Base!” we can see that the app continuously shows this message.

Because we only have 3 instances the update was fast but normally, during the rolling update it is possible to see a mix of the old and new response as some instances are still running the old version while others already run the new one.


Part 2 : VM Orchestration with Packer and OpenTofu

Step 1 : Building a VM Image using Packer

Packer VM image build

Step 2 : Deploying the VM Image using OpenTofu

Correcting the path to :

github.com/asmaeoiskhine/devops_base//td3/scripts/tofu/modules/asg

Then I initialized and applied tofu. For that, I had to make some changes to the main.tf. I added a module alb with the obligatory expected parameters :

module "alb" {
  source = "github.com/asmaeoiskhine/devops_base//td3/scripts/tofu/modules/alb"
 
  name                  = "sample-app-alb"
  app_http_port         = 8080
  app_health_check_path = "/health"
 
  targets = module.asg.instance_ids
}

I also created a key_name variable in variable.tf and included it in main.tf using the ansible-ch3 key.

Step 3 : Deploying an ALB

ALB deployed

ALB result

Step 4 : Implementing Rolling Updates with ASG Instance Refresh

ASG instance refresh error

The error says that the max_healthy_percentage attribute is required. This attribute was indeed not specified in the code given by the lab paper so I added it to the code.

Result :

ASG refresh result

Finally, after I completed the other steps :

Final VM orchestration result


Part 3 : Container Orchestration with Docker and Kubernetes

The objective of this part is to understand the steps involved in container orchestration using Kubernetes on AWS. To do this we will :

  • deploy a Kubernetes cluster in AWS using Amazon EKS
  • push a Docker image to Amazon ECR
  • deploy a dockerized application to the EKS cluster
  • practice working with Kubernetes in a cloud environment

Step 1 : Building and running the Docker image locally

For this first step, I will run the Docker image locally. I set up the working directory, navigated to it and created the sample app and the Dockerfile to containerize the app. Then I built the Docker Image and ran the Docker container locally.

Docker image built

In a separate terminal (Powershell), I verified the app response by using curl :

curl response from Docker container

Step 2 : Deploying the application to local Kubernetes cluster

After I enabled Kubernetes through Docker Desktop settings, I verified it was indeed running :

Kubernetes running in Docker Desktop

Then I created the Kubernetes deployment configuration, applied it and verified the pods are running :

Kubernetes pods running

Then I created the Kubernetes service configuration, applied it and tested the application :

Kubernetes service test

Step 3 : Performing a Rolling Update

Kubernetes rolling update

Step 4 : Deploying a Kubernetes cluster in AWS using EKS

EKS cluster deployed

Step 5 : Pushing a Docker Image to Amazon ECR

5.1

ECR repository created

5.2

ECR login

registry_url = "270758478612.dkr.ecr.us-east-2.amazonaws.com/sample-app"

Step 6 : Tag and push the Docker Image to ECR

First we tag the Docker Image then we authenticate Docker to ECR :

Docker tag and ECR auth

Then we push the Docker Image to ECR :

Docker push to ECR

Step 7 : Deploying the sample application to the EKS Cluster

First I updated sample-app-deployment.yml to use the image from ECR :

Deployment YAML updated

Then I applied the Kubernetes manifests and verified the deployment :

Kubernetes deployment verified

I retrieved the external IP of the load balancer and tested the app :

external-ip of sample-app-loadbalancer:
a0d02e1bcfdf943ed80e47610048c180-676188662.us-east-2.elb.amazonaws.com

App running on EKS

Finally, I did the cleanup.


Part 4 : Serverless Orchestration with AWS Lambda

In this part, I explored serverless orchestration using AWS Lambda and API Gateway. The goal was to deploy a function that responds to HTTP requests and update it efficiently without managing servers.

I started by setting up the working directory, creating the lambda function code and the main OpenTofu configuration in main.tf.

Then I deployed the lambda function :

Lambda function deployed

After it was successfully deployed, I opened AWS Lambda console and confirmed that the function lambda-sample exists :

Lambda function in console

Then tested it manually using the test button :

Lambda manual test

As expected it shows the JSON response we defined in index.js.

In the 3 following steps, I configured the API Gateway to trigger the Lambda function which makes it accessible via HTTP. Using OpenTofu, I added an api-gateway module to the main.tf configuration. This module specifies the Lambda function ARN and defines a route (GET /) to handle the requests.

I also created an output.tf file to capture the API endpoint as an output variable. After applying the updated configuration with tofu apply, the API Gateway was deployed and linked to the Lambda function. Finally all that was left was to test the endpoint using curl and confirm the Lambda function responded correctly which it did :

Lambda curl test

Finally, I updated index.js to return a new message “DevOps Base!”, applied again and tested the API endpoint again :

Lambda updated message

The function returned the right message.