Why use Ansible?

Before Ansible I knew three ways of how to (remotely) manage a server:

  1. Using shell scripts and SSH.
  2. MCollective
  3. Using a configuration management tool like Puppet or Chef.

While using the first with many servers quickly can become chaotic, the second and third options are just too heavyweight for my taste considering smaller ad-hoc installations.

That is where Ansible fits in perfectly: easy and fast to set up, and easy to scale if an installation grows.

In this post I will show how to set up Ansible and perform some tasks with it.

Preparing the Playground

Create two Vagrant boxes that have a fixed IP:

# -*- mode: ruby -*-
# vi: set ft=ruby:
 
VAGRANTFILE_API_VERSION = "2"
 
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
 config.vm.box = "centos_70"
 config.vm.define "foo" do |foo|
 foo.vm.network "private_network", ip: "192.168.10.10"
 end
 
 config.vm.define "bar" do |bar|
  bar.vm.network "private_network", ip: "192.168.10.20"
 end
end

I am using my own base box here, which I called “centos_70”. Feel free to roll your own. You can find a guide here.

Start both of them with vagrant up. You can access the Vagrant boxes individually with vagrant ssh foo and vagrant ssh bar respectively.

An SSH key-pair is needed because all Ansible communication goes via SSH.

ssh-keygen -t rsa
eval "$(ssh-agent -s)"
ssh-add /vagrant/id_rsa

Put the private key file into /home/vagrant/.ssh/id_rsa on foo, and on bar copy and paste the public key into /home/vagrant/.ssh/authorized_keys.

Install and Configure Ansible

Let’s deal with Ansible itself now.

Install it with:

cd /tmp
wget https://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
yum install epel-release-7-5.noarch.rpm
yum -y install ansible

On box “foo” edit the file /etc/ansible/hosts so it contains the IP address of box bar:

192.168.10.20

A first Test

Now, if everything is correct, you can try the following test on foo:

ansible 192.168.10.20 -m ping

The result should be something like:

192.168.10.20 | success >> {
 "changed": false, 
 "ping": "pong"
}

A real-world Example (more or less…)

Let’s create a fictitious deployment process that consists of the following two steps:

  1. Copy the “deployment artifact” from the deployment server foo to the deployment target bar.
  2. Execute a script on the deployment target bar.

My little deployment playbook deploy.yml looks like:

- hosts: 192.168.10.20
 tasks:
 - name: copies a file from a to b
 copy: src=/vagrant/hello.txt dest=/home/vagrant
 - name: executes a script
 command: /home/vagrant/hello_world.sh
 register: hello
 
 - debug: msg="{{hello.stdout}}"

The only thing that the script hello_world.sh does is:

#!/usr/bin/bash
printf "Hello World!\n"

The file hello.txt is just empty.

Let’s trigger the deployment on foo with:

ansible-playbook deploy.yml

The resulting output should be:

PLAY [192.168.10.20] ********************************************************** 
 
GATHERING FACTS *************************************************************** 
ok: [192.168.10.20]
 
TASK: [copies a file from a to b] ********************************************* 
ok: [192.168.10.20]
 
TASK: [executes a script] ***************************************************** 
changed: [192.168.10.20]
 
TASK: [debug msg="{{hello.stdout}}"] ****************************************** 
ok: [192.168.10.20] => {
 "msg": "Hello World!"
}
 
PLAY RECAP ******************************************************************** 
192.168.10.20   : ok=4 changed=1 unreachable=0 failed=0

Nice, very nice!

What’s next?

For those who already have a bunch of scripts and are logging into server after server, using Ansible is a revelation. Integrating those scripts with Ansible’s shell or command modules is a breeze.

After playing around for a while I realized there is one important page I visit all the time: the module index. This page provides a nice general intro about Ansible modules.

Done for today!