Configure Load Balancer and multiple dynamic web servers using Ansible
Hi all, in this blog post we are going to see one more use case of ansible to configure a load balancer and multiple web servers dynamically using AWS EC2 instances.
So before we start our practical, let's understand some technical terms first.
Load Balancer:- It is a server running to distribute or pass the client requests/load to all the available web servers so that there is lagging or downfall of services.
AWS EC2:- It is a service provided by Amazon Web Services to dynamically create virtual machines/OS with desired hardware, networking, security and other configurations.
Ansible:- It is a configuration management tool used to configure almost all devices and softwares in any kind of environment, may it be UNIX, LINUX, Windows, cloud, containers, network devices etc. Ansible is built on top of Python and is an agent-less tool running on Control Node facilitating configurations on Managed Nodes.
Web Server:- It is a kind of server where we usually run our websites/webpages. Some common examples are like, Apache WebServer, nginx, IIS web server etc.
So lets start our setup.
Prerequisites:-
- Need to have one controller node with Ansible installed. Check with ansible --version command
- Should have access to one aws cloud account with IAM configured.
Go to /etc/ansible/roles path and then we need to create 3 ansible roles,
- First to create aws ec2 instances.
- Second to install and configure load balancer. Here we will use haproxy software.
- Third to install and configure Apache web server.
Ansible command to create roles is:
ansible-galaxy init <role-name>
ansible-galaxy init ec2_instance
ansible-galaxy init lbserver
ansible-galaxy init webserver
Contents of each role will be some predefined directories defaults, files, handlers, meta, tasks, templates, tests, vars.
Now we will write tasks to create our ec2 instances. Open ec2_instance/tasks/main.yml file and copy below contents to it.
########################################################
##### Below file is required to login aws account #######
- include_vars: secure.yml
In some time we will generate one ansible vault file called secure.yml file to store our secret aws credentials which is used to login to our aws account.
#####################################################
###### Creating a security group for ec2 instance ###
- name: create a security group
ec2_group:
name: "{{ security_group }}"
description: "An ec2 group for ansible webserver"
region: "{{ region }}"
vpc_id: "vpc-f735d19c"
state: present
aws_access_key: "{{ myuser }}"
aws_secret_key: "{{ mypass }}"
rules:
- proto: tcp
from_port: 22
to_port: 22
cidr_ip: 0.0.0.0/0
rule_desc: "Allow ssh on port 22"
- proto: tcp
ports:
- 80
- 8080
cidr_ip: 0.0.0.0/0
rule_desc: "ansible-grp-80"
rules_egress:
- proto: all
cidr_ip: 0.0.0.0/0
register: grp_result
tags: create_group
All the words inside "{{ }}" are ansible variables written in jinja2 format which is a python template engine. We will define the values of these varibales below in some time.
################################################
#### Starting our ec2 instance on aws #########
- name: provision an ec2 instance
ec2:
key_name: "{{ keypair }}"
instance_type: "{{ instance_type }}"
image: "{{ image }}"
wait: yes
#wait_timeout: 600
instance_tags:
Name: "{{ instance_tag }}"
count: 1
vpc_subnet_id: "subnet-8b000de3"
assign_public_ip: yes
region: "{{ region }}"
state: present
# group_id: "sg-0416e7537feb95257"
group: "{{ security_group }}"
aws_access_key: "{{ myuser }}"
aws_secret_key: "{{ mypass }}"
register: ec22
when: grp_result.failed == false
tags: create_ec2
The value of "{{ myuser }}" and "{{ mypass }}" we will get from secure.yml that we have included in this file at top.
#############################################
##### Waiting to let ec2 instance comes up ###
- name: wait for SSH to come up
wait_for:
host: "{{ item.public_ip }}"
port: 22
state: started
with_items: "{{ ec22.instances }}"
when: ec22.failed == false
Finally wait_for module is required to let the ec2 instance come up properly.
We will create one ansible vault named secure.yml under path /etc/ansible/roles/ec2_instance/vars/ which will contain my aws account AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. For security we will attach a password to it
ansible-vault create --vault-id ec2@prompt secure.yml
New vault password (ec2):
Confirm new vault password (ec2):
myuser: "AWS_ACCESS_KEY_ID"
mypass: "AWS_SECRET_ACCESS_KEY"
Now open ec2_instance/vars/main.yml file and paste below code in it.
---
# vars file for ec2_instance
security_group: ansible_web_sg # This name can be any group name
keypair: "EC2 Tutorial" # This name should match to your Key pair generated from aws console
image: "ami-0ebc1ac48dfd14136" # This is the aws in-built image id. Can provide any valid image id
instance_type: "t2.micro" # This instance type comes with aws free tier
region: "ap-south-1" # Need to provide aws valid region code where we want to create out ec2 instance
instance_tag: "webserver" # Can be any unique name but it is required later
Now we will create tasks for load balancer server. Open /etc/ansible/roles/lbserver/tasks/main.yml file and copy below content
---
# tasks file for lbserver
###########################################
##### Insatlling load balancer software ###
- name: Install haproxy load balancer
package:
name: "haproxy"
state: present
#################################################
##### Configure LB to register new webservers ###
- name: Configure load balancer for multiple web servers
template:
src: "haproxy.cfg"
dest: "/etc/haproxy/haproxy.cfg"
notify: lb restart
##########################################
#### Start load balancer service ######
- name: start haproxy load balancer server
service:
name: "haproxy"
state: started
Here haproxy.cfg is the conf file for haproxy server. We have defined this file at /etc/ansible/roles/lbserver/templates/haproxy.cfg. Open this file and save below content
#---------------------------------------------------------------------
# Example configuration for a possible web application. See the
# full configuration options online.
#
# https://www.haproxy.org/download/1.8/doc/configuration.txt
#
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
# to have these messages end up in /var/log/haproxy.log you will
# need to:
#
# 1) configure syslog to accept network log events. This is done
# by adding the '-r' option to the SYSLOGD_OPTIONS in
# /etc/sysconfig/syslog
#
# 2) configure local2 events to go to the /var/log/haproxy.log
# file. A line like the following can be added to
# /etc/sysconfig/syslog
#
# local2.* /var/log/haproxy.log
#
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
# turn on stats unix socket
stats socket /var/lib/haproxy/stats
# utilize system-wide crypto-policies
ssl-default-bind-ciphers PROFILE=SYSTEM
ssl-default-server-ciphers PROFILE=SYSTEM
#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend main
bind *:8080
acl url_static path_beg -i /static /images /javascript /stylesheets
acl url_static path_end -i .jpg .gif .png .css .js
use_backend static if url_static
default_backend app
#---------------------------------------------------------------------
# static backend for serving up images, stylesheets and such
#---------------------------------------------------------------------
backend static
balance roundrobin
server static 127.0.0.1:4331 check
#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend app
balance roundrobin
{% for host in groups['tag_Name_webserver'] %}
server app1 {{ host }}:80 check
{% endfor %}
In this file we have mainly modified the listening port of haproxy server to 8080 and
frontend main
bind *:8080
we have added jinja2 syntax to dynamically register websever IPs as
backend app
balance roundrobin
{% for host in groups['tag_Name_webserver'] %}
server app1 {{ host }}:80 check
{% endfor %}
In /etc/ansible/roles/lbserver/tasks/main.yml file, we have referenced one handler using notify directive and the implementation for that handler needs to be defined in /etc/ansible/roles/lbserver/handlers/main.yml file as
---
# handlers file for lbserver
###################################################################
## Whenever changes happen in haproxy conf file, restart server ####
- name: lb restart
service:
name: "haproxy"
state: restarted
Its time to create ansible role for webserver. Open /etc/ansible/roles/webserver/tasks/main.yml file and copy below content
###############################################
#### Installing apache webs server software ###
- name: Install httpd web server
package:
name: "httpd"
state: present
To test our load balancer we will create a webpage which will print the host (in our case ec2 instance) private IP. So every time we refresh webpage we can see different IP will be printed in round robin fashion.
###############################################
### Configure webserver to show new webpage ###
- name: Configure httpd web server
copy:
content: "Hi from {{ ansible_hostname }}"
dest: /var/www/html/index.html
Then we will start our web server.
############################
### Start the web server ###
- name: Start httpd web server
service:
name: "httpd"
state: started
~
So we have created all the required 3 roles. Now we can create our playbooks which actually use these roles to create new EC2 instances, install haproxy (Load Balancer) and webserver on them.
Create another directory named /root/dynamicinventory/ and created 2 files
- ec2_instance.yml -- to provision new EC2 instances
- lb_ec2_setup.yml -- to install and start load balancer & web servers.
We also need to copy two files ec2.py and ec2.ini files from github path
Download the files and make ec2.py executable using
chmod 755 ec2.py
ec2.py will list out all the running ec2 instances. This script required three env variables to set.
export AWS_ACCESS_KEY_ID='your-aws-key'
export AWS_SECRET_ACCESS_KEY='your-aws-secret-key'
export AWS_REGION='ap-south-1' # which region you want to start the ec2 instance
Open ec2_instance.yml and write below yml code
- name: create one ec2 instance
hosts: localhost
connection: local
gather_facts: false
tasks:
- include_role:
name: "ec2_instance"
vars:
region: "ap-south-1"
instance_tag: "lbserver" # valid values are'webserver' or 'lbserver'
- name: Refresh the ec2.py cache
command: python3 /root/ansibleWorks/mycode/dynamicInventory/inv/ec2.py --refresh-cache
- name: Refresh inventory
meta: refresh_inventory
Here we are asking ansible to run ec2_instance role in the localhost with two variables region & instance_tag. In next playbook, based on this instance_tag we will run different roles. The command module will refresh our dynamic inventory so that next playbook can access those dynaic IPs.
We then write lb_ec2_setup.yml playbook with below content
# Setting up load balancers and web server
- name: Configure webserver
hosts: tag_Name_webserver
become: yes
remote_user: ec2-user
roles:
- role: webserver
- name: Configure load balancer
hosts: tag_Name_lbserver
become: yes
remote_user: ec2-user
roles:
- role: lbserver
So first run ec2_instance.yml playbook to create a new EC2 instance which will be our load balancer using below command
ansible-playbook --vault-id prod@prompt ec2_instance.yml
Run the same playbook 3 times after modifying instance_tag: "webserver" to create 3 EC2 instances as webserver
ansible-playbook --vault-id prod@prompt ec2_instance.yml
We can verify in our aws console that two new ec2 instances are created with Name as lbserver & webserver.
We can also verify in localhost that all the EC2 instances are spawned with below command
Now its time to run our final playbook lb_ec2_setup.yml that will setup one load balancer and 3 webservers with below command.
ansible-playbook lb_ec2_setup.yml
So with this our setup is complete and now we can verify if our load balancer is working properly
Go to web browser and type the load balancer IP and port as 8080. In my case
As we refresh our web page, we can see different private IP showing in round robin fashion.
Hope you learnt something new and useful today.
Comments
Post a Comment