First time I heard of Ansible was at a customer site where they implemented it to perform automation on their linux and AIX systems.

I was not sold on other automation solutions like CHEF and Puppet. But Ansible win me over easily :)

So here I will deploy Vector, an performance monitoring solution opensourced by Netflix, with Ansible. I hope to show you some of the greatness of Ansible during this post.

Ansible installation

It’s one of the many good points with Ansible. It’s serverless and agentless. You only need to have python installed. Ansible will connect to client systems by ssh.

In this post, all commands will be performed on Ubuntu 14.04 vm built with Vagrant :)

The first step is to install pip the python package manager :

vagrant@vm2:~$ sudo aptitude install -y python-pip

After installation, we update it :

vagrant@vm2:~$ sudo pip install --upgrade pip
Downloading/unpacking pip from https://pypi.python.org/packages/py2.py3/p/pip/pip-6.1.1-py2.py3-none-any.whl#md5=172eb5abab25a5e0f7a7b63c7a49378d
  Downloading pip-6.1.1-py2.py3-none-any.whl (1.1MB): 1.1MB downloaded
Installing collected packages: pip
  Found existing installation: pip 1.5.4
    Uninstalling pip:
      Successfully uninstalled pip
Successfully installed pip
Cleaning up...

And finally we install Ansible :

vagrant@vm2:~$ sudo pip install ansible

And that’s all ! Pretty easy isn’t it ? :)

Setup ssh between systems

Ansible is based on modules. You can run Ad-Hoc commands but it’s not a best practice. Modules maintains(or try to) idempotent principle. So running the same action multiple time on an system should not affect it.

We will test the installation by using the module ping to ping another system.

First you need to generate ssh keys and copy them on the other system(here vm1) :

vagrant@vm2:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/vagrant/.ssh/id_rsa.
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.
The key fingerprint is:
3c:ce:e6:69:75:3d:9b:88:d6:67:c8:e0:40:80:d2:31 vagrant@vm2
The key's randomart image is:
+--[ RSA 2048]----+
|   .Eo           |
|  . o..          |
|   .   .         |
|       ..        |
|       .S    .   |
|       o..o . o  |
|        ++ * o + |
|       o..+ = =  |
|       .o.   o   |
+-----------------+
vagrant@vm2:~$ ssh-copy-id vm1
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
vagrant@vm1's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'vm1'"
and check to make sure that only the key(s) you wanted were added.

vagrant@vm2:~$ ssh vm1 hostname
vm1

So now we can test our installation.

Ansible work with a host file to list the systems it will interact with.

Here we create the most simple host file with only vm1 :

echo vm1 >> hostlist

And now we do our test :

vagrant@vm2:~$ ansible all -i hosts -m ping
vm1 | success >> {
    "changed": false,
    "ping": "pong"
}

If you see this result, congratulations Ansible is working :)

Ansible host file

From this point, I will use this host file for Ansible :

localhost ansible_connection=local
[testlab]
vm1
vm2

It’s adding the host localhost with the option ansible_connection=local to not use ssh to connect on it.

And i am creation a group of machines named testlab containing vm1 and vm2.

Here the ping(it’s a ssh connection test not a real ping) result :

╭─adejoux@feddy  ~/devel/vagrant/testlab 2.1.2
╰─$ ansible all -m ping
localhost | success >> {
    "changed": false,
    "ping": "pong"
}

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

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

Vector

Vector was opensourced by Netflix in April. It’s a web interface for PCP. I didn’t experiment much with it yet but it seems really interesting. It’s giving you a view of your system(cpu, memory, networks and disks) for the 2, 5 or 10 last minutes.

here a screenshot :

The nice thing is it’s distributed. You don’t need to install Vector on all your systems. Only one. The other systems only need PCP installed.

Here i check vm2 from vm1 :

Vector and PCP are evolving a lot. And it seems a lot of nice things are planned.

Installing PCP

Like said on the project website Performance Co-Pilot is a system performance and analysis framework.

Installation instructions are available NetFlix wiki.

Honestly it’s pretty straightforward and easy to install. So it’s a really good example for automation with Ansible.

When planning to perform multiples actions on an system with Ansible, it’s better to write a playbook file. It’s a file in YAML format. It’s a human friendly data serialization standard.

I am using ubuntu 14.04, so the first steps will be to add a new package repository because the PCP version provided with Ubuntu is too old for Vector.

Here the first entries in our playbook pcp.yml :

---
- hosts: testlab
  sudo: yes
  tasks:
    - name: Add key for pcp repository
      apt_key: url=https://bintray.com/user/downloadSubjectPublicKey?username=netflixoss state=present

    - name: Add pcp repository into sources list.
      apt_repository: repo='deb https://dl.bintray.com/netflixoss/ubuntu trusty main' state=present

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

    - name: Install pcp-webapi package
      apt: name=pcp-webapi state=present

I specify with hosts my target systems, here the testlab group.

I will use sudo to perform the actions.

It’s not mandatory to name your tasks but i find it more clear.

My first task is named Add key for pcp repository and use the module apt_key to add the public key of this repository.

The task named Add pcp repository into sources list use the module apt_repository add this repository in the sources list.

The task named Install pcp package use the apt module to update the cache and install the pcp package if it’s not installed on the system : the present value for state.

The task named Install pcp-webapi package use the apt module to install the pcp-webapi package if it’s not already installed.

I described every tasks but the text file is pretty self explanatory. YAML is easily readable. For people not familiar with YAML, indentation matters. It’s what allow the applications to retrieve data structures.

To perform the actions we will use the ansible-playbook command with the -i option to specify the hosts file.

╭─adejoux@feddy  ~/devel/ansible/ansible_vector ‹2.1.2›
╰─$ ansible-playbook -i ~/devel/ansible_hosts pcp.yml

PLAY [testlab] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [vm1]
ok: [vm2]

TASK: [Add key for pcp repository] ********************************************
changed: [vm1]
changed: [vm2]

TASK: [Add pcp repository into sources list.] *********************************
changed: [vm1]
changed: [vm2]

TASK: [Install pcp package] ***************************************************
changed: [vm1]
changed: [vm2]

TASK: [Install pcp-webapi package] ********************************************
changed: [vm1]
changed: [vm2]

PLAY RECAP ********************************************************************
vm1                        : ok=5    changed=4    unreachable=0    failed=0
vm2                        : ok=5    changed=4    unreachable=0    failed=0

Pretty neat report showing what was done and where.

A big advantage of module usage is we can run again the playbook without breaking anything :

╭─adejoux@feddy  ~/devel/ansible/ansible_vector ‹2.1.2›
╰─$ ansible-playbook -i ~/devel/ansible_hosts pcp.yml

PLAY [testlab] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [vm1]
ok: [vm2]

TASK: [Add key for pcp repository] ********************************************
ok: [vm1]
ok: [vm2]

TASK: [Add pcp repository into sources list.] *********************************
ok: [vm1]
ok: [vm2]

TASK: [Install pcp package] ***************************************************
ok: [vm1]
ok: [vm2]

TASK: [Install pcp-webapi package] ********************************************
ok: [vm1]
ok: [vm2]

PLAY RECAP ********************************************************************
vm1                        : ok=5    changed=0    unreachable=0    failed=0
vm2                        : ok=5    changed=0    unreachable=0    failed=0

Ansible didn’t modify anything on the target systems this time because everything was already set. It’s the idempotent concept at the heart of tools like Chef, Puppet and Salt.

Vector installation

Vector Get Started documentation shows multiple ways to install Vector. I choose the local one because it seemed to be the more tricky to me.

I made a new playbook for Vector installation :

---
- hosts: vm1
  sudo: yes
  tasks:
    - name: Install Node.js
      apt: name=nodejs state=present

    - name: Install git
      apt: name=git state=present

    - name: Install npm (Node.js package manager)
      apt: name=npm state=present

    - name: Install Bower globally
      npm: name=bower global=yes state=present

    - name: Install gulp globally
      npm: name=gulp global=yes state=present

    - name: Create website folder
      file: path=/www state=directory

    - name: Clone vector repository in /www
      git: repo=https://github.com/Netflix/vector.git dest=/www/vector version=stable

    - name : Symbolic link to node
      file: src=/usr/bin/nodejs dest=/usr/bin/node state=link

    - name: Install npm packages
      npm: path=/www/vector state=present

    - name: Change /www/vector ownership to vagrant
      file: path=/www/vector owner=vagrant group=vagrant recurse=yes

    - name: Install bower dependencies
      bower: path=/www/vector state=present
      sudo: no

    - name: Run Gulp build
      shell: gulp build >> gulp_build.txt chdir=/www/vector creates=gulp_build.txt
      sudo: no

    - name: Run gulp serve in background
      shell: echo "gulp serve"|at now chdir=/www/vector

I will split each task for explanation :

    - name: Install Node.js
      apt: name=nodejs state=present

    - name: Install git
      apt: name=git state=present

    - name: Install npm (Node.js package manager)
      apt: name=npm state=present

This 3 tasks will install git, Node.js and npm with the apt module. Nothing fancy here :)

    - name: Install Bower globally
      npm: name=bower global=yes state=present

    - name: Install gulp globally
      npm: name=gulp global=yes state=present

This tasks will use the npm module to install :

  • bower : It’s another package manager mainly used to install front-end development tools or framework(like bootstrap).

  • gulp : Here it will be used to provide our web server but it can do a lot of things. It’s a kind of task manager.

Here globally means at system level. You will find gulp and bower binaries in /usr/local/bin.

    - name: Create website folder
      file: path=/www state=directory

I pass quickly on the task “Create website folder” which use file module to create the /www directory.

    - name: Clone vector repository in /www
      git: repo=https://github.com/Netflix/vector.git dest=/www/vector version=stable

This task will use the git module to clone the git repository set by the repo parameter. A nice thing is you can directly what branch to checkout with the version parameter. Here i choose the “stable” branch.

    - name : Symbolic link to node
      file: src=/usr/bin/nodejs dest=/usr/bin/node state=link

This task use the file module to create a symbolic link named /usr/bin/node from /usr/bin/nodejs. it’s because the debian package renamed it nodejs but packages installed by npm often expect the node binary. Like here.

    - name: Install npm packages
      npm: path=/www/vector state=present

This task will install all npm modules defined in /www/vector/package.json. I didn’t specify the name parameter, so it will run “npm install” command. This command is expecting a package.json file listing the dependencies for the application. So you need to use the chdir parameter which specify where the command is run.

    - name: Change /www/vector ownership to vagrant
      file: path=/www/vector owner=vagrant group=vagrant recurse=yes

This task change files and directories ownership to vagrant. It’s needed by the next task. Not the recurse parameter allowing this task to work recursively.

    - name: Install bower dependencies
      bower: path=/www/vector state=present
      sudo: no

This task will install assets needed by the application. I was surprised but a bower module exist :)

bower should not be run like root. It’s here only to install assets. So I added “sudo: no”. It will make this task run under vagrant user.

    - name: Run Gulp build
      shell: gulp build >> gulp_build.txt chdir=/www/vector creates=gulp_build.txt
      sudo: no

This task will use the shell module to run gulp under vagrant user. No module was developed for gulp. To be idempotent, the creates parameter is used. It will create the file name in parameter the first time the task is executed. If the file exist, the shell command will not be run again.

    - name: Run gulp serve in background
      shell: echo "gulp serve"|at now chdir=/www/vector

The last task will run the gulp web server. The interesting part here is i can execute a real shell command with pipelining. I echo “gulp serve” to the “at now” command. Really handy.

The output when running the playbook :

╭─adejoux@feddy  ~/devel/ansible/ansible_vector ‹2.1.2›
╰─$ ansible-playbook -i ~/devel/ansible_hosts vector.yml

PLAY [vm1] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [vm1]

TASK: [Install Node.js] *******************************************************
changed: [vm1]

TASK: [Install npm (Node.js package manager)] *********************************
changed: [vm1]

TASK: [Install Bower globally] ************************************************
changed: [vm1]

TASK: [Install gulp globally] *************************************************
changed: [vm1]

TASK: [Create website folder] *************************************************
changed: [vm1]

TASK: [Install git] ***********************************************************
changed: [vm1]

TASK: [Clone vector repository in /www] ***************************************
changed: [vm1]

TASK: [Symbolic link to node] *************************************************
changed: [vm1]

TASK: [Install npm dependencies] **********************************************
changed: [vm1]

TASK: [Change /www/vector ownership to vagrant] *******************************
changed: [vm1]

TASK: [Install bower dependencies] ********************************************
changed: [vm1]

TASK: [Run Gulp build] ********************************************************
changed: [vm1]

TASK: [Run gulp serve in background] ******************************************
changed: [vm1]

PLAY RECAP ********************************************************************
vm1                        : ok=14   changed=13   unreachable=0    failed=0

Again it’s really easy to read. You know very well what is done. I like it :)

Next

I only showed the most basics capabilities of Ansible. It’s capable of a lot more. But I think it’s enough for a first post :)

Ansible is a really great product, try it. It’s easy to install :)