Vagrant, yet another amazing product from Hashicorp.
Vagrant makes it really easy to provision virtual servers for local development (not limited to), which they refer as “boxes”, that enables developers to run their jobs/tasks/applications in a really easy and fast way. Vagrant utilizes a declarative configuration model, so you can describe which OS you want, bootstrap them with installation instructions as soon as it boots, etc.
What are we doing today?
When completing this tutorial, you will have Vagrant and Virtualbox installed on your Mac and should be able to launch a Ubuntu Virtual Server locally with Vagrant and using the Virtualbox provider which will be responsible for running our VM’s.
We will also look at different configuration options to configure the VM, bootstrapping software, using the shell, docker and ansible provisioner.
For this demonstration, I am using a Mac OSX, but you can run this on Mac, Windows or Linux. First we will use Homebrew to install Virtualbox, then Vagrant, then we will provision a Ubuntu box and I will also show how to inject shell commands into your Vagrantfile so that you can provision software to your VM, and also forward traffic to a web server from the host to the guest.
If you are looking for a Linux version instead of mac, you can look at this post: * Use Vagrant to Setup a Local Development Environment on Linux
Pre-Requisites
I will be installing Vagrant and Virtualbox with Homebrew, if you do not have homebrew installed, you can install homebrew with:
1
|
|
Once homebrew is installed, it’s a good thing to update the indexes:
1
|
|
Virtualbox
Install VirtualBox using homebrew:
1
|
|
Vagrant
Install Vagrant using homebrew:
1
|
|
Install the virtualbox guest additions plugin for vagrant:
1
|
|
If you would like a vagrant manager utility to help you manage your vagrant boxes, you can install vagrant-manager using homebrew:
1
|
|
Create your first Vagrant Box
From app.vagrantup.com/boxes/search you can search for any box, such as ubuntu, centos, alpine etc and for this demonstration I am going with ubuntu/focal64.
I am creating a new directory for my devbox:
1 2 |
|
Then initialize the Vagrantfile by running:
1
|
|
A Vagrantfile
has been created in the current working directory:
1 2 3 4 5 |
|
Boot the VM:
1
|
|
The box should now be in a started state, and we can verify that by running:
1 2 3 4 |
|
We can now SSH to our VM by running:
1 2 |
|
Installing Software with Vagrant
First let’s destroy the VM that we created:
1
|
|
Then edit the Vagrantfile
and add the commands that we want to be executed when the VM boots, in our case, installing Nginx:
1 2 3 4 5 6 7 8 |
|
You will also notice that we are forwarding port 8080 from our host, to port 80 on the VM so that we can access the webserver on port 8080 from our laptop. Then boot the VM:
1
|
|
Once the VM has booted and installed our software, we should be able to access the index document served by Nginx on our VM:
1 2 3 4 5 6 7 8 9 10 11 |
|
Shared Folders
Let’s say you want to map your local directory to your VM, in a scenario where you want to store your index.html
on your laptop and map it to the VM, we can use config.vm.synced_folder
.
On our laptop, create a html
directory where we will store our index.hml
:
1
|
|
Now create the content in the index.html
under the html
directory:
1
|
|
Now we need to make vagrant aware of the folder that we are mapping to the VM, so we need to edit the Vagrantfile
and it will now look like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
To reload the VM with our changes, we use vagrant provision
to update our VM when changes to provisioners are made, and vagrant reload
when we have config changes such as config.vm.network
, but to restart the VM and forcing provisioners to run, we can use the following:
Thanks @joshva_jebaraj
1
|
|
Once the VM is up, we can verify the changes:
1 2 |
|
Now we can edit our content locally which is synced to our VM.
Setting Hostname and Configure Memory
We can also configure the hostname of our VM and configure the amount of memory that we want to allocate to our VM using:
config.vm.hostname
vb.memory
An example of that will look like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
`
In this example our VM’s hostname is mydevbox
and we assigned 1024MB of memory to our VM.
Provisioners: Shell
We can also run scripts from our local directory on our laptop on our VM using the shell provisioner.
First we need to create the script on our local directory:
1 2 3 4 |
|
Then in our Vagrantfile
we inform vagrant to execute the shell script:
1 2 3 4 5 6 7 8 |
|
Since my VM is already running, I will be doing a reload
:
1 2 3 4 5 6 7 |
|
As you can see the shell script from our local directory was executed on our VM, you can use this method to automate installations as well, etc.
Provisioners: Docker
Vagrant offers a docker provisioner, and for this example we will be hosting a mysql server using docker container in our VM.
Our Vagrantfile
:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Since I don’t have port 3306
listening locally, I have mapped port 3306
from my laptop to port 3306
on my VM and I am using the mysql:8.0
container image from docker hub and passing the arguments which is specific to the container.
The convenient thing about the docker provisioner, is that it will install docker onto the VM for you.
Once the config has been set in your Vagrantfile
do a reload:
1 2 3 4 5 6 7 |
|
From our laptop we should be able to communicate with our mysql server:
1 2 3 4 5 6 7 8 9 10 11 |
|
We can also SSH to our VM and verify if the container is running:
1
|
|
And then list the containers:
1 2 3 |
|
Provisioners: Ansible
We can also execute Ansible playbooks on our VM using the Ansible Provisioner.
Something to note is that we use ansible
to execute the playbook on the host, and ansible_local
to execute the playbook on the VM.
First we will create our project structure for ansible, so that we have the following in place:
1 2 3 4 |
|
Create the provisioning
directory:
1
|
|
Then the content for our provisioning/playbook.yml
playbook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Our provisioning/group_vars/all
file that will contain the variables for the all group:
1
|
|
In our Vagrantfile
:
1 2 3 4 5 6 7 8 9 10 |
|
When using ansible with vagrant the inventory is auto-generated when then inventory is not specified. Vagrant will store the inventory on the host at .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory
.
To execute playbooks with ansible, we need ansible installed on our host machine, for this demonstration I will be using virtualenv and then install ansible using pip:
1 2 3 4 |
|
Now that we have ansible installed, reload the VM to execute the playbook on our VM:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Pretty neat right?
Tear Down
To destroy the VM:
1
|
|
Resources
For more information on vagrant, check out their documentation:
On provisioning documentation:
- https://www.vagrantup.com/docs/provisioning/shell
- https://www.vagrantup.com/docs/provisioning/docker
- https://www.vagrantup.com/docs/provisioning/ansible_intro
I have a couple of example Vagrantfile
s available on my github repository:
Thank You
Thanks for reading, if you like my content, check out my website or follow me at @ruanbekker on Twitter.