Playing With Ansible Using Ad-Hoc Commands

In previous posts we created a cluster of VMs and set up Ansible to manage the cluster. So we have a cluster of VMs ready to play with. Today we will explore Ansible with a bunch of ad hoc commands, which is the easiest way to get a flavour of cluster management.

Ansible

Before going any further it’s important to understand Ansible philosophy, because it drastically differentiates Ansible from similar tools.

In contrast to Puppet and other tools that automate execution of commands on remote systems, Ansible manages remote systems’ state. In Ansible you specify a state that remote systems have to get in and it’s up to Ansible to execute appropriate commands to ensure the state is achieved. Therefore, most of Ansible commands are idempotent and do nothing if a remote system is already in requested state.

State management is at heart of Ansible design, but sometimes it’s necessary just to execute a command on remote systems. You can do it with Ansible too, although it is not a recommended approach in general.

Ad-Hoc commands

Ad hoc commands are the easiest way to explore Ansible. They have the following structure:

ansible <pattern> -m <module> [-a <arguments>]

where:

  • <pattern> specifies which hosts the module should be applied to.
  • <module> tells Ansible which command (module) to execute.
  • <arguments> are optional module parameters.

In this post we will execute commands for all hosts. Feel free to experiment with Patterns if you want to play with a part of your cluster.

Cluster configuration

While setting up Ansible, we created a simple inventory file. The inventory has only one group named all, which includes all the VMs created. For examples to follow I shrank the inventory file (/etc/ansible/hosts) to bare minimum to minimise output:

server1 ansible_host=127.0.0.1 ansible_port=3022
server2 ansible_host=127.0.0.1 ansible_port=4022

[all]
server1
server2

Playing with cluster

It’s play time. Let’s start with harmless ping, then install some packages and finally restart the cluster.

Checking the cluster is up and running

First, let’s do something harmless. We will use ping module to check if all nodes in cluster are up and running:

ansible all -m ping

Output:

server2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
server1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Note that ping module does not require any arguments.

All the servers responded successfully, so our cluster is ready to do some work.

Installing Go

This blog is devoted to Go language, so let’s install golang package on all cluster nodes.

My VM nodes run Ubuntu, and I will use apt module for package installation. Remember that Ansible is state based, so I need to specify a desired state name=golang-go state=present as arguments to apt module.

To install packages we also require super user privilege. Ansible --become option does exactly that. Also we will use -K option to require manual password entry for privilege escalation.

ansible all -m apt -a 'name=golang-go state=present' --become -K

Truncated output:

SUDO password:
server2 | SUCCESS => {
    "cache_update_time": 0,
    "cache_updated": false,
    "changed": true,
    "stderr": "\rExtracting templates from packages: 69%\rExtracting templates from packages: 100%\n",
    "stdout": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nThe following additional packages will be installed:\n  binutils build-essential cpp cpp-5 dpkg-dev fakeroot g++ g++-5 gcc gcc-5\n  golang-1.6-go golang-1.6-race-detector-runtime golang-1.6-src\n  golang-race-detector-runtime golang-src libalgorithm-diff-perl\n  libalgorithm-diff-xs-perl libalgorithm-merge-perl libasan2 libatomic1\n  libc-dev-bin libc6-dev libcc1-0 libcilkr

...

server1 | SUCCESS => {
    "cache_update_time": 0,
    "cache_updated": false,
    "changed": true,
    "stderr": "\rExtracting templates from packages: 69%\rExtracting templates from packages: 100%\n",
    "stdout": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nThe following additional packages will be installed:\n  binutils build-essential cpp cpp-5 dpkg-dev fakeroot g++ g++-5 gcc gcc-5\n  golang-1.6-go golang-1.6-race-detector-runtime golang-1.6-src\n  golang-race-detector-runtime golang-src libalgorithm-diff-perl\n  libalgorithm-diff-xs-perl libalgorithm-merge-perl libasan2 libatomic1\n  libc-dev-bin libc6-dev libcc1-0 libcilkr

...

There are heaps of output, but from SUCCESS for each host we know that packages were installed.

If we execute the same command again, Ansible will do nothing, because it’s already in the desired state (the requested package is installed):

ansible all -m apt -a 'name=golang-go state=present' --become -K

Output:

SUDO password:
server1 | SUCCESS => {
    "cache_update_time": 0,
    "cache_updated": false,
    "changed": false
}
server2 | SUCCESS => {
    "cache_update_time": 0,
    "cache_updated": false,
    "changed": false
}

Note that value of changed property is false this time, signalling us that host was already in requested state.

Restarting the cluster

Let’s do something dangerous now and restart all nodes in our cluster with five minute delay:

ansible all -a "/sbin/shutdown -r +5" -u yury --become -K

Output:

SUDO password:
server2 | SUCCESS | rc=0 >>
Shutdown scheduled for Mon 2016-05-30 16:29:13 AEST, use 'shutdown -c' to cancel.

server1 | SUCCESS | rc=0 >>
Shutdown scheduled for Mon 2016-05-30 16:29:12 AEST, use 'shutdown -c' to cancel.

The output should be obvious for you now.

Note that we didn’t specify module. When module is omitted, the default command module is assumed.

The bottom line

VirtualBox and Ansible is all you need to create an environment for Big Data experiments on your PC. Ad-hoc commands is a great way to explore Ansible, but prefer playbooks for managing your cluster.