Blog

Deploying Elasticsearch to Amazon EC2 with Ansible

Over the past few weeks Ive been working with Elasticsearch. However, what I really have been missing is a good deployment system. Elasticsearch has a chef provisioning tutorial on their site, but I decided to check out another open source alternative, Ansible. Ansible claims to be “dead simple”. I have to agree with the statement. Compared to the custom language of Puppet and the quirks of Chef, Ansibles simple human readable syntax using yaml is breath of fresh air. Ansible also avoids some of the chicken-and-egg issues of deployment with puppet and chef by managing machines purely over SSH. I used the built in AWS module (one of many built in modules!), and created a convenient solution for creating an Elasticsearch cluster with a single command.

I pushed a simple example to Github as a quick bootstrap. This code is based on the Eucalyptus deploy sample in the Ansible example playbooks. These files will let you create a variable number of EC2 instances, and automatically provision them so that Elasticsearch is running and all your nodes are in the same cluster. I used Ubuntu 13.04 64 bit machines, specifically the AMI image ami-c30360aa. Other Ubuntu images should work as well, but your success might vary. The Git readme explains the details of the setup and what you have to do to deploy. The main goals are to setup an EC2 security group, setup your local environment, and customize some configuration files. In this post, Im going to walk through how everything works.

In Ansible, you list out tasks to be executed on a group of host machines in a play. Multiple plays can be combined into a single playbook, like a Chef recipe or Puppet manifest. In my playbook (deploy_elasticsearch.yml), I have two plays.

The first play stages the AWS instances. I want my local machine to send the commands, so I target the host group local. Ansible can collect information about machines, but we dont need any facts about our local machine, so the gather_facts flag is set to false. Ansible supports tags, which allow you to later call groups of tasks individually, so I included them for future use. In the first play, the first task launches instances, and register the outputs of that task to the variable ec2. The next task adds every single amazon instance that was created to the “deploy” group of hosts, so that we can reference them later. The commented task relating to attaching volumes is from the original example, and I kept that in case anyone wants to use that feature. Back in the first task, the “wait” parameter was set to yes. This waits to go to the next step until the amazon instance is running. However, “running” doesnt mean that the OS is fully booted, so we wait for SSH to be available to determine that the OS is fully booted. The last task in the play gives all the instances a few seconds of breathing room before jumping to the next step. This seems like a “hackish” solution, but was the best I could find after several hours of troubleshooting a bug with Ansibles apt module*.

The second play is targeted at the hosts group “deploy”, and provisions each of them with Elasticsearch. The tasks are fairly simple, installing JRE, downloading and installing Elasticsearch, installing Elasticsearch plugins. The last task copies over the Elasticsearch configuration file on our local machine to the remote one. At the very end, we see something new – a handler. Similar to Puppets state management systems, if any handlers are notified during the play, they will execute after the play, but if the handler is notified multiple times it will only execute once.

The biggest feature of Ansible that I havent used yet is roles. My playbook is great for many instances of a single type of server (which works well if you use Elasticsearchs default setting that automatically elects a master node). But what if we have multiple specific node configurations we wanted? Well, instead of making tons of playbooks that all say the same thing, we can make playbooks that include other playbooks when necessary, and set up a system where every node is configured with a common set of plays, and then specialized with a different playbook.

Ansible is a quick and surprisingly powerful deployment and provisioning tool. Anybody tired of Chef or overwhelmed with Puppet should definitely take a look at Ansible.

*The Ansible module for apt control is built on python-apt. I ran into issues with files not existing within 1 second of boot time that python-apt needs to function correctly.