Vagrant and Ansible
Recently, I have started a personal project built with Clojure, a website for managing family records and visualizing pedigree tree. The problem is that I need an automation tool for setting up the development environment and deploying my website to the server. And yes, Vagrant is one of the best solution out there. I have used Vagrant with Chef before and found that Chef is a bit complex and requires installation of Ruby and Chef on the server before you can deploying anything.
After looking at all of Vagrant’s Provisioning solution, I decided to give Ansible a try because it is simple and operate over SSH, which means I need to install nearly nothing on the server to use it (of course you still can Ansible on server and do a local Provision there, but it’s only one more command to type).
This blog post is the summary of my experience with Vagrant and Ansible, how I set up my development environment and how can I re-use the code for other types of project. The sample project can be found at https://github.com/tmtxt/clojure-web-skeleton. Before you come to the next part, take a look at the basic usage of Vagrant and Ansible.
Basic Structure
A project with Vagrant and Ansible will look like this
Just focus on the Vagrantfile
and the ansible
folder, they are the main
stuff. The other files and folders are just example of a Clojure project.
- Vagrantfile: storing config for Vagrant and specific Ansible variables to override when running inside Vagrant.
- ansible: folder for storing Ansible stuff, used for provisioning
- group_vars: defining variables used for the project
- main.yml: main playbook for the project
- roles: a git submodule that defines various Ansible roles for setting sepcific service on the server. This is reusable for other projects.
Reusable Ansible Roles
As described in the above section, there is roles
folder which contains several git
submodules for defining reusable Ansible components. You can take a look at my
sample roles
folder
here.
Each submodule is an Ansible role,
which can be created using the ansible-galaxy
command
Each role here is used for installing and configuring specific software or service. You can look into the roles I defined on Github for more detail. Inside every role, we will define some default variables and then in each project, depending on the purpose, we can override it later.
Putting all your roles into git sub module allows you to re-use it for other kinds of project easily. For example, for a Clojure project and a Nodejs project, you both need to use a database server (maybe PostgreSQL), just include the role PostgreSQL you have defined before and the database server is set up and ready to use.
Custom playbook for each project
In the structure above, in every of my project, there is a main.yml
playbook
for defining the task that need to set up for that kind of project. For example,
in my Clojure project, the main
playbook will look similar to this
You can override the role variable directly inside this file or in group_vars
(like I did in the project on Github) or host_vars
, for example
As you can see, when working on a new project, what I need to do is just include the roles I need and everything is set up for me. After that, just add the tasks specific to that kind of project and start developing.
Provisioning in Vagrant
Now, you have the playbook ready to run, you need to plug it into Vagrant to start provisioning. You Vagrantfile will look like this
That’s it, now just run vagrant up --provision
and the automation process will
happen automatically.
Deployment
Ansible opearate through SSH connection so you don’t really need to install
anything on server. First, define a hosts
, which contains the address to your
remote server, inside your ansible
folder. Next, create a new file in with the
same name with your remote server inside ansible/hosts_vars
and put all the
necessary variables you need to override for that remote server there.
An example of host file and host_vars can be found here.
Finally, start Ansible for the remote server with the command