Name Last Update
inventory Loading commit data...
roles Loading commit data...
webapp Loading commit data...
.gitignore Loading commit data...
README.markdown Loading commit data...
Vagrantfile Loading commit data...
deploy_and_run_web_app.yml Loading commit data...
presentation.key Loading commit data...
presentation.pdf Loading commit data...


Utviklerlunsj: Ansible

The presentation is available as a keynote or pdf file.

How to install the demo environment

First, install Vagrant and VirtualBox on your Linux, Mac, or Windows box. Both installs are easy and fairly quick.

Then, clone this repository somewhere and, in a terminal, cd into the directory you cloned the repo into. There:

$ vagrant plugin install vagrant-hosts

You need to do that step only once. On my mac, it failed at first because it didn't find some Ruby gem, but when I just tried again a few minutes later, it worked. No idea whether that was just bad luck.

Then, to build and start all the demo VMs:

$ vagrant up

That will take a bit of time, certainly the first time you run this. When everything is up and running, you have 3 VirtualBox VMs running on your machine: "control", "target1", and "target2". You can ssh to either of them like this:

$ vagrant ssh NAME

The directory you've been in on the host OS (the one with the Vagrantfile) is mapped into the guest VM at /vagrant.

When you're done playing and want to ditch the VMs, do this:

$ vagrant destroy

For anything beyond this, consult the Vagrant documentation.


These are the things I demonstrate in the talk.

1. Install ansible on the control machine

$ vagrant ssh control
vagrant@demo-control:~$ sudo apt-get update
vagrant@demo-control:~$ sudo apt-get install -y ansible

Ubuntu 14.04's Ansible version is a bit outdated, but it's recent enough for us.

2. Run a few innocent ad-hoc commands ("SSH is all you need")

First, cd to /vagrant, which is the directory containing the cloned repository, mapped into the VM. Then

vagrant@demo-control:/vagrant$ ansible all -i inventory/demo -m ping

You need to say yes to adding target1's and target2's host keys now. This happens only the first time you connect to the hosts, as usual with SSH.

Run a few other commands to get a feel for what's happening.

vagrant@demo-control:/vagrant$ ansible all -i inventory/demo -m shell -a "free -h"
vagrant@demo-control:/vagrant$ ansible target1 -i inventory/demo -m shell -a "free -h"

Ignore the -m and -i things for now, we'll get to them later.

Here's one that spews out a lot of info about target1:

vagrant@demo-control:/vagrant$ ansible target1 -i inventory/demo -m setup

Since Ansible uses SSH under the hood, and you very likely require root privileges on your target systems at some point, it is important to be able to get root privileges in one way or another. If you don't specify anything to that effect, Ansible will just try to log in as "the current user":

vagrant@demo-control:/vagrant$ ansible target1 -i inventory/demo -m shell -a "whoami"

There are two ways to become root with Ansible: Either SSH as root to the target (-u root will do this), or log in as whichever user and use sudo on the target (--sudo). In both cases, you have to be able to do the respective thing without password. There are options you can specify that lets Ansible ask you for a password, but that is really not recommended.

On our Vagrant boxes, passwordless SSH for the root user is not set up, but the 'vagrant' user can sudo without a password. So let's use sudo:

vagrant@demo-control:/vagrant$ ansible target1 -i inventory/demo -m shell -a "whoami" --sudo

3. Describe the state you want a target to be in, not how to get there

Let's do something.

Say we want to have pip (a Python package manager) installed on one of the target machines. We tell Ansible that we want the apt package named "python-pip" present:

vagrant@demo-control:/vagrant$ ansible target1 --sudo -i inventory/demo -m apt -a "pkg=python-pip state=present"

Note that we didn't say "install python-pip", but rather "have python-pip installed". It is up to Ansible to figure out whether anything has to be done or not. Go on, just do the same thing again. All you get back now is "changed: false". So Ansible only does something if something needs to be done. That's why you specify what state you want in the end, not how to get there. That last bit is an implementation detail you usually don't concern yourself with!

If you want to break the rules, however, nothing stops you from running "apt-get install -y python-pip" with the "shell" module.

4. On second "abstractions" slide: make re-usable descriptions of small aspects of the system that you can combine in endless ways

We have a minimal web app in the webapp directory. We want to deploy this to two app servers (target1 and target2), and put those behind a simple load balancer on target1.

The playbook in deploy_and_run_web_app.yml does all of this.

The playbook contains two 'plays', i.e., sequences of tasks and role invocations that are applied to a set of hosts. In the first play, the playbook sets up the web app instances on all hosts in the 'app_servers' group (as defined in the inventory file). To do this on each host, we have a role 'runs_web_app'. In it, a sequence of tasks is (per convention) in tasks/main.yml. Files, templates and other role specific things can also be found in the role's directory. For this role, we just have one file in its 'files' subdirectory. Check out the Ansible documentation on roles for more details about what you can do with and inside roles. Roles are Ansible's main abstraction for making re-usable and shareable bits and pieces.

The second play of the playbook sets up the load balancer on all hosts in the 'load_balancer' group. In our inventory, that group contains only one host. For running a simple load balancer, we have the role 'runs_simple_load_balancer'. This role can be configured a bit through variables that are passed into it on invocation: you can specify which hosts and ports the app servers that should be load-balanced run on. There is one template in the role (the nginx config file snippet) that uses these variables. Variables can also be used nearly anywhere else outside of templates; think of most of the playbook text content as template-able content. Nearly everything you write is (or can be) passed through the jinja2 templating engine that comes bundled with Ansible. Check the Ansible documentation on variables and templating everywhere for more.

5. Play!

Run the playbook:

vagrant@demo-control:/vagrant$ ansible-playbook -i inventory/demo deploy_and_run_web_app.yml

Point your browser to the IP that is shown at the end of the playbook output. Refresh the page a couple of times to see the load balancer in action.

Now play around with the sources and playbooks and files as you wish. For example, change the inventory file so that you have only one host in the app_servers group. Run the playbook again. See the difference? It is that easy now to change your infrastructure and update your app's configuration.

Use Vagrant to your advantage while playing around. If you want to "wipe" target2, for example, just run "vagrant destroy target2" and then "vagrant up target2" on your machine (in the directory with our Vagrantfile). When you do this, target2 will get a new SSH host key. You will have to deal with that on the control VM. How to do this is left as an exercise to the reader ;) (approaches include disabling strict host key checking or removing the host key from /home/vagrant/.ssh/known_hosts).