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.
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.
- I can create a box with a custom configuration for a project, create a git repository for it and share it with my colleagues so we all are consistent, use the same O/S version, the same tools and the same setup. We’ re having the same issues too !
- As we’re keeping the VagrantFile in git, it’s easy to provision or to apply new changes to the system’s configuration
- It’s Automation versus Stone Age. Simply as that.
- Downloading a Vagrant Box and creating several new machines from it has way less overhead than cloning Virtualbox machines or re-installing the O/S several times from an .iso (Stone Age appears again). It consumes way less disk space and resources.
- People use it to spin multiple servers at once, which I find pretty awesome, especially If you want to experiment with load balancing and other technologies. I will be covering Multi Machine Clustering in a future post, along with Plugin creation and other stuff.
- This is fun. You may check the latest CentOS version out of curiosity or test your application for compatibility in an old Fedora installation. Or freeBSD to see “what it looks like”.
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?
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
in the console.
The first location that must be bookmarked should be Hashicorp’s vagrant box search engine:
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 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
- Initialises a new Ubuntu box, which includes downloading the whole Ubuntu operating system machine locally.
- It brings the installed machine “up” and specifies the provider. It could be vmware, parallels etc.
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:
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
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.
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.
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_VERSION” PG_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_HBA” echo “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_ON” echo “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
vagrant init vagrant up vagrant status vagrant suspend vagrant halt vagrant reload vagrant ssh vagrant box list vagrant destroy vagrant global-status
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 !