Provisioning servers with Ansible
Here at Bluegg, we’re always trying to find new ways of optimising and streamlining our workflow. One of the repetitive and laborious tasks that we find ourselves doing every project is setting up web hosting for our clients.
This can involve downloading and installing the necessary software on the server, setting up the directory structure for the website/app and creating databases for those websites powered by a CMS.
Wouldn’t it be nice if we could achieve all this using one command from the terminal? Step in Ansible!
What is Ansible?
Ansible is a radically simple IT automation engine that automates cloud provisioning, configuration management, application deployment, intra-service orchestration, and many other IT needs.
As you can see, Ansible covers a wide range of services, however in this article I’ll just be concentrating on the provisioning functionality it offers.
In simple terms, 'provisioning' just means setting up a server ready to accommodate whatever websites/web apps you wish to put on it. Historically, whenever we’ve set up hosting for a client we would have to SSH into the new server and then trawl through a long list of server commands to get it set up how we needed it.
Hold on to your face.
In Ansible, inventory files are where you specify the servers that you want to run your tasks on. This will usually be a list of your server IP addresses/domain names. These servers can be organised into groups. We can then use these group names in our playbooks to specify which servers we want to run our tasks on.
Take the following example:
[webservers] foo.example.com 188.8.131.52 [dbservers] bar.example.com Another.example.com
This inventory file has two groups of servers: webservers and dbservers. We can use either of these groups in our playbooks and the tasks in that playbook will be run on all servers in that group.
A playbook is a YAML file contains a set of tasks that you want to run on the server. When you run the playbook from the command line it will go through the tasks, in sequence, and execute them one by one.
Here’s an example playbook called example.yml:
--- - hosts: webservers remote_user: root - name: move folder from one location to another command: mv sitefolder /var/www/sitefolder - name: enable php5 mcrypt php module command: php5enmod mcrypt
If we were to run the following in the command line on our local machine...
Ansible would run all the tasks in the example.yml playbook on the webserversgroup in our inventory. This would result in the following changes on the servers:
- The sitefolder directory getting moved to the /var/www directory
- The php5 mcrypt module getting enabled
As you can see, each task in the playbook has a name and a command. The name contains a brief description of the task, whilst the command contains the actual command that gets executed on the server.
This is a great improvement to our workflow already, but at the moment we still have to specify the exact server commands that we want to run. Luckily Ansible ships with a set of modules that allow us to specify tasks in a more concise and readable fashion.
Let’s take an example of how modules can simplify how we write tasks. Let’s say we have a playbook called example2.yml that simply creates a directory on the server, sets the owner of that directory to a particular user and then modifies the permissions of that directory.
Using the command functionality that we used in our first example, our playbook would look like this:
--- - name: create directory command: mkdir exampledir - name: set directory owner command: chown joebloggs:admin exampledir - name: set directory permissions command: chmod 2775 exampledir
As you can see, whilst still reasonably concise, we needed three tasks to achieve what we need. However, if we use the file module that comes with Ansible, we simply need the following:
--- - name: create directory with correct owner and permissions file: path=exampledir state=directory owner=joebloggs group=admin mode=2775
Here, we’ve substituted the command module with the file module which enables us to put everything we need on one line. The file module is one of Ansible’s core modules that allows us to create, delete and set attributes on files and directories on a server.
Ansible ships with hundreds of modules that cover many common server tasks such as setting up and managing databases, installing packages and deploying files from version control.
Now that we’ve covered playbooks and modules, let’s quickly look at roles.
Let’s say, for example, that we have two different playbooks in our project, and both playbooks need to be able to create a database on our server. The way things currently are we would have to add the relevant tasks to both our playbooks and that doesn’t make for DRY code. This is where roles come in.
Roles allow us to organise our tasks in a way that makes it trivial to include them in multiple playbooks.
In basic terms, a role is just a directory that lives in the roles directory of the your Ansible project. Each role directory contains a set of other directories containing tasks, variables and templates relevant to that role.
For example, if we created a roles > database directory then we could just call that role into our playbook as follows:
--- - hosts: webservers - roles: - database
We’re currently in the process of updating our provisioning and deployment strategies at Bluegg using Ansible. Once finished, we’ll be using it to take care of the following when setting up servers:
- Creating user accounts
- Adding SSH keys
- Installing required packages
- Enabling required php/apache modules
- Setting up directory structure ready for deployments
- Setting up virtual host configurations
- Creating required databases
- Downloading and installing Craft CMS (our CMS of choice)
- Configuring and installing SSL certificates
We’re also in the process of replacing Capistrano with Ansible as our method of deploying our websites/apps. Since both technologies just rely on commands being run on the server over an SSH connection the swap over is fairly straightforward. We can also use Ansible to easily synchronise databases between remote servers and our local development environments.
This article just scratches the surface of what Ansible is capable of. If you’re finding yourself spending too much time setting up hosting for each new project then do yourself a favour and take a look!