Complete steps on building and deploying an application using docker-compose and AWS autoscaling

Reev Ranj
Data Fog
Published in
11 min readSep 21, 2020

Let me take you through the steps we took in packaging and deploying a web socket-based nodeJs application by integrating to a CI/CD pipeline and making it horizontally scalable.

This article shows the deployment using docker-compose and AWS EC2 Autoscaling. I will publish another article on doing this in Kubernetes (EKS) soon. So stay tuned and let's begin.

We have used the following tech stacks in the process:

  1. NodeJs is the programming language used in building the web socket-based application.
  2. Nginx as a reverse proxy to forward requests to the application port from port 80.
  3. AWS CodePipeline as the CI/CD pipeline.
  4. Github as the code repository.
  5. AWS EC2 i3-large as a server instance.
  6. AWS Elastic load balancer
  7. AWS auto-scaling groups for horizontal scaling.
  8. AWS CodeDeploy for automated deployments to the configured autoscaling groups
  9. AWS certificate manager for managing SSL certificates.
  10. Cloudflare for domain name registration.

Note: This article expects you to be familiar with the AWS console, SSH to the ec2 instance, some basic understanding of shell commands, understanding how a load balancer helps in routing traffic to the application instances, etc.

Following is the folder structure of the web-socket-application:

Let’s discuss the packaging of the application to a docker container image:

One of the most important but simple processes in the deployment of applications is the packaging.

We will create a docker file with the following code, which can be used to build a docker image with the latest code every time the deployment happens. ( Will discuss code deploy later in this article ).

And save it as ‘Dockerfile’ in the root of the application as shown the folder structure above

Now let’s write the docker-compose files for both development and production or staging environments :

We use the following code for the docker-compose.dev.yml in the root folder as shown in the folder structure above:

Note: You can see that in the above file, the command field is set to `nodemon .` . Nodemon is used here to have an auto restart of application every save making it easier while the development and debugging.

We use the following code for the docker-compose.yml in the root folder as shown in the folder structure above:

Now that we have our docker related files ready, we ready with configs to package our application.

Let's write the Nginx configuration file mentioned which will be accessed by the Nginx docker container from its mapped volume as mentioned in the above files.

The following code is saved as ‘app.conf’ at the following path: `./data/nginx/app.conf ` as specified in the folder structure:

This configuration file sets the Nginx to allow web socket connections at port 80. We will not configure SSL certificates here in Nginx, we will configure it at the load balancer level as a common entry point to the service.

We need to create the shell script files, which will be used by appspec.yml file in the root directory, that tells AWS CodeDeploy what to do while deploying the application to the machines. I will explain what it does in later parts of this article while discussing the CI/CD pipeline.

We will create the following files with their respective names in the scripts folder as mentioned above in the folder structure.

  • create a file: after-install.sh
  • create a file: before-install.sh
  • create a file: boot.sh

Now let’s create `appsppec.yml` file in the root directory of the application as shown in the folder structure above:

We have completed all the code based parts of the complete process. We are good to commit the whole code and push to github.

By this, we are going to move a little away from any code from now on.

We will be dealing with the AWS based services by doing the following tasks :

  • Create an image for EC2 instances with all the dependencies like docker, docker-compose pre-installed.
  • Create auto-scaling groups
  • Attach a load balancer to accept traffic to the application
  • Attach a domain URL to the load balancer to accept traffic from a good human-understandable URL.
  • Configure to get SSL certificates for the Domain URL we have created.
  • Once the scalable application is ready we will integrate an AWS CodePipeline with the auto-scaling group to auto-deploy any updates in the code to all the instances one by one.

Let's begin with the deployment, Go to AWS console:

Create a two IAM role:

  1. Create a role called ‘Code-deploy-role’, and add a policy to it called ‘AWSCodeDeployRole’.
  2. Create a role called ‘EC2-s3-access’ and add a policy to it called ‘AmazonS3ReadOnlyAccess

Create an EC2 instance Image (AMI) with all the dependencies like docker, docker-compose, CodeDeploy agent pre-installed :

  1. Go to the EC2 console.
  2. Launch an instance, with Ubuntu as OS and resource configuration of your choice. I have used i3.large.
  3. SSH into the instance
  4. Install docker and docker-compose.
  5. Login to docker hub and ECR
  6. Install AWS CLI 2.0 from https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html
  7. Do aws configure by running: `aws configure`
    - access key id : AXXXQXXXTCXVXXXXXXXXXXX
    -
    access secret: xxxxxxxxxxxxxxxxxxApxx4xxxxxxxxxxxx
  8. Run this command to login to ECR using docker cli :
    aws ecr get-login-password — region eu-west-2 | docker login — username AWS — password-stdin 0XXXXX2XXXX1.dkr.ecr.eu-west-2.amazonaws.com
  9. Install this package :
    sudo apt-get install pass gnupg2
  10. Do docker login using this command :
    - aws ecr get-login-password — region eu-west-2 | docker login — username AWS — password-stdin 0XXXXX2XXXX1.dkr.ecr.eu-west-2.amazonaws.com
  11. Clone the repository in the /home/ubuntu folder. (Can be as per your needs).
  12. Run the application and test it using curl to verify if the application is working in the localhost.
  13. We need to install the CodeDeploy agent (it is a small program that connects with the CodeDeploy application) inside the instance for the AWS CodeDeploy to work properly and integrate with the instances.
  14. For this, I would want you to follow the steps mentioned in the following official instructions page by AWS: https://docs.aws.amazon.com/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html
  15. Exit from the remote server instance.
  16. Go to the AWS console and select the newly created instance.
  17. Go to actions at top and go to the image and then create an image.
  18. Once the image is created, we terminate the instance.

This AMI will be used by auto-scaling groups to create instances.

Create an EC2 autoscaling groups launch configurations :

  1. In the same EC2 console page, in the left menu options, select Launch configurations under Autoscaling .
  2. Create a new launch configuration.
  3. Select the AMI we created previously and go to next step.
  4. Select the type of instance based on your needs, we select i3.large. (You may be charged more or less based on the type of resource you select)
  5. Go to the next step and give a name to the launch configuration and in the IAM role option select ‘EC2-s3-access’, which we created earlier.
  6. In the next steps, create a security group and note down the name of the security group.
  7. Review the inputs and create the auto-scaling launch configuration.

Create EC2 autoscaling groups:

  1. In the same console page, select auto-scaling groups.
  2. Click on ‘Create Auto Scaling Group’
  3. Click on launch configurations and select the launch configuration which we created previously.
  4. Give a name to the group and select the default VPC.
  5. Select one or more subnet addresses and go to the next steps. (keep a note of subnet addresses and VPC selected)
  6. Click on ‘Use scaling policies to adjust the capacity of this group’ and make suitable policies for the group to scale.
  7. Review the inputs and create the auto-scaling group.

Create an SSL certificate for a domain :

  1. Go to the AWS certificates manager.
  2. Enter the domain name and select any option to validate the ownership of the domain.
  3. Wait for sometime after completing the steps.

Create a Load balancer :

  1. In the EC2 console page, select load balancers
  2. Click on ‘Create a load balancer’
  3. Select the Application load balancer.
  4. On the next page, enter a name.
  5. Select the internet-facing scheme
  6. Add two listeners, HTTP and HTTPS.
  7. Now select the same VPC and subnets we selected earlier for autoscaling groups.
  8. In the next step, create a new security group and configure it to accept HTTP and HTTPS traffic. And note down the security group id.
  9. In the next page create a new Target group
  10. Provide a name
  11. Set type as instance
  12. Set protocol as HTTP at port 80
  13. Now click next and skip the ‘Register Target’
  14. Review the inputs and create a load balancer.
  15. Note down the DNS name of the newly created Load balancer.

Register Auto Scaling group to the target group as target:

  1. Go to the Autoscaling groups console page.
  2. Select the auto-scaling group created.
  3. Click on the actions at the top.
  4. From the options select edit.
  5. Enter the name of the target in the target groups field
  6. And Click save
  7. Wait for some time and follow the steps :
  8. Go to the targeting groups console page.
  9. Select the target group created earlier.
  10. At the bottom, click in targets.
  11. Verify the list of instance id(s) listed with the ones in the autoscaling groups

Configure auto-scaling groups security group:

  1. Get the id of the security group of the autoscaling group we created earlier.
  2. Go to Security Groups in the EC2 console page and go to inbound rules at the bottom.
  3. Set the inbound rules to accept requests at port 80 and select the source as custom and enter the security group id.

Verify the URL :

  1. Verify if the load balancer forwards requests to the application server by entering the DNS URL of the load balancer.

By this, we complete the deployment process. Now, let's move to automated deployments, where any new update in the code will be automatically deployed to the machines you created earlier.

We will do the followings tasks to achieve this :

  1. Create a CodeDeploy Application and create a deployment group in it which points to the autoscaling group we have created.
  2. Create a CodePipeline application, which we will integrate with the Github account and repository so that it listens to changes in the repository.
  3. Integrate the CodeDeploy application we created with the CodePipeline. So that any changes triggered from the GitHub account, the pipeline will pick up the new code do some processing, and deploy it in the instances of the autoscaling group

Let's begin with the above steps.

Create a CodeDeploy application :

  1. Go to the CodeDeploy console. Click on create an application.
  2. On the next page, enter a name and select EC2 as a computing platform.
  3. Once the application is created, click on the ‘create deployment group’.
  4. On the next page, enter a name to the deployment group and note it down.
  5. In the service role field, enter ‘Code-deploy-role’ which we created earlier.
  6. Choose deployment type as ‘In-place’.
  7. In the environment configuration, select auto-scaling groups.
  8. Once we select it, Enter the name of the autoscaling group you created earlier.
  9. In the deployment settings, choose ‘one at a time’
  10. Select ‘Enable load balancing’. This allows the pipeline to pause the load balancer from sending traffic to a particular instance until redeployment happens.
  11. Now choose the target group you created earlier.
  12. Click on the ‘Create deployment group’. A deployment group will be created

Create a CodePipeline application and integrate the CodeDeploy application with the pipeline :

  1. Go to CodePipeline console.
  2. Click on create Pipeline
  3. On the next page, Enter a name for the pipeline.
  4. Click on create a new service role and enter a name to the service role.
  5. On the next page, select GitHub as a source.
  6. Once you see more options, connect your GitHub account to the pipeline by logging in.
  7. Choose the repository you want to link with the pipeline.
  8. Enter the name of the branch you want to listen to the changes of.
  9. In the GitHub Change detection options, Choose GitHub webhooks if the GitHub account you logged is the admin of the repository, or if you are just a collaborator then choose ‘AWS pipeline’.
  10. On the next page skip the build stage and move to the next page. We will not be covering the build process in this article.
  11. In the deploy stage select a deployment destination, here, in this case, it is CodeDeploy.
  12. Once you see more options to enter, select the name of the CodeDeploy application we created earlier.
  13. And select the name of the deployment group we created inside the CodeDeploy application. And click next
  14. Review the inputs and click on create a pipeline.
  15. Once the pipeline is created, go to the CodePipeline console.
  16. Click on the pipeline you just created.
  17. In the next page click on the ‘Release changes’, to forcefully trigger a deployment to instances with the latest code in the Github repository.

Now that we have created a CodeDeploy application and CodePipeline. There is still one question, how do the pipeline and code deploy application know what to do in the instance ? where will the pipeline deploy the application inside the instance?

Answer to these questions are the few files we created earlier :

  1. appspec.yml: created at the root folder of the application.
  2. scripts inside the scripts folder in the root of the application :
  3. after-install.sh
  4. before-install.sh
  5. Boot.sh

These scripts are used by CodePipeline.

It checks this file, on what to do inside the ec2 instance. You can refer to the file at the beginning of this article.

CodePipeline has many stages like BeforeInstall, AfterInstall, ApplicationStart, etc. At these stages, we can do certain things in the instance. And for these stages, we have written the above scripts and mentioned the path to these scripts inside the appspec.yml.

Once you do all these steps mentioned above. Your application is completely ready to be scaled horizontally based on the autoscaling configurations you have specified, with a CI/CD pipeline integrated to have automated deployments of the latest commit in the repository.

This whole process can be a little time consuming, but once done you probably will not have to think about the deployment-related part until you have to make some changes the way it deploys, and you focus completely on the Application code.

--

--

Reev Ranj
Data Fog
Editor for

Software Engineer | Building software for startups