LowEndBox

Hosting Websites on Bare Minimum VPS/Dedicated Servers

Getting started with ansible

Tags: Date/Time: May 18, 2013 @ 11:42 am, by Maarten Kossen

A common problem with managing multiple servers is keeping their configurations identical (or similar, depending on the purpose of the servers). When you set up a server (say server1), you tweak the configuration by hand and when you set up another server (server2) you do the same. But then there’s this setting you need to change on server2, which you then forget to change on server1. A different ecosystem was born.

What may also happen, is that you have to reinstall a server. Say, you’ve got your primary nameserver all set up and running for like a year, but then you want to (or have to) switch hosts. How on earth did you install that nameserver again? You probably need to start all over and google your way around. When switching hosts, this is not something you want to think about.

Enter ansible! Ansible is a tool which lets you perform actions on servers based on a definition called a playbook. You write a playbook (don’t worry, it’s not hard) where you define what needs to be done and you execute it with ansible. There’s no server-side software needed (well, except for python) and there’s hardly any configuration. By managing your servers with ansible you not only make sure their configuration is always identical and you always have the right tools (don’t you hate it when you go to a server, type ‘htop’ only to find out it’s not installed?). You also have your server configuration documented at all times.

So let’s get started and write our first playbook!

Note: because of the way WordPress works, some of my examples may have bad indentation and could have ansible throw errors. Make sure that if you copy-paste from this article, you fix indentation according to YAML standards.

Setting up ansible

To run ansible, you need Python 2.6 or higher on the machine you run ansible from. Most current operating systems have this by default. In addition to that, you need three Python modules:

  • paramiko
  • PyYAML
  • jinja2

The easiest way to install these is to use pip. Let’s first install pip for your distribution. This guide assumes you run Ubuntu (or some other Debian-based distribution) or CentOS.

Ubuntu

sudo apt-get install python-pip python-dev

CentOS

On CentOS, you need the EPEL Repository enabled for this to work:

yum install install python-pip python-devel

Now you’ve got pip installed, we can install ansible. We are going to install ansible from pip. pip has the most recent and stable version of ansible and is often more up-to-date than your OS repositories are. There are other ways to install ansible as well, which I’ll highlight in a bit. All dependencies will be pulled in by pip. This can also be done inside a virtualenv if you like (especially if you don’t want these dependencies to be in the way of other projects).

Ubuntu

sudo pip install ansible

CentOS

pip install ansible

There are other options to install ansible as well. Some distributions package it, you can run it directly from a git checkout or install it from a git checkout. There are also third-party repositories that have packaged versions of ansible and can you can even make a debian package from the git checkout. However, the pip way is the easiest way and in my opinion, the right way to install a python module.

Hosts definition

For ansible to work, you need a hosts definition. The default host definition is located in /etc/ansible/hosts. You can use a custom hosts definition (located outside /etc, for example) by defining them elsewhere and passing the -i [host-file location] parameter to the ansible-playbook command when running a playbook. We’re just going to work with /etc/ansible/hosts for now. Let’s open up the file and define out first hosts. If the file and/or directory don’t exist yet, you should create them:

(sudo) mkdir /etc/ansible

(sudo) touch /etc/ansible/hosts

Now let’s open up the empty hosts file and put the following contents in it:

[example]
192.0.2.101
192.0.2.102

What we’ve done here, is define a group named ‘example’. A group can be used in a playbook to run the playbook against a number of hosts. We’ve also added two hosts to the group. I’ve added them by IP address, but you can use hostnames as well. In your own host definition, replace these with actual the IP address(es) of your server(s). You can also defined just one host here and you don’t even need a group. It’s all up to you. However, for this guide, we’ll use a group with two hosts.

Now, there’s more that can be done with the hosts definition, like defining groups of groups, and even more can be done using groups. For simplicity’s sake, we’re just going to stick to this very basic version here.

Enough about installation, that’s not the most interesting part. Let’s write our first playbook!

Writing your first playbook

In ansible, a definition of how a server should look (or rather: how certain parts of a server should look) is called a playbook. In a playbook you define what actions ansible should take to get the server in the state you want. Only what you define gets done.

Playbooks are written in YAML. Read more about YAML here: http://en.wikipedia.org/wiki/YAML

We’re going to write a playbook that does the following:

  • Install apache2
  • Make sure index.html is present in /var/www/ and is owned by www-data
  • Restart apache

Note that this is just a very basic playbook, but it’s enough to get you started. For simplicity’s sake, we’re going to assume the target systems defined in our host definition run a Debian-based OS. Let’s start with setting up the playbook. It’s really easy, because this is it:

– hosts: all
user: root
sudo: no

Let’s go through this line-by-line.

– hosts: all

This line tells the playbook what hosts to run on. You can put a host or a group from your host definition here. I’ve used ‘all’ in the example, since we want the playbook to apply to all hosts in the definition we’ve just created (2 in total). You can also swap ‘all’ for ‘example’ in this case, as that’s the only group we’ve defined and that’s where all the hosts are in.

user: root

This line tells the playbook as which user to connect _and_ run the playbook. It’s possible to connect as user x and run the playbook as user y, but that’s something for the future. Right now, we’re assuming we’re connecting and executing the playbook as root.

sudo: no

We’re logging in as root, so we’re not using sudo. I’d always like to be verbose about this, since I have playbooks that use sudo as well. You can leave this out when using root as a user, though. If you change this to ‘yes’, your commands will be executed as the listed user but with sudo prefixes. If you use sudo, you need to pass the -K to the ansible-playbook command we’ll use later. Ansible will then ask you for your sudo password.

The next block is where the actual magic happens:

tasks:
– name: install apache2
action: apt pkg=apache2 state=latest

Basically, this is how all of ansible works. You have a name and an action. That’s the bare minimum for an ansible task (you can technically do without the name, but I don’t recommend it). Tasks are executed in the order in which they are defined. They are executed one-by-one against all hosts you run your playbook on.

Let’s dive into this.

tasks:

Indicates there’s a list of tasks coming. Simple as that.

– name: install apache2

This is the name of the task, or description. You can be as verbose as you want in here. This is what ansible will display when it executes the task.

action: apt pkg=apache2 state=latest

The action argument always starts with a module. A module is a part of ansible that executes your task. An action defines a module execution with the necessary parameters. In this case we’re using the apt module, since we’re running this against a Debian-based machine. The yum module works similar to the apt module, but package names may differ.

We’re passing two arguments to the apt module: pkg and state. The pkg argument tells the module which package we want to apply an action to. The state tells the module which state you want the package to be in. This can be latest, absent or present. Latest will update the package if a newer version is available, present will just make sure it’s there and absent will purge/remove the package if present.

So, summarized, the above tasks tells the apt module to install the latest version of the package apache2.

There’s a whole lot of modules, both official and contributed. I’m going to give two additional examples right now, so you get an idea how it works. We’re continuing the above tasks list, so we’re skipping ‘tasks:’ here.

– name: Make sure index.html is present for the default virtual host
action: copy src=files/index.html dest=/var/www/index.html

Right here, we’ve used to copy module to copy the index.html file to /var/www. The ‘files’ directory from src is a path relative to the playbook location. It may also be an absolute path. Copy copies the file to the destination only if there’s a difference. For the above to work, you need to create a ‘files’ folder and a file named ‘index.html’ in it.

– name: Make sure index.html is owned by www-data
action: file path=/var/www/index.html owner=www-data group=www-data

We’ve now used the file module to set the ownership of the index.html file we’ve just put in /var/www to www-data. Simple, isn’t it?

There’s one last thing to cover before we’ve completed out first playbook. Handlers. Handlers are similar to tasks in a sense that you can notify a handler. Let’s give you an example. Say we define the following handler (this can be done below the tasks block, ansible will make sure it knows about handlers when tasks are being executed):

handlers:
– name: start apache2
action: service name=apache2 state=started

This handler works just like a task: is uses the service module to make sure the service named ‘apache2’ is started. Now let’s use this handler in the above example where we install apache2:

– name: install apache2
action: apt pkg=apache2 state=latest
notify:
– start apache2

What we did here, is add the notify part. This tells ansible to execute the handler ‘start apache2’ after it has finished this task. In this case, it makes sure apache2 is started. So why would I use a handler if I can also use a tasks to restart apache2? Well, in our example, we could have just as well have used a task. However, if there are more tasks that require an apache2 restart, having one central handler for it prevents you from defining the restart multiple times.

So, we’ve just written our first complete playbook. Here it is:

– hosts: all
user: root
sudo: no
tasks:
– name: install apache2
action: apt pkg=apache2 state=latest
notify:
– start apache2
– name: Make sure index.html is present for the default virtual host
action: copy src=files/index.html dest=/var/www/index.html
– name: Make sure index.html is owned by www-data
action: file path=/var/www/index.html owner=www-data group=www-data
handlers:
– name: start apache2
action: service name=apache2 state=started

Save this as first-playbook.yml. Now let’s execute it!

Executing the playbook is easy. When you installed ansible, the ansible-playbook command became available to you. To run the playbook, all you need to do is:

ansible-playbook first-playbook.yml

This command will read the playbook, determine which hosts to run it against and as which user, and start executing your tasks. But I have assumed that you use a private key to log into the two servers we’ve listed before. However, that may not be the case. While I would recommend you to always log in with a private key, ansible also enables you to log in with a password. If we run:

ansible-playbook first-playbook.yml -k

It asks you for your SSH password for the user listed in the playbook.

Playbook executing looks as follows (I’ve cheated and I only ran my playbook against 127.0.0.1, so yours will look different):

Running your first playbook

You can clearly see what ansible does, step-by-step, and it gives a summary at the end! Ansible is verbose if something goes wrong. For example: on very minimal Ubuntu installations, the python-apt package may be missing. Ansible needs this on the target server for the apt module to work. It will tell you to install this package first.

Congratulations! You’ve just run your first playbook! Log in to your servers and see for yourself that everything is in order!

Final note

The above is just a very basic example of ansible usage. Your playbooks will usually be longer and/or more complex. There’s also a lot more modules to work with. Ansible has very good documentation. A good place to get started is here: http://ansible.cc/docs/gettingstarted.html. In the future, I’ll write additional guides on ansible, covering additional topics.

29 Comments

  1. 五杠小青年:

    不明觉厉

    May 18, 2013 @ 11:47 am | Reply
    • me too.

      May 19, 2013 @ 11:14 am | Reply
    • Maarten Kossen:

      Hi! Thanks for your response. However, Google Translate doesn’t help me much ;-) Would anybody help translate this for me, please? Thanks in advance!

      May 20, 2013 @ 1:36 pm | Reply
      • It’s short for
        虽不明,但觉厉。
        I don’t quite get it, but I think you are good (I think it’s useful).

        May 22, 2013 @ 2:35 am | Reply
        • Maarten Kossen:

          Ah, nice. Thanks!

          May 22, 2013 @ 1:34 pm | Reply
  2. neqste:

    This tool is good, but i like another way, it’s maby harder, but i sure for 100% about my configuration.

    1.) I do some change on the host, i notice this change to .sh script with command.
    2.) I add all commands for software installation, different passwords, etc things to bash script.

    If i change my server, i just copy my bash.sh to another host, and starting it. After sometime i get stable and secured (based by my own experience) server after ~ 15-20 minutes. When you configure your server, please notice all services, do not be lazy, few hours //commends, in code + commands in .sh = great result.

    May 18, 2013 @ 4:31 pm | Reply
    • Maarten Kossen:

      Thanks also a cool way to do it, especially with the Bash scripting. However, for larger deployments this becomes more of a hassle in my experience.

      Remember: there’s no tool that will suit everybody. No “one way”. Use what you’re comfortable with.

      May 20, 2013 @ 1:38 pm | Reply
  3. This is pretty interesting, thanks a bunch for writing the guide :)

    May 18, 2013 @ 4:36 pm | Reply
  4. changhefirst:

    How about chef or puppet?

    May 18, 2013 @ 9:25 pm | Reply
    • Maarten Kossen:

      Well, I don’t know much about Chef, since I haven’t used it. I have used puppet for about 9 months on a 40-50 server environment. Here’s what I think about Puppet:
      – It requires a daemon on every server it runs on, unless you find a good way to do it push-based. But even then, you still need to configure a lot. With Ansible, for me, there’s 0 configuration on the target machine besides ssh-copy-id (though that’s not even required).
      – Puppet has its own template language. With so many languages already available, why create a new one?
      – Puppet doesn’t follow the order of the puppet files. It just gets all the “commands”, tosses them on a big heap and then starts running in no order other than a completely random one. You need to be so insanely explicit about dependencies, it’s a real pane.
      – Bugs. There’s enough, but the one that made the puppet agent eat 1.5-2GB of ram on our servers, on a Sunday morning, without and reason to do so, was the real Puppet-killer for us.

      May 19, 2013 @ 6:44 am | Reply
      • changhefirst:

        Thank you very much.

        May 29, 2013 @ 5:19 pm | Reply
      • Very clear analytics!
        Last week I was considering using puppet, chef-solo or ansible. After a careful investigation, I decide to use the light tools, i.e ansible. But actually I still worry about it’s robustness compared with puppet or chef. Now I have complete my own playbooks, finding it’s convenient but not flexible enough.

        Hope it’ll be better in the future. :)

        July 24, 2013 @ 9:38 am | Reply
  5. Such a useful tool :)

    May 18, 2013 @ 11:29 pm | Reply
  6. W1V_Lee:

    Well reading this and a cup of coffee later and I feel a bit more educated, nice work.

    May 19, 2013 @ 12:19 am | Reply
    • Maarten Kossen:

      Thanks, W1V_Lee!

      May 19, 2013 @ 6:44 am | Reply
  7. How does this compare to the other solutions out there?

    May 19, 2013 @ 4:53 am | Reply
    • Maarten Kossen:

      My puppet comment above may be an interesting read ;-)

      I don’t have a solid comparison. I did quite some research before introducing puppet with my current employer and Puppet was like “the least evil” of the bunch, if you get what I mean. A colleague of mine stumbled upon ansible later. Ansible is still quite young and develops very fast, so I don’t think there’s too many proper comparisons out there.

      What I think ansible does better than the competition is:
      – It doesn’t require (much) server-side software
      – It’s light-weight, Open Source and free
      – It’s very easy to get started, because of the use of YAML and the way playbooks are structured
      – It runs the commands in the order they are in the playbook (or included in the playbook)

      May 19, 2013 @ 6:52 am | Reply
      • I’ve worked at places that have used Chef and Puppet but haven’t had direct interactions with it back then. Thanks for the explanation on Puppet.

        May 22, 2013 @ 2:08 pm | Reply
  8. root@ansible:/etc/ansible# ansible-playbook first.yml -i hosts
    ERROR: Syntax Error while loading YAML script, first.yml
    Note: The error may actually appear before this position: line 2, column 1
    
    - hosts: all
    user: root
    ^
    root@ansible:/etc/ansible#

    I tried but I get the above error, whats wrong? I checked the first.yml line by line and its the same YAML. Ubuntu 12.04 LTS

    July 27, 2013 @ 7:44 pm | Reply
    • Screenshot here: http://d.pr/i/zAIK

      July 27, 2013 @ 7:48 pm | Reply
      • Austin:

        I had the same issue. It’s a simple indentation problem. Line the ‘u’ in ‘user’ directly underneath the ‘h’ in ‘hosts.

        – hosts: all
        user: root

        August 8, 2013 @ 5:28 pm | Reply
      • Austin:

        My previous reply posted before I was done. And apparently the formatting doesn’t take anyway.

        Look at the first example on this link and follow that indentation: http://www.ansibleworks.com/docs/playbooks.html

        You’ll see everything lines up under the dashes (-).

        August 8, 2013 @ 5:32 pm | Reply
  9. Oded:

    I’m having the same problem as Asim Zeeshan mentioned.
    ERROR: Syntax Error while loading YAML script, first.yml
    Note: The error may actually appear before this position: line 2, column 1

    – hosts: all
    user: root
    ^

    any idea anyone ?

    August 1, 2013 @ 7:19 pm | Reply
    • Austin:

      See my comment to Asim Zeeshan above.

      August 8, 2013 @ 5:34 pm | Reply
  10. Andy:

    yum install install … ? A typo, I reckon. :-)

    August 6, 2013 @ 4:35 pm | Reply
  11. Michel de Greef:

    I have spent the last two weeks doing in-depth evaluations of Chef, Puppet, Ansible and SALT.

    For me, Ansible, came away a clear winner, principally for its comparative simplicity.

    If you want to speed up the original poster process, I would suggest that you use Vagrant as the front-end for orchestration and provisioning.

    Come to think of it, Vagrant, has also the ability to provision using scripts as well as Ansible. For what it is worth, there is a suggestion in the Vagrant site’s documentation that this may be a good way to learn Ansible.

    November 28, 2013 @ 11:52 pm | Reply
  12. I like this, I tried reading up the official ansible DOCS but they were too ‘technical’. This article should be made part of the official DOCS. I know the article is a bit out of date, for example, instead of using

    tasks:
    – name: install apache2
    action: apt pkg=apache2 state=latest 
    

    The latest version of ansible uses:

    tasks:
    – name: install apache2
    apt: name=apache2 state=latest
    

    ie, you no longer have to use the action keyword.

    Anyway, this article has made me see ansible for what it is. The official docs have a habit of complicating what is essentially a very simple thing.

    Kudos,
    Komu W.

    February 6, 2014 @ 2:27 am | Reply
  13. Varun Malhotra:

    Very well written. Thank you so much..Made me fall in love with Ansible. :)

    May 25, 2015 @ 10:28 am | Reply

Leave a Reply

Some notes on commenting on LowEndBox:

  • Do not use LowEndBox for support issues. Go to your hosting provider and issue a ticket there. Coming here saying "my VPS is down, what do I do?!" will only have your comments removed.
  • Akismet is used for spam detection. Quoting webhostingtalk.com URL seems to get binned consistently here, but I do peek into the spam box frequently to publish those comments.
  • Use <pre>...</pre> to quote the output from your terminal/console, or consider using a pastebin service.

Your email address will not be published. Required fields are marked *