Automating with Ansible - Part 1
October 22, 2016
Update 20161031: Changed compose-file to include container names
Automating tasks for installing new machines or managing your different environments is a must in the days of Continous Delivery. Most often I have seen these solutions done by chef or puppet. When I’ve worked in those projects many of the tasks/recipes has already been setup and whenever I’ve gone in and update those I’ve felt that it’s a bit of a jungle with a steep learning curve. I was therefore very interested when I got introduced to Ansible by a colleague.
What is Ansible?
Ansible is an IT automation tool to orchestrate, provision, deploy or install your infrastructure. Their goal is to be “simple” as in easy to use, yet very powerful in its features.
As an example, let’s say that you have an application that has a lot of prerequired steps to be installed on a fresh environment. In a delivery pipeline you would have to automate those preqrequired steps so that your pipe would continously setup a new version of your application each time. Another example could be that you have a lot of different environments for production and test. Whenever you update a configuration on the environments you need to do it on all existing environments, a tidious and boring task. In such a situation Ansible could help you with automating the provisioning of the updated configuration.
How does it work?
Ansible uses a control-machine, a machine where you’ve installed ansible to run your tasks towards all your target-hosts (e.g. production, test etc). When Ansible executes, it utilizes SSH towards the client to perform its tasks.
Ansible requires the control-machine to have python 2.6 or 2.7 installed and is currently not supported on Windows.
Okey, enough talk, hands-on!
If you want to try it out in a sandboxed environment I’ve prepared a few docker images. Check out the source code at https://github.com/olbpetersson/ansible-minimal-example.
Note: You will need to have docker and docker-compose installed.
Start the system
From the top of the repository, perform
docker-compose up. This will spin up a system containg of:
* An ansible controller - this is where we will perform our provisioning
* A dummy node - will be the target where we will execute our commands against
* Another dummy node - same as above
The compose file:
version: '2' services: controller: build: docker/ansible_controller container_name: a_controller tty: true volumes: - ./work:/home/ node1: build: docker/ansible_target container_name: a_node tty: true node2: build: docker/ansible_target container_name: another_node tty: true
Check the connection to your targets
To make sure that everything is correctly setup, execute
docker exec -ti a_controller bash. Now you should be in bash within the container
of the ansible controller.
Let’s see if we can reach our clients. Execute
ansible -m ping all. You should now hopefully see three success responses in
your shell, one from each machine. If we have a closer look at the command, we sent in
-m ping to use the predefined ansible module ping.
We also told ansible to perform the ping-action towards all hosts (see below).
Ansible comes with a lot of predefined modules which can help you with performing tasks at your target machines. To see a selection of them, visit http://docs.ansible.com/ansible/modules_by_category.html.
Defining our hosts
Ansible is working with an inventory file. This is a configuration file where we define our “machine-groups”. A “machine-group” could for example be all our databases. We could define these in the group [Databases] with the host names of our databases listed.
In our example, we have defined a_group which consist of the first target node (node1). We also have the group nodes as well as our controller. The controller is defined as a local connection since this is the machine from which we will execute our ansible commands.
[a_group] node1 [nodes] node1 node2 [controller] controller ansible_connection=local
Our inventory file defined in work/dev, referenced from work/ansible.cfg
If we look back on the ping-command we used above, we could now send in the host name as nodes (instead of all) to only ping the nodes-group, or node1 to only ping the node1 machine etc.
This was the first part in Ansible where we tried out Ansibles modules and inventory-file to define our system and ping different machines in our system. In an upcoming post we will examine how we can perform more ellaborate tasks using Ansibles Playbook concept.