In my previous post, Vagrant and Ansible (part1), I wrote about the Vagrant configuration. Let's now move on to the Ansible part.
Basic Ansible configuration
Ansible needs at least two configuration files, as you can see in the Vagrantfile:
- an inventory
- a playbook
The inventory
assigns machine IPs to groups, the playbook
configures the hosts or the groups.
Inventory file
This is our inventory file (provision/ansible/development
):
[development]
192.168.33.20
Quite simple. It just assigns our guest IP (192.168.33.20
) to a group we call development
.
Playbook
The playbook is where the fun begins. Here it is (provision/ansible/site.yml
):
{% raw %}
- hosts: all
sudo: yes
handlers:
- name: restart ssh
service: name=ssh state=restarted
- name: restart apache
service: name=apache2 state=restarted
tasks:
- name: Update Apt cache
apt: update_cache=yes
- name: Update Installed packages
apt: upgrade=yes
- name: Install Apache+PHP
apt: pkg={{ item }} state=latest
with_items:
- libapache2-mod-php5
- php5-mysql
- php5-sqlite
- php5-cli
- php5-json
- php5-gd
- php5-curl
- name: Disable unused apache modules
command: a2dismod autoindex cgi env
- name: Enable required apache modules
command: a2enmod rewrite
- name: copy apache vhost file for SITE1
template: src=guest/etc/apache2/sites-available/site_1
dest=/etc/apache2/sites-available/site_1
owner=root
group=root
mode=0644
- name: copy apache vhost file for SITE2
template: src=guest/etc/apache2/sites-available/site_2
dest=/etc/apache2/sites-available/site_2
owner=root
group=root
mode=0644
- name: enable apache2 site
command: /usr/sbin/a2ensite site_1 site_2
notify:
- restart apache
{% endraw %}
It's in YAML format. The hierarchy is defined by indentation level.
Let's split it to understand what it does (but it should be clear enough).
- hosts: all
sudo: yes
This begins defining what we want to configure (all of our hosts: just one!) and telling ansible it
needs to prepend sudo
to every command, as we don't have direct access as root to the target
guest.
handlers:
- name: restart ssh
service: name=ssh state=restarted
- name: restart apache
service: name=apache2 state=restarted
This defines a couple of “services”. Consider them like some kind of function you can call later to do something on the target. In our case we have two functions: one to restart ssh and one to restart apache.
tasks:
This row defines the beginning of the operations we want to execute on the target while provisioning it.
- name: Update Apt cache
apt: update_cache=yes
- name: Update Installed packages
apt: upgrade=yes
The first rule uses the apt
module included in ansible to
access apt-get
on the target and update its cache. Simply put: apt-get update
.
The name
row defines what appears on the screen while provisioning.
The second rule upgrades all the installed packages, even those in the base image.
{% raw %}
- name: Install Apache+PHP
apt: pkg={{ item }} state=latest
with_items:
- libapache2-mod-php5
- php5-mysql
- php5-sqlite
- php5-cli
- php5-json
- php5-gd
- php5-curl
{% endraw %}
This defines which packages to install on the target. It uses Ansible's apt
module to install the
packages, and the items cycle
to allow us to install all the packages in a single run, instead of
creating a configuration section for each one of them.
It also makes sure that each package gets updated if it's already installed (state=latest
) if we
provision the target again.
The packages list naturally includes the packages your application needs, and the dependencies are resolved as usual by Apt.
- name: Disable unused apache modules
command: a2dismod autoindex cgi env
- name: Enable required apache modules
command: a2enmod rewrite
Here we can see how to launch arbitrary commands on the target machine. We are using it to disable unused/dangerous apache modules and install the ones we need.
- name: copy apache vhost file for SITE1
template: src=guest/etc/apache2/sites-available/site_1
dest=/etc/apache2/sites-available/site_1
owner=root
group=root
mode=0644
- name: copy apache vhost file for SITE2
template: src=guest/etc/apache2/sites-available/site_2
dest=/etc/apache2/sites-available/site_2
owner=root
group=root
mode=0644
Ansible can copy files from your host machine to the target. We are using it to copy the Apache's
Virtualhost configuration files in the sites-available
directory and setting the correct
permissions.
Remember that our guest machine is a Ubuntu 12.04 Linux installation. If you are using a different
distribution, the dest
paths could change.
- name: enable apache2 site
command: /usr/sbin/a2ensite site_1 site_2
notify:
- restart apache
Finally we are using the command
feature again to enable the Virtualhosts, or make sure they are
enabled. We are also using the notify
check to call a function (see above) when the command
finishes, so that, in this case, we restart Apache after the Virtualhosts are enabled.
Apache Virtualhosts
The Apache Virtualhost configuration are no different from the standard. For example:
<VirtualHost *:80>
ServerAdmin root@example.com
ServerName site1.example.com
ServerAlias www.site1.example.com
DocumentRoot /var/www/sites/site_1/
<Directory /var/www/sites>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>
CustomLog ${APACHE_LOG_DIR}/site1-access.log combined
ErrorLog ${APACHE_LOG_DIR}/site1-error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
# supress php errors
php_flag display_startup_errors off
php_flag display_errors off
php_flag html_errors off
# enable PHP error logging
php_flag log_errors on
php_value error_log /var/log/site1-PHP-errors.log
</VirtualHost>
In our case, we are targeting a site inside the directory exported from our host to the guest by
Virtualbox (see Vagrantfile
: config.vm.synced_folder
).
One More Thing
This is the directory structure for our whole project. It could help understand it better:
~/Vagrant/Dev/
|- Vagrantfile
|- provision/
|- ansible/
|- development
|- site.yml
|- guest/
|- etc/
|- apache/
|- sites-available/
|- site_1
|- site_2
It's not mandatory. There is no imposed structure. You could flatten it or expand it as needed. For
example, you could have guest_development
, guest_staging
and guest_production
instead of just
guest
if you want to have different configurations for three servers. Or call them huey
, dewey
and louie
if you so prefer.
That's All, Folks!
This is a good start point to create one or more virtual machines to test your sites locally cloning the production server's configuration without the need to install a different OS on your developemnt machine.
But it can be used to create and deploy the staging or the production machine (or machines).
During the provision phase you could copy the application code in the correct directory, install MySQL, PostgreSQL, MongoDB, Redis, etc., and copy data from a dump or from another server.
Have fun!