Ansible Deployment on AWS EC2 with Load Balancing and Auto-Scaling: Part One

Journey to the Hello World
6 min readJun 27, 2021

background:

Two years ago, I graduated from university and got my first job in tech industry. It is an online media company, who runs several websites and has millions of visits per day. My job’s nature is much like a DevOps instead of developer. During the first month of the probation, I was asked to set up a production env for our incoming website. The new website is a trivial LNMP project, which didn’t adopt any container tech. The requirements for the new prod env areas followed:

  1. All the running server must be in a private network so that they are not accessible from outside world.
  2. There should be a local balancer in front of the running servers.
  3. The servers should be able to scale up/down depends on the traffic automatically.

We choose the Ansible as our deployment tools and set up the env on AWS EC2. The remaining post documents the process of setting it up. I will choose a Node.js server as a sample.

prerequisite:

I assumed that you have basic knowledge of AWS EC2 and Ansible and are familiar with git. This post won’t go into the details of how to register an AWS account or set up Ansible on your local machine. You should have the following dependencies installed before continue:

  • Python ≥ 3.9.1
  • Ansible ≥2.9.0
  • boto3

ok, enough intro. let’s get in to it.

the architecture:

ansible deployment on AWS EC2

Quite simple, right ? It only includes load balancing and auto scaling group. All the running EC2 instances are in an auto scaling group behind the load balancer. Once the traffic surges, AWS Cloudwatch will inform the auto scaling group to grows based on the rules set by us. When a new EC2 instance is created, it will pull the source code from git. Once it is ready and pass the health check, it will be put into the target group, which service the request distributed by load balancer.

how to set up

The dummy project that we are going to deploy is accessible at https://github.com/xiaohanxu-git/nodejs-demo.

https://github.com/xiaohanxu-git/nodejs-demo

It bootstrap a node.js server that simply handle user’s request and returns a text like Hello World from balahbalah.

First, we are going to write an Ansible script, which can find all the instances in an auto scaling group and deploy our dummy project to them.

Ansible need a specific IP to execute commands on the target instance. However, the instance status in an auto scaling group is dynamic. We could not just hardcode the IPs of the current running instance in an inventory. God knows when they will be terminated and their IP are deallocated. It is good to have some method to query the IPs of the instance in real-time. Fortunately, someone already implemented a script for this purpose. we need ec2.ini and ec2.py script from https://github.com/ansible/ansible/tree/stable-2.9/contrib/inventory.

The ec2.py script assumes that it is being executed where the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables are set with proper permissions. In our use case, the AmazonEC2ReadOnlyAccess and AmazonElasticCacheReadOnlyAccess is all we need. You can either set the environment right before executing the script, or write a simple script that encapsulate the process.

ec2.sh

We create a helper script named ec2.sh and make ec2.sh and ec2.py executable by running chmod +x ec2.sh ec2.py Now if I run ./ec2.sh in the terminal I will get the following output:

ec2.sh outputs

Because we didn’t create any EC2 instance yet, the result won’t provide any info of the running instances.

Now we have a ansible project with the following content:

  • deploy.sh is the final script that we gonna run on each deployment.
  • deploy.yml is the ansible playbook that orchestrate all the tasks that are going to be executed on target servers.
  • roles contains individual module which will be arranged in deploy.yml
  • keys contains the ssh private key.
  • group_vars store all the env variables that our deployment gonna use, such as git repo url, source code version, etc.

Now we are going to make an AMI for our incoming EC2 instance. For those who not familiar with AMI,

An Amazon Machine Image (AMI) provides the information required to launch an instance. You must specify an AMI when you launch an instance. You can launch multiple instances from a single AMI when you need multiple instances with the same configuration. You can use different AMIs to launch instances when you need instances with different configurations.

So it pretty much like a blueprint that AWS use to provisioning an EC2 instance .The AMI need to contains all the dependencies that our nodejs-demo and ansible need.

We choose Ubuntu 20.04 instance type, launch a new instance and install Node.js, Nginx on the instance.

We also need to set up ansible on the instance so that a newly created instance can run the ansible script to pull the correct version of source code.

sudo apt update
sudo apt install python3-pip
git clone git@github.com:xiaohanxu-git/ansible-demo.git
cd ansible-demo
pip3 install -r requirements.txt

If you run the ./ec2.sh, you should be able to get some info on your running EC2 instances. Remember also git clone the nodejs-demo repo to the AMI.

git clone git@github.com:xiaohanxu-git/nodejs-demo.git

Now the setup is done. Let’s make the AMI !

Go to the AWS console -> find the EC2 instance that we just set up => right click => Select Images and Templates => Select Create Image => DONE :D

Now we have our AMI ready, it’s time to create our auto scaling group.

Basically, we are going to follow the procedure of AWS official document:

When we are baking our launch configuration, there are something need to pay attentions to:

  1. In the 5th step of creating a launch configuration, we should choose the AMI we just made. and t2.micro as it is in the free-tier.
  2. In the Step 7.d, for Advanced details, User data, we need to specify some user data or a configuration script to configure the instance during the launch. In our circumstances, the configuration script need handle two things: pull the latest code from ansible repo, and run the ansible script. This is the script that we are going to run on each instance during the setup:
#!/bin/bash
service nginx stop
cd /root/ansible-demo && git pull && ansible-playbook -i /root/ansible-demo/hosts ./startup_deploy.yml && touch /var/www/html/finish.html

Now we have our ansible script and launch config ready. It’s time to create the auto-scaling group and put that behind a decent load balancer !

I will demonstrate that in the coming part two.

--

--