Setup and Configure Haproxy with Ansible on Ubuntu 14.04

This tutorial will guide you through setting up haproxy on a fresh Ununtu box using Ansible.

We will follow the steps outlined in How To Use HAProxy to Set Up HTTP Load Balancing on an Ubuntu VPS. Except: instead of doing it manually, this time, we will do it with Ansible.

We will write a playbook that sets up and configures a HAProxy load balancer via a single ansible command.

Here goes:

Requirements

Currently Ansible can be run from any machine with Python 2.6 installed (Windows isn’t supported for the control machine).

This includes Red Hat, Debian, CentOS, OS X, any of the BSDs, and so on.

Ansible Installation

Install Ansible

On your local computer, run:

pip install ansible

That's pretty easy :). If you run ansible-playbook from the command you should now see some useful stuff.

Let's get started with the least amount of work necessary

  1. Create a file called playbook.yml
  2. Create a file called hosts

Things should now look like this:

|_ playbook.yml
|_ hosts

Add your target server to hosts

edit hosts file and simply paste in the IP of the server that you want to install haproxy on. For this example, we will use a server with the IP 1.2.3.4.

echo 1.2.3.4 > hosts

(replace 1.2.3.4 with your actual server IP (or domain name)).

Note: You can do a lot more stuff with inventory.

Installing haproxy

Now, paste the following into playbook.yml

- hosts: all

  tasks:
    - name: update apt cache
      apt: update_cache=yes cache_valid_time=3600

    - name: install haproxy
      apt: name=haproxy state=present

Pretty simple. This will update the apt-cache and install haproxy with apt-get

Let's run the playbook:

ansible-playbook playbook.yml -i hosts -u username

Note: if you are not running this as root, you will need to add the --sudo flag (possibly in conjunction with --ask-sudo-psss if relevant)

After that completes successfully haproxy is installed on your target box (the one you defined in your hosts file. That was easy :)

Nextup, We need to enable HAProxy to be started by the init script. We'll need to edit our /etc/default/haproxy file. We'll use the replace filter to do this.

Add the following task:

- name: Enable init script
  replace: dest='/etc/default/haproxy' 
         regexp='ENABLED=0'
         replace='ENABLED=1'

This will edit /etc/default/haproxy and replace ENABLED=0 with ENABLED=1.

You can run this now with the same command:

ansible-playbook -i hosts -u username

Notes:

  1. This will run our entire playbook. There's an important point here. Ansible is designed so that you can always run the entire script safely and be comfortable that the state of the server is accurately reflected in your playbook.
  2. For debugging purposes you can pass in the flag --start-at-task=.. which will start the process from the specified task. This can be quite a time-saver while we're testing. For example, we can run our new task with:

    ansible-playbook -i hosts -u username --start-at-task="Enable init script"
    

Configuring HAProxy

HAProxy's configuration lives at /etc/haproxy/haproxy.cfg we're going to use Ansible's template module to build our config file.

Create a templates directory:

mkdir templates

Create and edit templates/haproxy.cfg:

nano templates/haproxy.cfg

Paste the following config file in there:

global
  log 127.0.0.1 local0 notice
  maxconn 2000
  user haproxy
  group haproxy

defaults
  log     global
  mode    http
  option  httplog
  option  dontlognull
  retries 3
  option redispatch
  timeout connect  5000
  timeout client  10000
  timeout server  10000

listen {{haproxy_app_name}} 0.0.0.0:80
  mode {{haproxy_mode}}
  stats {{haproxy_enable_stats}}
  {% if haproxy_enable_stats == 'enable' %}
  stats uri /haproxy?stats
  stats realm Strictly\ Private
  {% for user in haproxy_stats_users %}
  stats auth {{user.username}}:{{user.password}}
  {% endfor %}
  {% endif %}
  balance {{haproxy_algorithm}}
  option httpclose
  option forwardfor
  {% for server in haproxy_backend_servers %}
  server {{server.name}} {{server.ip}}:{{server.port}} {{server.paramstring}}
  {% endfor %}    

You can learn more about what all these HAProxy configs do in the original tutorial

You'll notice we've got a bunch of things in curly braces (e.g.: {{haproxy_app_name}}. These are Ansible variables. Ansible uses the jinja2 templating engine. You can learn more about what you can do with Jinja2 templates on the Jinja website.

Since we're using variables in our template, we will need to define them somewhere. For simplicity, we will simply put them directly into our playbook for now. You can read more about variables in the Ansible docs on variables.

Add the following block to your playbook:

...
vars:
  haproxy_app_name: myapp
  haproxy_mode: http
  haproxy_enable_stats: enable 
  haproxy_algorithm: roundrobin
  haproxy_backend_servers:
    - {name: server1, ip: 10.133.181.247, port: 80, paramstring: cookie A check}
    - {name: server2, ip: 10.133.186.46, port: 80, paramstring: cookie A check}
  haproxy_stats_users:
    - {username: joe, password: soap}

...

You should substitute sensible values.

Finally, let's add our task to put this config file in place. Add the following task to your playbook.yml

- name: Update HAProxy config
  template: src=templates/haproxy.cfg 
        dest=/etc/haproxy/haproxy.cfg 
        backup=yes

Notes:

  • src tells ansible where to find our template file on the local computer.
  • desc tells Ansible where to find the file to replace on the remote server
  • We're going to make a backup of the file before we change it. backup=yes

Your playbook should now look something like this.

Let's run it:

ansible-playbook playbook.yml -u root -i hosts  --start-at-task="Update HAProxy config"

So now we've got a basic HAPProxy load balancer installed and configured. Let's just make sure that it's started. To do this, we'll add a handler.

In playbook.yml after our tasks block, add a handler block like so:

handlers:
- name: restart haproxy
  service: name=haproxy state=restarted

We will then need to notify our handler to be called at the appropriate time. Update the task "Update HAProxy config" so that it looks like this:

- name: Update HAProxy config
  template: src=templates/haproxy.cfg 
        dest=/etc/haproxy/haproxy.cfg 
        backup=yes
  notify: 
    - restart haproxy

Notice we hadded a notify block that will inform the task to make sure that haproxy is restarted in the case that we change the config

Run the playbook again for these changes to take effect.

Now, you should be able to navigate to your haproxy server and you will be forwarded to a different server each time you reload the page (this is because we're using the roundrobin algorithm).

Congratulations. You've just configured a HAProxy server with Ansible.

You can download the code for this tutorial from Github

Next steps:

  • Repackage this playbook as a role and post it to the Ansible Galaxy](https://galaxy.ansible.com/). Blog post coming soon.
  • Extend our role to also include ACLs which will allow us to handle forwarding multiple different domains to backend clusters. See: HAProxy - route by domain name

References: