Ansible roles for configuring Apache Webserver and HAProxy LoadBalancer

Gursimar Singh
7 min readMar 30, 2021

--

Ansible roles are used to simplify Ansible playbook which means we can break a complex Ansible playbook in independent and reusable roles that are used to automatically load certain var_files, tasks, and handlers as per pre-defined file structure. We can call the roles in any Ansible playbook as it is reusable and independent of each other. We can also share the configuration template using roles easily. As mentioned roles need a standardized file structure that means at least one of the directories exists and must have a main.yml however we can remove other directories if not in use. The default location of roles is “/etc/ansible/roles”.

We have a command ‘ansible-galaxy init <ROLE_NAME>’ to create a role skeleton for us. It creates pre-defined directories structure and files as below:

Syntax:

$ ansible-galaxy init nginx_role

Explanation: In the above example, we have created a role called ‘nginx_role’ and used tree command to see the directories structure of the newly created role. Most of the directories have main.yml file that works as the entry point for correspondence directories of the role.

Functions of Ansible Roles

Let’s understand the functionality of each folder :

  • Tasks: We keep our tasks or the plays that will be performed by the roles however we can keep tasks in other folders separately as well for readability and better manageability and can be added to the roles using include if required. If this directory is being used in the role then it must contain main.yml.
  • Vars: It stores variables that are used within the roles. It has the highest level of precedence and we can only override by passing variables via the command line (CLI). It also has a main.yml file.
  • Defaults: It also stores the variables but default variables that mean it supposed to be changed while running the play however role is going to use the default variable if it is not defined or passed. It has the lowest level of precedence.
  • Handlers: It contains handlers that may be flagged to run using the notify keyword, notify keyword-only flags the handler if a task makes changes and handler will be triggered only once besides notified by multiple tasks. It is not only used by the same roles in which it defined but anywhere outside that role.
  • Files: The file directory contains static files that can be deployed via this role. It does not contain var files or templates as it is for simple ordinary files. We can reference the files within this directory without the path. It does not have the main.yml file in it.
  • Templates: It contains templates that we can deploy via this role. It does not have the main.yml file as well.
  • Meta: We can configure role dependencies and other configurations such as allow_duplicates etc.

What is HAproxy ?

HAProxy is a free, amazingly fast and reliable solution offering high availability, load balancing, and proxying for TCP and HTTP-based applications. It is particularly suited for extremely high traffic web sites and powers quite a number of the world’s most visited ones. Over the years it has become the de-facto standard opensource load balancer, is now shipped with most mainstream Linux distributions, and is often deployed by default in cloud platforms. Its mode of operation makes its integration into existing architectures very easy and riskless, while still offering the possibility not to expose fragile web servers to the internet.

Task Description :

Create an ansible role myapache to configure Httpd WebServer.

Create another ansible role myloadbalancer to configure HAProxy LB.

We need to combine both of these roles controlling webserver versions and solving challenge for host ip’s addition dynamically over each Managed Node in HAProxy.cfg file.

Let’s jump into the task:

Let’s create a folder and set the roles path in the ansible configuration file,

$ mkdir myroles$ vim /etc/ansible/ansible.cfg

role_path=</path>

Let us create two ansible roles:

$ ansible-galaxy init myapache$ ansible-galaxy init myloadbalancer

Let us configure the myapache role,

$ ls$ vim tasks/main.yml---
- name: "Installing apache server"
package:
name: "{{ p_name }}"
state: present
- name: "Copying the webpage"
copy:
src: files/index.html
dest: /var/www/html/index.html
register: x
- name: "Starting service"
service:
name: "{{ s_name }}"
state: started
when: x.changed

The variables used here are fetched from vars directory’s main.yml file:

$ vim vars/main.yml---
p_name: "httpd"
s_name: "httpd"

Let’s move on to ”myloadbalancer” role to configure haproxy,

$ cd myroles/$ ls$ cd myloadbalancer/$ vim tasks/main.yml
---
- name: "installing haproxy software"
package:
name: "{{ p_name }}"
state: present
- name: "setting up configuration file"
template:
src: "files/haproxy.cfg.j2"
dest: "/etc/haproxy/haproxy.cfg"
- name: "starting the firewalld services"
service:
name: "firewalld"
state: started
- name: "exposing the port of proxy"
firewalld:
port: "{{ port_no }}/tcp"
state: enabled
permanent: yes
immediate: yes
- name: " disabling selinux"
command: "setenforce 0"
- name: "starting service"
service:
name: "{{ s_name }}"
state: started
enabled: yes

The variables used here are fetched from vars directory’s main.yml file,

$ vim vars/main.yml---
p_name: "haproxy"
s_name: "haproxy"
port_no: 8080

Now, let’s configure the haproxy config file,

$ vim  files/haproxy.cfg.j2#---------------------------------------------------------------------
# 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 *:{{ port_no }}
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 ip in groups['webservers'] %}
server app {{ ip }}:80 check
{% endfor %}

Here we are using jinja2 embedded code for dynamically fetching the new webserver IP with HAproxy loadbalancer.

Now let’s jump ahead to testing the roles we just created,

We successfully completed the task. We can verify it by running the web page.

We can notice the IP is switching even though the IP address which we are using at browser is same. This proves that the HAproxy is working fine.

Now, let’s add one more IP address into the inventory file to check the handler added into the myloadbalancer role is working or not.

Let’s run the playbook again and check if the HAProxy server automatically adds the new target node in its configuration file or not.

Here, we can the tasks are changed for the new target node. Now, let us visit the HAproxy once again and check if it has recognized and updated its configuration file with the new target node or not.

From the output , we can see that IPs are switching, which means that the extra target node is also automatically added in the configuration file of HAproxy and new web page also copied into the target node.

GitHub URL: ARTH-Program/Ansible roles for configuring Apache Webserver and HAProxy LoadBalancer at main · gursimarh/ARTH-Program (github.com)

--

--

Gursimar Singh
Gursimar Singh

Written by Gursimar Singh

Google Developers Educator | Speaker | Consultant | Author @ freeCodeCamp | DevOps | Cloud Computing | Data Science and more

No responses yet