Set up Vagrant otherwise re-invent the wheel

In my early days of programming experimentation I used to code lots of PHP. This is the first reason I call them the Dark Days. The second one is tightly related to the absence of portability and version control in my daily routine. In a sentence, it seemed like coding with no future.

Vagrant

Hopefully, the version control problem was solved a long way back when I started using Git, but I was still facing issues with my current setup. I was working on different kinds of machines with different tools and different databases, building apps and uploading them by FTP to hosting providers. I had a rather complicated setup, built on different versions of Apache/nginx, different configurations and the worst: different O/Ses and versions. Then I found Virtualbox and gradually entered the world of Virtualization.

Virtualbox was open source (and still is, for those who haven’t used it) and pretty easy to use, so I started building different kind of machines and kept my individual configurations in each one separately.

After many years of moving from PHP to Ruby and Rails stack (while first passing through Python/Django for something more than a half year), I found out Vagrant and my development process changed to its best, until today.

Vagrant is a cross-compatible open source tool that lets us build complete development environments inside individual virtual machines. It decreases the time needed to setup a new development environment, totally controlling multiple virtual machine “instances” from the console and does all this by parsing a simple configuration file.

Vagrant can be used with Virtualbox, vmware, Parallels among others. It can also use Chef, Puppet, Ansible etc. in order to provision the virtual machine. This means that by using a simple command you’re able to create as many instances (they are called “boxes”) of a machine as you want.

Why ?

Because:

If you have not been using any kind of Virtualization for development, you may start considering it soon. You will be doomed If your employer assigns you a project based on a different configuration.

If you feel you’ll be “just fine” developing for differently configured servers from your current “personal” one, then good luck! I guess you also see testing as waste of time, correct?

Installation

There’s no need to spend a lot of time here as the installation is pretty smooth, at least on OSX/macOS and Ubuntu/Debian/Kali that I’ve tried.

Make sure you’ve installed the latest version of Virtualbox (or any other Virtualization tool you want) first.

You can either download the package from the official website downloads page or install it manually. In macOS a simple

brew install vagrant

will suffice, considering that you’ve installed homebrew first. In Debian-based O/Ses, it installs as usual with

sudo apt-get install vagrant

You can find out the version running by typing

vagrant --version

in the console.

Start

The first location that must be bookmarked should be Hashicorp’s vagrant box search engine:

Discover Vagrant Boxes - Atlas by HashiCorp

This page lets you discover and use Vagrant Boxes created by the community. You can search by operating system… atlas.hashicorp.com There are lots of ready-to-install boxes, including Windows, BSDs and everything else. As an example I’ll choose the Official Ubuntu Server 14.04 LTS box:

Vagrant box ubuntu/trusty64 - Atlas by HashiCorp

vagrant init ubuntu/trusty64; vagrant up --provider virtualbox

I won’t go through the process of creating a file by hand and entering all information there from scratch, but use the ready-to-go command that’s listed in the page:

vagrant init ubuntu/trusty64; vagrant up --provider virtualbox

This command:

There will be lots of informative messages displayed and installation may take a while depending on the system, its resources and the box you’re installing. If everything completes fine, you’ll be able to SSH to the newly installed box with a simple command:

vagrant ssh

My :debian Vagrant Setup

I am working with several operating systems so I have to set them up separately. At the end all of them run smoothly and in parallel on my macbook.

My debian configuration I am using lately in order to build a full Ruby on Rails v5 stack looks like this:

# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrant configuration
Vagrant.configure(2) do |config|
  # Requires
   require ./settings
  # Vagrant box
   config.vm.box = debian/jessie64
  # Networks
   config.vm.network public_network
   config.vm.network private_network, ip: 192.168.44.10
  # Shared folders
   config.vm.synced_folder HOST_DIR, QUEST_DIR, type: nfs
  # Resource configuration
   config.vm.provider virtualbox do |vb|
     vb.cpus = 2
     vb.memory = 512
   end
  # Shell scripts
   config.vm.provision :shell,
     path: scripts/install-tools.sh
   config.vm.provision :shell,
     path: scripts/install-rvm.sh,
     args: stable,
     privileged: false
   config.vm.provision :shell,
     path: scripts/install-ruby.sh,
     args: 2.3.0 bundler,
     privileged: false
   config.vm.provision :shell,
     path: scripts/postgresql.sh
   config.vm.provision :shell,
     path: scripts/install-redis.sh
end

Networking

I usually work in a private_network but there are cases e.g. I want to scan the entire subnet or I want to show something to a colleague. Then I include a public_network with an IP Address acquired by DHCP Server. What I love in Vagrant about that is that you can write a script to create multiple network interfaces or simple copy and paste them. There was I case I wanted to add 8 network interfaces to test a network application, so I didn’t have to create them one by one in Virtualbox.

Settings

I am actually requiring ./settings.rb because I want to keep my folders dynamic, as I might want to share this Vagrantfile with another colleague who might have a different folder structure on his machine. I also have included the settings file in .gitignore and renamed it to settings.local.rb in order to be there If someone clones the repository.

HOST_DIR= “~/myusername/projects/new-project”
QUEST_DIR= “/home/vagrant/new-project”

I usually follow the same practise to add multiple mappings with the help of NFS.

I am also using shell scripts to provision my VM as this is pretty simple and I don’t have to set up a whole Chef stack in order to complete basic operations.

install-tools.sh

Most things are obvious here, I use -y to auto reply to requests and autoremove to delete junk. The packages I am installing are the ones essential to setup a basic Rails v5 stack.

#!/bin/sh -e
apt-get update
apt-get -y upgrade
sudo apt-get -y install curl git nodejs build-essential libgmp-dev libpq-dev
sudo apt-get autoremove -y
install-rvm.sh

Time to install RVM. I am willing to replace it in the future but for now it does what it says, so I a pretty fine with it. The instructions to set it up have been grabbed from the original website: https://rvm.io.

I am only adding an argument as a version, in order to control its installation from the Vagrantfile.

#!/usr/bin/env bash
gpg — keyserver hkp://keys.gnupg.net — recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s $1
install-ruby.sh

Parsing again an argument as the version I want to install from the Vagrantfile is the only different thing here.

#!/usr/bin/env bash
source $HOME/.rvm/scripts/rvm
rvm use — default — install $1
shift
if (( $# ))
 then gem install $@
fi
gem update — system
postgresql.sh

Setting up PostgreSQL initially may seem a bit hard in the beginning but If the steps are analyzed, what we have to do seems pretty obvious. Every command has comments on the top to describe its purpose.

#!/bin/sh -e
# Edit the following to change the name of the database user that will be created:
APP_DB_USER=vagrant
APP_DB_PASS=dbpass
# Edit the following to change the version of PostgreSQL that is installed
PG_VERSION=9.4
export DEBIAN_FRONTEND=noninteractive
PROVISIONED_ON=/etc/vm_provision_on_timestamp
if [ -f$PROVISIONED_ON]
then
 echo “VM was already provisioned at: $(cat $PROVISIONED_ON)echo “To run system updates manually login via ‘vagrant ssh’ and run ‘apt-get update && apt-get upgrade’”
 echo “”
 exit
fi
PG_REPO_APT_SOURCE=/etc/apt/sources.list.d/pgdg.list
if [ ! -f$PG_REPO_APT_SOURCE]
then
 # Add PG apt repo:
 echo “deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main” >$PG_REPO_APT_SOURCE# Add PGDG repo key:
 wget — quiet -O — https://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | apt-key add -
fi
# Update package list and upgrade all packages
apt-get update
apt-get -y upgrade
apt-get -y install “postgresql-$PG_VERSION” “postgresql-contrib-$PG_VERSIONPG_CONF=”/etc/postgresql/$PG_VERSION/main/postgresql.conf”
PG_HBA=”/etc/postgresql/$PG_VERSION/main/pg_hba.conf”
PG_DIR=”/var/lib/postgresql/$PG_VERSION/main”
# Edit postgresql.conf to change listen address to ‘*’:
sed -i “s/#listen_addresses = ‘localhost’/listen_addresses =*’/” “$PG_CONF# Append to pg_hba.conf to add password auth:
echo “host all all localhost trust” >>$PG_HBAecho “host all all all md5” >>$PG_HBA# Explicitly set default client_encoding
echo “client_encoding = utf8” >>$PG_CONF# Restart so that all new config is loaded:
service postgresql restart
cat << EOF | sudo su — postgres -c psql
 — Create the database user:
CREATE USER $APP_DB_USER WITH CREATEDB CREATEROLE;
EOF
# Tag the provision time:
date >$PROVISIONED_ONecho “Successfully created PostgreSQL dev virtual machine.”
echo “”
install-redis.sh

Redis is necessary for Rails v5 in order for ActionCable to work, so I am including it to the system’s configuration. I have been also using Sidekiq and others related to Redis and I had no option to ignore it.

#!/bin/bash
mkdir /opt/redis
cd /opt/redis
# Use latest stable
wget -q http://download.redis.io/redis-stable.tar.gz
# Only update newer files
tar -xz — keep-newer-files -f redis-stable.tar.gz
cd redis-stable
make
sudo make install
sudo mkdir -p /etc/redis
sudo mkdir -p /var/redis/6379
sudo useradd — system — home-dir /var/redis redis
sudo chown -R redis:redis /var/redis
sudo cp -u /vagrant/redis.conf /etc/redis/6379.conf
sudo cp -u /vagrant/redis.init.d /etc/init.d/redis_6379
sudo update-rc.d redis_6379 defaults
sudo /etc/init.d/redis_6379 start
sudo chmod a+x /etc/init.d/redis_6379
sudo /etc/init.d/redis_6379 start

Cheatsheet

vagrant init
vagrant up
vagrant status
vagrant suspend
vagrant halt
vagrant reload
vagrant ssh
vagrant box list
vagrant destroy
vagrant global-status

Conclusion

The company behind Vagrant is HashiCorp which has been developing some other also awesome tools. They have also tried to replace Vagrant with Otto but people seem to love it too much to migrate to another tool, so it seems that they’re abandoning it.

Vagrant has limitations but it’s a wonderful tool for what it offers. Except from some adventures with NFS and Virtualbox in OSX I recently went into, it seems that it is stable and works always issue-free, at least for the last two years I’ve been using it !


References:

Vagrant Up & Running