Configuring Haproxy over AWS using Ansible Playbook

Using EC2 Instances

Ranga Mani kumar
9 min readMar 25, 2021

Introduction :

Ansible

Ansible is a open-source tool provided by Redhat for automating configuration management, cloud provisioning, application deployment, intra-service orchestration and many more IT works. It was written by Michael DeHaan. The code of Ansible Modules mainly written in Python, Shell, Ruby. Ansible is a declarative language, we just have to tell what to do. It is agentless, connecting remotely via ssh to do its tasks.

Load Balancer

Load Balancer is a device which works as a Reverse Proxy and manages the traffic from the clients across the multiple servers. But What is Reverse Proxy ? It is a program which takes the request from the clients and sends it to the server on the behalf of client, the output from the server is sent to the reverse proxy and reverse proxy sends to the client.

HAProxy

HAProxy, High Availability Proxy, is a open source software which provides Load Balancer and Proxy server for TCP/HTTP based applications. It works on Round Robin Algorithm which sends the request to backend servers turn by turn and distributes equally.

Task :

We have to configure AWS EC2 instances with Load balancer(HA Proxy) and backend server (Httpd) using Ansible Playbook.

Step-1 : For doing this we need atleast 3 AWS EC2 instances. So let us launch 3 AWS EC2 instances for 1 Load balancer and 2 httpd backend servers using Ansible Playbook only.

Launching EC2 instances using Ansible Playbook :

First, we have to setup our environment and install some packages using pip3. If you don’t have pip3, you can install using this command

yum install python3-pip

After installing pip3, we use pip3 to install some python modules called boto and boto3.

pip3 install boto boto3

Here, we have one module called ec2 in Ansible. By using this we can install whatever the resources regarding to ec2, we can install.

You can refer this official docs of ansible for further information

Now, I have to install 2 instances for backend server (httpd). I write all code in my playbook under tasks.

Before we write the code, we need to give values to the parameters. It’s better to create seperate file for variables. We create variables for all needed parameters and assigning values in the variables file. We assign variable values to the parameters in playbook using jinja syntax. We get all this values for required paramaters from your AWS account.

Here I gave tags to Instances with key= Name, Value=loadbalancer (for haproxy) and Key=Name, Value=webserver

AMI_ID_web: "ami-052c08d70def0ac62"
key_name_web: "ansiblekey"
Instance_Name_web: "webserver"
Instance_count_web: 1
VPC_ID_web: "subnet-c40108ac"
Region_web: "ap-south-1"
sg_name_web: "webserver"


AMI_ID_lb: "ami-052c08d70def0ac62"
key_name_lb: "ansiblekey"
VPC_ID_lb: "subnet-c40108ac"
Region_lb: "ap-south-1"
Instance_Name_lb: "loadbalancer"
Instance_count_lb: 1
sg_name_lb: "loadbalancer_sg"

Now assign all this values in our main playbook using jinja syntax. We have included the variable file named vars_lb_web.yml under vars_files and IAM user credentials in as.yml file. Here as.yml is secret file that means encrypted file. User credentials are sensitive, so we need to encrypt this file ansible-vault. Here I encrypted the existing file as.yml

ansible-vault encrypt as.yml

It asks to enter password, enter it and whenever you run the playbook using the vault file for values of variables you need to give argument ask-vault-password. You have to give password everytime you run the playbook. You can understand by seeing demo pictures at last.

- hosts: localhost
vars_files:
- as.yml
- vars_lb_web.yml
tasks:
- name: Launching Instances for webserver
ec2:
key_name: "{{ key_name_web }}"
instance_type: "t2.micro"
image: "{{ AMI_ID_web }}"
wait: yes
count_tag:
Name: "{{ Instance_Name_web }}"
exact_count: "{{ Instance_count_web }}"
instance_tags:
Name: "{{ Instance_Name_web }}"
vpc_subnet_id: "{{ VPC_ID_web }}"
assign_public_ip: yes
region: ap-south-1
group: "{{ sg_name_web }}"
aws_access_key: "{{ access_key }}"
aws_secret_key: "{{ secret_key }}"

- name: Launching instance for load balancer
ec2:
key_name: "{{ key_name_lb }}"
instance_type: "t2.micro"
image: "{{ AMI_ID_lb }}"
wait: yes
count_tag:
Name: "{{ Instance_Name_lb }}"
exact_count: "{{ Instance_count_lb }}"
instance_tags:
Name: "{{ Instance_Name_lb }}"
vpc_subnet_id: "{{ VPC_ID_lb }}"
assign_public_ip: yes
region: "{{ Region_lb }}"
group: "{{ sg_name_lb }}"
aws_access_key: "{{ access_key }}"
aws_secret_key: "{{ secret_key }}"


- name: "Waiting for Instances to Launch "
pause:
seconds: 100
- name: " Refreshing inventory to load the new instances IPs"
meta: "refresh_inventory"

Here I paused the playbook for 100 seconds after launching instances for letting them complete launching and I refresh inventory. Here we are going to use dynamic inventory.

So after launching instances, we need to configure them as load balancer and webserver with playbook. So, we need to provide public IP of instances. But public IP would not be same for everytime, they change. So it is bad to update public IP manually in inventory file for every boot or launching new instance. This process is static. So we need some program which dynamically retreive our instances public IP based on tags. Here comes Dynamic Inventory.

Step-2 :

Dynamic Inventory :

This is the inventory which dynamically retreive IP of instances and we can use it in our playbook by just giving tag under the hosts. So when we launch, it automatically retreive and update.

Let’s see how we can do that…

i) First, we create directory. Here I created directory called dynamic_inventory

mkdir /dynamic_inventory

ii) Now, we need to install one software called wget. This will download package from internet by giving link.

yum install wget -y

iii) Then, we need to download the inventory module files called ec2.py and ec2.ini in dynamic_inventory directory which we have created.

wget https://raw.githubusercontent.com/ansible/ansible/stable-2.9/contrib/inventory/ec2.pywget https://raw.githubusercontent.com/ansible/ansible/stable-2.9/contrib/inventory/ec2.ini

iv) We have to update the ec2.py with python3. At the first line give this

#!/usr/bin/python3

In this ec2.py file, go and comment 172 line.

v) We have to update the ec2.ini file with region and AWS credentials like access key id and secret key

vi) Now, we need to make the files executable.

chmod +x ec2.pyChmod +x ec2.ini

vii) We have to export this 3 details for environmental variables.

export AWS_REGION='XXXXXX'export AWS_ACCESS_KEY_ID='XXXXXX'export AWS_ACCESS_SECRET_KEY='XXXXXXXXXXXXX'

Now let’s update the ansible configuration about inventory and other details about privilage escalation and attaching key to enter into instances for further process as ansible follows ssh protocol. Go to default config file of ansible /etc/ansible/ansible.cfg file.

[defaults]
inventory=/dynamic_inventory/
host_key_checking=false
ask_pass=false
private_key_file=/ansiblekey.pem
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false

Here it uses .pem key file we need to make into readable mode.

chmod 400 ansiblekey.pem

We have done setup how to launch instances and dynamic inventory.

Step-3:

Configuring Haproxy and Httpd

Now the main part is how to configure loadbalancer(Haproxy) and backend server(webserver).

Lest’s do it…

Step-1 :

First, we have to install HAproxy software. We use package module of Ansible to install. Ansible can only tell to the yum to install the software. Yum is the command who goes to the location of software and install it.

  - name: "Installing haproxy"
package:
name: haproxy
state: present

Step-2 :

Then we have to configure HAproxy configuration file i.e., haproxy.cfg. In this file we have to provide port number to bind. Through this port only clients will connect. Then we have to provide backend IP Address, port number as shown in below fig. But in this dynamic world, the IP Address changes anytime and it is not good to do it manually. So we use Python Jinja2 convention and we write code for it and the IP Address will automatically updated according to the dynamic inventory file while running playbook. Using tag_Name_webserver. This will retreive all the IPs of with tag Name=webserver.

Here I have given port_number variable in jinja syntax as it automatically update the value of port_number variable. There are some ways to give value to the variable but here I have used vars_prompt and it prompts to enter value for port_number variable.

vars_prompt:
- name: port_number
private: no
prompt: "Enter the port number for load balancer"

We have to copy this Python Jinja2 convention configuration file with .j2 extension using template module to the Load Balancer.

- name: "Copying haproxy configuration file"
template:
src: /ws6/haproxy.cfg.j2
dest: /etc/haproxy/haproxy.cfg
notify: Restarting haproxy

Here we use notify to notify handler part to restart the service whenever there is a change in the configuration file.

Step-3 :

We have to make SELinux permissive using selinux module.

  - name: "Making SElinux permissive"
selinux:
policy: targeted
state: permissive

Step-4 :

All part is done, now we have to start the haproxy service using service module. It will start the service on port_number.

- name: "Starting haproxy service"
service:
name: haproxy
state: started
handlers:
- name: Restarting haproxy
service:
name: haproxy
state: restarted

Here I use handlers to run this block whenever notify notifies to this handlers. You can see in step-2 that I used notify. It will notifies to this handler part as configuration changes and it will restart the service.

Then our task of Configuring Load Balancer complete.

Configuring Backend Server with httpd :

Step-5 :

Now, We have to install the httpd software using package module in the same way as we did for HAproxy using Ansible.

tasks:
- name: "Installing httpd software"
package:
name:
- httpd
- php

Here, we are also installing php as we use php in our web page.

Step-6 :

Now, we have to copy the web page to the default root directory /var/www/html using template module. We can also create our own directory and customise it. But here Iam using default location of httpd.

- name: "Copying web page"
template:
src: "/ws6/m.php"
dest: "/var/www/html/m.php"

Here I copied m.php file

<pre>
<?php
print "<br/>";
print `/usr/sbin/ifconfig`;
?>
</pre>

Step-8 :

This is the last step where we have to start the httpd service using service module.

Now all process of configuration is done, Now we just need to run the all the playbooks. That all will launch the ec2 instances and configure and set up the HAProxy Load Balancer with Backend server.

Now see how it worked by seeing demo pictures.

It launched 2 aws ec2 instances with tags Name=webserver and Name=loadbalancer.

Here you should use argument -u ec2-user to do whatever you want to do in the instances.

I used -u ec2-user for logging into instance -b for activating Privilage escalation.

Here I entered port_number value as 8080.

Here I configured 2 systems as httpd backend servers.

As soon as they configured, we can connect but here we are using Load Balancer IP Address and port number 8080 to connect to the web server.

Here you can see I connected with my Load Balancer IP Address with port number 8080 and it is showing the IP Address of one of the backend webserver.

As soon as I refresh, you can see as it is showing the IP Address of my second webserver.

You can get this code from my github repo link

Thankyou…

Keep learning…

Keep sharing…

--

--