Sunday, April 5, 2015

Introduction to Ansible

Like Salt, Ansible is, at its core, an execution engine, allowing you to execute commands on one or more remote systems, concurrently if needed. It's now a full blown server configuration management and orchestration system. Orchestration is basically the ability to coordinate server availability for provisioning purposes. An example scenario would involve taking a server out of a load balancer array, applying your patches, rebooting the instance, and then adding it back to the array prior to deployment. Ansible also comes with the following features:

  • Free
  • Agentless
  • YAML-based configuration
  • SSH-based (SSH & Python must be installed on all servers)
  • Favors pushing configurations over SSH
  • Task management through "playbooks"

Prerequisites: Check out this guide on Vagrant.

It also helps to know a bit about SaltStack, although it isn't necessary. They are both ultimately moving towards the same end goal, which you'll hear more about as you proceed through this article.

Host Inventory

Now that we have a high level overview of Ansible, let's learn about the Host Inventory. This inventory is the list of hosts you can manage using Ansible. It's common to store this data in INI-like flat files, but you have the option to leverage a Dynamic Inventory through cloud providers like AWS EC2 or Rackspace. This inventory can be managed into groups and you also have the ability to set ports or override other connection settings. Take a look at the following example /etc/ansible/hosts file:



For the remainder of this tutorial, we're going to take a hands on approach to learning about the rest of the basic components.

Initial Setup

Let's start by creating some directories for our Vagrantfiles

# set up your directories
mkdir -p ~/vagrants/ansible
cd ~/vagrants/ansible

# initialize your Vagrantfile
vagrant init ubuntu/trusty64

You're going to want to replace the Vagrantfile with the following contents in order to set up a multi-machine environment:

# -*- mode: ruby -*-
# vi: set ft=ruby :


groups = {
  "host" => ["host"],
  "webservers" => ["webserver1", "webserver2"]

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config |
  config.vm.define "host" do |host| = "ubuntu/trusty64" "private_network", ip: ""
  config.vm.define "webserver1" do |webserver1| = "ubuntu/trusty64" "private_network", ip: ""
  config.vm.define "webserver2" do |webserver2| = "ubuntu/trusty64" "private_network", ip: ""

Now run vagrant up inside ~/vagrants/ansible directory. As a side note, having all of your machines on the same network means they can all ssh into each other. This is come in handly since Ansible is primarily SSH based.

Configuring Ansible on the Host Machine

vagrant ssh host

# install ansible
sudo apt-add-repository -y ppa:ansible/ansible
sudo apt-get update
sudo apt-get install -y ansible

# back up the hosts file
sudo mv /etc/ansible/hosts /etc/ansible/hosts.orig

Now add the following contents to the /etc/ansible/hosts file:



Adding Your SSH Keys

Follow this guide about generating SSH keys to allow communication between the ansible host and both of your webservers.

# concatenate the public key to all servers
cat ~/.ssh/ >> ~/.ssh/authorized_keys
cat ~/.ssh/ | ssh vagrant@ "cat >> ~/.ssh/authorized_keys"
cat ~/.ssh/ | ssh vagrant@ "cat >> ~/.ssh/authorized_keys"

Running Basic Commands

Let's start by pinging both of our web servers from the host VM that ansible is installed on.

ansible all -m ping | success >> {
    "changed": false,
    "ping": "pong"
} | success >> {
    "changed": false,
    "ping": "pong"
} | success >> {
    "changed": false,
    "ping": "pong"


Tasks are defined in Playbooks which run on a specified subset of the hosts. They can describe a command line steps or policies for your remote systems to enforce. The basic hierarchy of this data is comprised of a Playbook, plays, tasks and modules that execute all of the work.

# create a directory to store all of our files
mkdir ~/ansible && cd ~/ansible

# create an nginx playbook
sudo nano nginx.yml

# with the following contents:
- name: Set Up Webservers
  hosts: webserver
   - name: Install Nginx
     apt: pkg=nginx state=installed update_cache=true
      - Start Nginx

This is a standard yaml-formatted Playbook for provisioning Nginx on a server. SaltStack uses the same format to configure states, as well. You define the name, hosts, and tasks. Within each task, you define modules like apt, templates, and notify. Before we run this playbook, we're going to learn a few things about tasks, modules, and handlers.

Tasks and Modules

Tasks call Modules in order to alter server configurations. Changes are made idempotently, meaning you can run a task multiple times without it changing state. There is a huge ecosystem of modules that Ansible provides (200+) to help you with configuration. Before writing your own module, make sure to check the module index first.

Modules have the ability to install packages, run commands, mount drives, copy and template files, and manage services.


Handlers are additional tasks that run in response to triggers. They always run at the end of a play and are run only once, no matter how many times they've been triggered. The following handler will restart or relad Nginx after installation. So let's update our nginx.yml file with the following handler block:

- name: Set Up Webservers
  hosts: webserver
   - name: Install Nginx
     apt: pkg=nginx state=installed update_cache=true
      - Start Nginx

   - name: Start Nginx
     service: name=nginx state=started

Now, let's finally run our playbook.

ansible-playbook -s nginx.yml

And there you have it. Both of our webservers have now been configured with Nginx. Verify that nginx is running on these machines by sending an http request to each of them:

curl -i
curl -i

# hitting any of the servers should return the following result
HTTP/1.1 200 OK
Server: nginx/1.4.6 (Ubuntu)
Date: Sun, 05 Apr 2015 20:19:17 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 04 Mar 2014 11:46:45 GMT
Connection: keep-alive
ETag: "5315bd25-264"
Accept-Ranges: bytes

<!DOCTYPE html>
<title>Welcome to nginx!</title>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href=""></a>.<br/>
Commercial support is available at
<a href=""></a>.</p>

<p><em>Thank you for using nginx.</em></p>

To conclude this introductory tutorial, we'll lightly go over roles and other features. Check back for a follow up article.


Roles are a feature of Ansible that allow you organize your tasks, making it easier to delegate them to specific servers. Roles also allow you to keep your configuration DRY. Make sure to stop by Ansible Galaxy, a community for sharing roles. For fine grained access control and auditing, see Ansible Tower.

Variables, Templates & Facts

Variables allow you to change your configuration for different environments. Rather than rewrite your tasks, you have the ability to set and alter variables to change different configuration settings.

Templates allow you to copy configuration files and update certain sections using variables. In a follow up article, we're going to create a template file for our nginx configuration.

And finally, we have facts. Facts are information collected about each server in your inventory (e.g. IP address, memory, disk space, etc.) You can use Facts to help with server configuration or apply settings to templates.

No comments:

Post a Comment