Introduction to Ansible
I’ve been using Ansible now for just under six months. I love it. I used to use Puppet religlously, but with a change of job, I needed something simple to setup, to work with a range of different servers, and be easy for others to learn.
Puppet is very powerful, and I was very happy with it. I still use it for some of my projects, because it just doesn’t make sense to switch them over to Ansible unless there is a specific reason that warrants rewriting the manifests.
You may think Puppet will win over people who manage multiple servers, and that maybe Ansible can’t handle this*. You’d be absolutely wrong to make that assumption. Ansible is known to be used in setups managing in the range of 20,000 nodes. Ansible does’t require a complex master-slave setup, as Puppet does, that takes in itself hours to setup. And it’s coped perfectly with any setup I’ve encountered so far.
Provide Ansible with a list of machines to provision, and the specifics and it’ll just go ahead and do it.
Lets dive in.
Note: I’m using Vagrant for this demo. Though, all of the Ansible specific parts are not dependent on it.
Also, at time of writing, I’m using Vagrant 1.3.5 and Ansible 1.3.4.
Install Ansible with:
sudo easy_install pip
sudo pip install -U ansible
Get a base Ubuntu machine up and running:
mkdir ansible-demo
cd ansible-demo
vagrant init precise64 http://files.vagrantup.com/precise64.box
We’ll want to give the machine a dedicated IP address that we can have Ansible connect to, so in your Vagrantfile
, ensure you have the following line:
config.vm.network :private_network, ip: "192.168.33.20"
config.vm.provision :ansible do |ansible|
ansible.playbook = "private/ansible/site.yml"
ansible.inventory_path = "private/ansible/development"
end
We’ve also told Vagrant that we want to use Ansible to provision our box.
Let’s create the directory structure for Ansible:
mkdir -p private/ansible
Now to tell Ansible about the machine we want it to provision. Add the following file private/ansible/development
and add the following:
[development]
192.168.33.20
This adds our new Vagrant machine to the development
group. We specify it’s IP address so Ansible knows how to connect to it.
Note: An inventory file isn’t actually required for Vagrant—it would create one automatically for us if we hadn’t—however, it’s a core part of Ansible, so worth using it with an explanation.
Now we’ll tell Ansible how to provision our Vagrant machine. Create the file private/ansible/site.yml
and inside it add:
---
- hosts: all
sudo: yes
tasks:
- name: Update Apt cache
apt: update_cache=yes
- name: Install Apache
apt: pkg=apache2 state=present
Lets get the machine up and running:
vagrant up
After a while, you should see some output like:
[default] Running provisioner: ansible...
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.33.20]
TASK: [Update Apt cache] ******************************************************
ok: [192.168.33.20]
TASK: [Install Apache] ********************************************************
changed: [192.168.33.20]
PLAY RECAP ********************************************************************
192.168.33.20 : ok=3 changed=1 unreachable=0 failed=0
If you now visit http://192.168.33.20 in your browser, you should see the amazing “It works!” Apache default host file.
If you hadn’t noticed already, site.yml
is written in YAML. To run through the site.yml
:
- hosts: all
This allows you to target specific groups for Anible to apply rules to. We’ve used the development
group in the private/ansible/development
file. You could also have webservers
, databases
, etc. For our introduction, we’ll just tell Ansible to target all hosts.
As we’re installing packages on Ubuntu, we need root
privileges, so we tell Ansible to use sudo
.
Next up we define a bunch of tasks to run. Tasks use Ansible modules to apply things to the machine. Each task you give a descriptive name which is used in the output when Ansible runs. Be as descriptive as possible.
We use the apt
module. First of all, we tell Apt to update it’s cache so that it can install new packages.
Then we get the apache2
package installed. We tell Ansible to ensure it’s present
so that on subsequent runs, it doesn’t need to try and install it again.
So… the Apache ‘It works!’ page isn’t that much fun. Lets get PHP up and running.
Create a file called test.php
in the directory private/ansible/files
with the contents:
<?php phpinfo();
At the bottom of site.yml
we’ll include some more tasks:
- name: Add test php file
copy: src=files/test.php dest=/var/www/test.php
- name: Install PHP
apt: pkg=libapache2-mod-php5 state=present
We have two new tasks here. The first will copy the local test.php
file to the remote machine into the specified destination. We then install the PHP module for Apache.
If we were to provision this now with Ansible, the packages would be installed and the file would be copied, however, we need to restart Apache so that it loads the PHP module.
We do this in Ansible with handlers.
At the bottom of site.yml
add the following:
handlers:
- name: Restart Apache
service: name=apache2 state=restarted
We then want to reference this handler when we install the PHP module. Update the PHP apt
task to be:
- name: Install PHP
apt: pkg=libapache2-mod-php5 state=present
notify: Restart Apache
We’ve added the notify
part to the task. This will tell Ansible to run the Restart Apache
handler when PHP is installed, which in turn will restart the Apache server making PHP become available.
At this stage it’s safe to run vagrant provision
.
Once this has run, you’ll be able to visit http://192.168.33.20/test.php and you should see a PHP info page.
That’s nice. But we’ll be a bit more exciting.
In your Vagrantfile
lets mount out Vagrant project to the virtual machine so that we can use files in our project. Add this below the :private_network
line:
config.vm.synced_folder ".", "/vagrant", :nfs => true
This mounts our Vagrant project directory, denoted by the period .
and on the virtual machine, mounts it to /vagrant
.
Create a new file called wordpress.conf
and put it inside the private/ansible/files
directory with the following contents:
<VirtualHost *:80>
DocumentRoot /vagrant/public
ServerName wordpress.dev
</VirtualHost>
Go to WordPress.org and download the latest version. Extract the files and move the wordpress
directory into your project and rename it to public
.
Now add the following to your site.yml
file below where we installed PHP:
- name: WordPress virtualhost
copy:
src: files/wordpress.conf
dest: /etc/apache2/sites-available/wordpress.conf
- name: Enable WordPress virtualhost
file:
src: /etc/apache2/sites-available/wordpress.conf
dest: /etc/apache2/sites-enabled/wordpress
state: link
notify: Restart Apache
- name: Disable default Apache virtualhost
file:
dest: /etc/apache2/sites-enabled/000-default
state: absent
- name: Install MySQL
apt: pkg=mysql-server state=present
- name: Install python-mysqldb
apt: pkg=python-mysqldb state=present
- name: Install PHP MySQL bindings
apt: pkg=php5-mysql state=present
notify: Restart Apache
- name: Setup WordPress database
mysql_db: name=wordpress state=present
It should be pretty clear what this does by just reading the name
titles for each task! However, I’ll explain two of them.
We disable the default Apache virtual host so that we don’t have to worry about setting up a hostname in our /etc/hosts
file. This is something you’ll more than likely do when you have a few different projects on your machine, but we’ll skip that for this demo.
We also install the python-mysqldb
package. This is installed so that Ansible can then create databases for us with the mysql_db
task. It’s an Ansible dependency that we have to include so that we get to use Ansible more.
Because we’ve changed the Vagrantfile
we can’t just do a vagrant provision
- we need to actually reboot the machine so that Vagrant can mount the /vagrant
directory share. Because we’re using NFS for this, it’ll need sudo
privileges, so you may be prompted for your password.
vagrant reload
Once the machine is running again, provision with Ansible:
vagrant provision
You can now visit http://192.168.33.20 and you should be preented with a WordPress installer. That’s it. Very little effort. You can use the default MySQL credentials to setup Wordpress:
- Host:
localhost
- User:
root
- Password: leave this blank
- Database:
wordpress
- this is what we specified in oursite.yml
file
Note: We’re not worrying about securing MySQL. This is just a development virtual machine, so we don’t have to worry about it. That’s why we use the root
account with no password.
That’s a very quick, brief introduction to Ansible. In my next post, I’ll talk more about reusable Ansible tasks - roles.
Some very helpful websites to have in your arsenal when working with Ansible:
- Ubuntu Packages for finding package names to use to install. Be sure to select
Precise
as your Ubuntu version which is Ubuntu 12.04 LTS - Ansible Modules documentation has all of the references for modules you can use
Update: Michael DeHaan—creator of Ansible—thought I should clarify my original sentence. Quite rightly so, as it could have been misinterpreted that I was suggesting Ansible couldn’t handle complex/large/master-slave setups. It can do all of them. The point I was trying to make is that it’s something Puppet users may think it can’t, and that they could make decisions not to use Ansible because of a misunderstanding.
@davidwinter "and that maybe Ansible can’t handle this" ... might wish to clarify we have users in the 10k-20k node range, etc :)
— Michael DeHaan (@laserllama) November 24, 2013