Vagrant is a tool for building and managing virtual machine environments in a single workflow. Because Vagrant is geared so strongly toward automation, it lowers the time it takes to set up environments and as a result, improves production parity and consistency across all environments, making the "but it works on my local" an excuse of the past.
As a DevOps Engineer, I've found an interest in Vagrant because it enabled our team to provision disposable environments in a consistent manner. As a result, this allowed us to quickly test our infrastructure scripts and POC tools such as Chef, Puppet and Ansible. For further reading, visit Vagrant
Installation
Installing Vagrant is extremely easy. Head over to the Vagrant downloads page and get the appropriate installer or package for your platform. Install the package using standard procedures for your operating system.
Getting Started
Once you have both the Vagrant CLI and VirtualBox installed, create a new working directory and run vagrant init
. You'll notice that a file had been created. The Vagrantfile describes the type of VMs that are to be created in terms of the configuration and the method used to provision these VMs.
Next for the purpose of this tutorial, get your favorite editor handy and copy the sample below. I've added comments, in hope to demystify some of the Vagrantfile magic.
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version
Vagrant.configure("2") do |config|
# Defining a VM named controller
config.vm.define "controller" do |controller|
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
controller.vm.box = "minimal/xenial64"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
controller.vm.network "private_network", ip: "192.168.50.4"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
controller.vm.network "public_network"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
controller.vm.provider :virtualbox do |vb|
vb.customize [
"modifyvm", :id,
"--cpuexecutioncap", "50",
"--memory", "1024",
]
end
end
end
Run vagrant up controller
The vagrant up [name|id]
command creates and configures the VM according to your Vagrantfile in your current working directory.
Once up, run vagrant status controller
to confirm that the VM had been created and is running.
To ssh to the host run vagrant ssh controller
Because we've configured the VM to belong to a private network in our Vagrantfile, we're able access the server using its private IP 192.168.50.4
Provisioners
Provisioners in Vagrant allow us to automate the installation of software, as well as alter the system configuration as part of the vagrant up
process.
Of course, all the installation and configuration can be done by hand using vagrant ssh
. But by using the provisioning systems built-in to Vagrant, we can automate the process so that it is repeatable. Most importantly, it requires no human interaction, so we can vagrant destroy
and vagrant up
and have a fully ready-to-go work environment with a single command.
Vagrant supports a multitude of provisioners. For the purpose of this tutorial we'll make use of the Ansible provisioner. We'll write a playbook to create a new user and amend our Vagrant file to run the playbook during the vagrant up
process.
Get out your editor and create main.yaml
in the current working directory and copy the sample code below.
- name: Dev.to Vagrant with Ansible Demos
hosts: all
tasks:
- name: Add the user ansible with a specific uid and a primary group of 'admin'
user:
name: ansible
comment: ansible created
uid: 1040
group: root
become: true
become_user: root
Next, amend the Vagrant file.
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version
Vagrant.configure("2") do |config|
# Defining a VM named controller
config.vm.define "controller" do |controller|
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
controller.vm.box = "minimal/xenial64"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
controller.vm.network "private_network", ip: "192.168.50.4"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
controller.vm.network "public_network"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
controller.vm.provider :virtualbox do |vb|
vb.customize [
"modifyvm", :id,
"--cpuexecutioncap", "50",
"--memory", "1024",
]
end
# Run Ansible from the Vagrant Host
controller.vm.provision "ansible" do |ansible|
ansible.playbook = "main.yaml"
end
end
end
Save and run vagrant destroy controller
and then vagrant up controller
. You'll see in the console output that the playbook is then executed as part of the vagrant up
process. See output below and of course, I checked that the user had been created.
Vagrant has been an essential to my development and my learning process. I hope I sparked some interest in this awesome tool. Vagrant has enabled me to learn both Puppet and Ansible and in general it improved my Sys Admin skill set.
If there is an interest in wanting to learn more about Vagrant, I am definitely keen and showing you guys how to set up an entire Python web server stack using a Vagrantfile and Ansible. I hope you all enjoyed this read.
Top comments (2)
Would it at all be possible to do inline appending of the vagrantfile during the ansible run to streamline the entire process?
If I'm understanding your question correctly, you could append the Vagrantfile with a provisioner and run
vagrant up controller --provision
. It will only execute the provision blocks, without destroying the VM