Install WordPress with Ansible playbooks – Part 2


ha-wordpress-ansible

This is the second post in a series on how to install a scalable WordPress with Ansible. If you have missed the first article – please start with: How to install a scalable, production ready WordPress with Ansible – Part 1.

In the previous article, we talked about setting up the servers and getting them ready to install WordPress. In this post I will start installing the main components to run a highly available WordPress cluster.

As described before, the installation consists of a three tier infrastructure:

 

  • Load Balancer.
  • Application server(s).
  • Database server(s).
scalable wordpress architecture

scalable wordpress architecture

 

You can of course mix and match different components and tools to gain the desired output, but my choice of tools will be as following:

 

  • HAproxy will be installed on the load balancer, to bounce the requests between the backend pool of servers.
  • Nginx will be installed on every application backend server to proxy the request to hhvm php processor, and also php-fpm will be installed as a fallback.
  • MySQL will be installed as the backend datastore.

 

Also I will introduce the use of ansible-vault in this post to store different passwords safely, so let’s start right away. You can find the sources for this tutorial on https://github.com/slash4-de/install-wordpress-ansible

 

Load Balancer Setup

 

For the rest of this guide I will use a static hosts file (static inventory) to store my infrastructure information:

[apps]
app1

[dbs]
db1

[lbs]
lb1

 

The grouping of the servers is self explanatory, and it is very important because as you will see shortly I will use this grouping to run different tasks on different servers according to their role.

The following snippet will be added to the playbook.yml file to apply the haproxy role on the load balancer server only. Please note that I will not configure the haproxy server to distribute the requests between the app servers, this will be done in the next post.

# Load Balancer(s) roles
- hosts: lbs
  sudo: yes
  roles:
    - { role: haproxy, tags: ["haproxy"] }

HAproxy Role

haproxy is a very fast reliable load balancer that is suited for high traffic web sites, it can distribute requests on TCP or Application HTTP levels, the role will install haproxy 1.6 on the ubuntu 14.04 machine:

---
- name: Add haproxy (1.6) apt repository
  apt_repository: repo="ppa:vbernat/haproxy-1.6"

- name: Install haproxy package
  apt: name=haproxy update_cache=yes state=present

 

The role is very simple, it will add the apt repository for haproxy and then installs the package, also I will configure two handlers that will be used later after configuring haproxy:

---
- name: Restart haproxy
  service: name=haproxy state=restarted

- name: Reload haproxy
  service: name=haproxy state=reloaded

Application Server Setup

The next play will be to install the main components to run any PHP application on the application servers. As of WordPress Version 3.9, they have updated their code base to be compatible with HHVM 2.0, which means that we can take advantage of the super fast HipHop web server.

The play will install Nginx as front-end webserver, HHVM, and also PHP FPM as a fallback if HHVM fails, the following will be added to playbook.yml:

# Application Server(s) roles
- hosts: apps
  sudo: yes
  roles:
    - { role: nginx, tags: ["nginx"] }
    - { role: php5, tags: ["php5"] }
    - { role: hhvm, tags: ["hhvm"] }

Nginx Role

The nginx role will install and configure Nginx on the application server(s), the role has many variables that will be placed in the nginx.conf template, for example the following lines will be replaced by the variables in the defaults directory:

# event mod Configuration #
###########################
events {
    # 1024
    worker_connections  {{nginx_worker_connections }};
    # 32
    worker_aio_requests {{ nginx_worker_aio_requests }};
    # on
    accept_mutex {{ nginx_accept_mutex }};
}

 

Originally this role can be used in different scenarios including using passenger with ruby applications, but by default the passenger configuration is disabled. The following are the tasks defined by the nginx role:

---

- name: Include Gzip Configuration
  include_vars: gzip.yml
  when: nginx_gzip == "on"

- name: Include Passenger Configuration
  include_vars: passenger.yml
  when: nginx_passenger | bool

- name: Add Nginx Repository
  apt_repository: repo="ppa:nginx/{{nginx_version}}" state=present
  register: nginxrepo

- name: Install Nginx
  apt: pkg=nginx state=installed update_cache=true
  notify: Start Nginx

- name: Remove Default Site
  file: dest=/etc/nginx/sites-enabled/default state=absent
  when: nginx_delete_default | bool

- name: Add Nginx Configuration
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
  notify: Restart Nginx                                               

The role simply installs the hhvm application but it will not configure the servers – this will be discussed thoroughly in the next post. Now we will just make sure that every component is in place.

- name: Add apt key for hhvm
  apt_key: url=http://dl.hhvm.com/conf/hhvm.gpg.key state=present

- name: Add apt repo for hhvm
  apt_repository: state=present repo="deb http://dl.hhvm.com/ubuntu {{ ansible_distribution_release }} main"

- name: Install hhvm package
  apt: name=hhvm update_cache=yes state=present
  notify:
      - Restart hhvm

This role will install and configure the php fpm engine. I will be using geerlingguy’s role which installs and adjusts the main configuration for php and php-fpm, but it doesn’t configure the php pool to run the application.

The role also gives you the ability to compile php from source, and to interact with different web servers, but in our case we will use the pre built package version. Here are some variables that I will enable in this setup:

group_vars/apps.yml

# php5 configuration
php_memory_limit: "128M"
php_max_execution_time: "5"
php_upload_max_filesize: "10M"
php_apc_enabled_in_ini: false
php_opcache_enabled_in_ini: true
php_expose_php: "Off"
php_short_open_tag: true
php_enable_php_fpm: true
php_enable_webserver: false
php_conf_path: "/etc/php5/fpm"
php_packages:
  - php5
  - php5-cli
  - php-pear
  - php5-common
  - php5-curl
  - php5-dev
  - php5-gd
  - php5-gmp
  - php5-mcrypt
  - php5-mysql
  - php5-xmlrpc

As of PHP 5.5.0 opcache is bundled with the main version of PHP. opcache can enhance the performance for any php application … but make sure to not increase the memory limit for the PHP application to avoid memory leakage.

Database Server Setup

Finally, the last component of our setup is the backend datastore, which will be MySQL. We will also be using the MySQL role of geerlingguy, which is great and covers all the basics.

The role will be applied on the database servers defined in the static inventory that I have described before. The role will create a root password for MySQL – according to the mysql_root_password variable. To be able to store the password safely, I will use the ansible-vault tool that will encrypt the password files.

ansible-vault is a feature of ansible that keeps passwords and keys in encrypted files to be used later within an ansible-playbook.
To create an encrypted file for mysql root password, use the following command:

# ansible-vault create group_vars/mysql_passwords.yml
Vault password: 
Confirm Vault password: 

After entering the the vault password, you add the root password safely, and then add this file to the last play we will in this post:

# Database Server(s) roles
- hosts: dbs
  sudo: yes
  vars_files:
      - group_vars/mysql_passwords.yml
  roles:
    - { role: mysql, tags: ["mysql"] }

Finally to run the playbook you should use –ask-vault-pass with ansible-playbook command:

# ansible-playbook -s --ask-vault-pass -i hosts playbook.yml

 

Ok, so far for today. Stay tuned for the next article in this series where we will finish the installation of WordPress with ansible.