Managing Dev Environments with Vagrant

Maros Kukan
6 min readJul 10, 2023

--

Foreword

A good tool improves the way you work. A great tool improves the way you think.
— Jeff Duntemann

In any endeavor, whether it be crafting a piece of art, constructing a building, or solving complex problems, the quality of the tools at our disposal plays a pivotal role in determining the outcome. Just as a painter requires high-quality brushes and paints to create a masterpiece, or a carpenter relies on precise instruments to build a sturdy structure, the use of good tools is essential for achieving optimal results.

Good tools not only enhance our abilities but also increase efficiency, accuracy, and effectiveness in our work. They empower us to overcome challenges, unleash our creativity, and achieve the desired outcome with precision and finesse. Therefore, the importance of good tools in producing good outcomes cannot be overstated.

In this article I would like to take you on a journey to learn and understand one of my favorite tool called Vagrant. By the end, you will be able to use it to quickly and efficiently manage your local development environments.

Vagrant

HashiCorp Vagrant is an open-source tool that simplifies the creation and management of virtual development environments. Initially published in 2010 by Mitchell Hashimoto, Vagrant aimed to address the challenges faced by developers when setting up and configuring consistent development environments across different machines and platforms.

By providing a unified workflow, Vagrant allows us to easily create and share reproducible development environments, eliminating the “works on my machine” problem. Vagrant achieves this by abstracting the underlying infrastructure details, such as virtualization providers (e.g., Hyper-V, VirtualBox, VMware), and providing a declarative configuration file, known as a Vagrantfile, that specifies the desired environment setup.

With Vagrant, we can quickly provision and configure virtual machines or containers, easily share our development environments with team members, and ensure consistency between development, testing, and production environments. It also integrates seamlessly with configuration management tools like Ansible, Chef, and Puppet, enabling automated provisioning and configuration of the environment. Overall, HashiCorp Vagrant empowers developers to focus on writing code rather than spending valuable time on environment setup and configuration.

Architecture

Vagrant — Big Picture

To deploy a local 3-tier application environment for development, we would usually follow these steps:

  1. We start by downloading the source application and environment artifacts from remote git repository. The repository includes the template (Vagrantfile) which defines our desired environment. To further customize the deployed application we can use simple shell scripts or more complex configuration management tools like Ansible or Puppet.
  2. Before the environment creation Vagrant can interpret and validate vagrant validate the Vagrantfile. It is considers a good practice to validate your templates.
  3. Using vagrant up we instruct Vagrant to create desired environment. If the virtual machine template (box file) is not available locally it will be downloaded from Vagrant Cloud.
  4. Once the template is available locally, Vagrant will deploy a new virtual machine using correct provider.
  5. After the VM is powered on, provisioning scripts will be executed.
  6. Finally, the synced folder feature can be used to ensure that the application code is available within the virtual machine.

There are two other components worth to mention:

  • Triggers are a feature that allows you to execute custom scripts or commands at specific points during the Vagrant workflow. These triggers can be used to automate tasks, perform additional provisioning, or customize the Vagrant environment.
  • Plugins are additional software components that extend its functionality and provide extra features. They allow users to customize and enhance the capabilities of Vagrant to suit their specific needs. For example in order to support provisioning using VMware Workstation, we need to have the vagrant-vmware-desktop plugin installed.

In the next section, we take a closer look at simple Vagrantfile and provisioning script that can be used to quickly install Docker daemon on top of Ubuntu 23.04.

💡Tip: If you are curios how the Ubuntu 23.04 Vagrant Box was created in the first place, take a look at Automating Golden Image builds with Packer.

Template

Vagrantfile is a configuration file written in Ruby. It acts as a blueprint for creating machine images using Vagrant. It provides a structured and declarative approach to define the desired virtual machine state.

💡Tip: Vagrantfile used in this example refers to maroskukan/ubuntu23.04 template which was built to supports multiple providers.

Different section are used to describe the provider specific configuration. For example in order to enable nested virtualization support and linked clone support using Hyper-V the following block is used:

  # Hyper-V Provider Specific Configuration
config.vm.provider "hyperv" do |h|
h.enable_virtualization_extensions = true
h.linked_clone = true
h.memory = 2048
h.maxmemory = 2048
end

The source template definition can be found in the config.vm.define block. We can also set the machine hostname.

 config.vm.define "binaries" do |binaries|
# VM Shared Configuration
binaries.vm.box = "maroskukan/ubuntu2304"
binaries.vm.hostname = "binaries"

Finally, the config.vm.provision block contains the type and path for provisioning scripts.

  # VM Shared Provisioning
config.vm.provision "shell", path: "install.from.binaries.sh"

Workflow

The following workflow describes the required steps to provision Docker deamon on Ubuntu 23.04 Virtual Machine.

  1. Clone the remote repository and navigate to folder that contains the Vagrantfile.
git clone https://github.com/maroskukan/docker-on-linux
cd docker-on-linux\Vagrantfiles\binaries

2. Validate the Vagrantfile using vagrant validate. This step is useful in a CI/CD process, in where depending on the exit code value captured in variable $LASTEXITCODE in PowerShell or $? in Bash we can fail early in the pipeline.

vagrant validate
Vagrantfile validated successfully.

💡Tip: Use the vagrant status command to quickly verify the current status of an environment. Some common values include not_created, runningor off.

3. Start and provision the environment using vagrant up. This command can take optional argument --provider <name> to specify which provider (hypervisor) to use to build the environment.

vagrant up
Bringing machine 'binaries' up with 'hyperv' provider...
==> binaries: Verifying Hyper-V is enabled...
==> binaries: Verifying Hyper-V is accessible...
==> binaries: Box 'maroskukan/ubuntu2304' could not be found. Attempting to find and install...
binaries: Box Provider: hyperv
binaries: Box Version: >= 0
==> binaries: Loading metadata for box 'maroskukan/ubuntu2304'
binaries: URL: https://vagrantcloud.com/maroskukan/ubuntu2304
==> binaries: Adding box 'maroskukan/ubuntu2304' (v2023.06.23) for provider: hyperv
binaries: Downloading: https://vagrantcloud.com/maroskukan/boxes/ubuntu2304/versions/2023.06.23/providers/hyperv.box
binaries:
==> binaries: Successfully added box 'maroskukan/ubuntu2304' (v2023.06.23) for 'hyperv'!
==> binaries: Importing a Hyper-V instance
binaries: Creating and registering the VM...
binaries: Successfully imported VM
binaries: Configuring the VM...
binaries: Setting VM Enhanced session transport type to disabled/default (VMBus)
==> binaries: Starting the machine...
==> binaries: Waiting for the machine to report its IP address...
binaries: Timeout: 120 seconds
binaries: IP: 192.168.101.224
==> binaries: Waiting for machine to boot. This may take a few minutes...
binaries: SSH address: 192.168.101.224:22
binaries: SSH username: vagrant
binaries: SSH auth method: private key
binaries:
binaries: Vagrant insecure key detected. Vagrant will automatically replace
binaries: this with a newly generated keypair for better security.
binaries:
binaries: Inserting generated public key within guest...
binaries: Removing insecure key from the guest if it's present...
binaries: Key inserted! Disconnecting and reconnecting using new SSH key...
==> binaries: Machine booted and ready!
==> binaries: Setting hostname...
==> binaries: Rsyncing folder: /cygdrive/c/code/maroskukan/docker-on-linux/Vagrantfiles/binaries/ => /vagrant
==> binaries: Running provisioner: shell...
binaries: Running: C:/Users/mk/AppData/Local/Temp/vagrant-shell20230710-10060-60pi9j.sh
binaries: Reading package list

💡Tip: If you want to skip the provisioning steps you can optionally add the --no-provision argument.

4. Connect to machine via SSH using vagrant ssh. If you would like to see the SSH specific configuration used to define this connection, use the vagrant ssh-config command.

vagrant ssh
Welcome to Ubuntu 23.04 (GNU/Linux 6.2.0-23-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Mon Jul 10 10:48:17 AM UTC 2023

System load: 0.11 Processes: 115
Usage of /: 6.1% of 77.15GB Users logged in: 0
Memory usage: 16% IPv4 address for eth0: 192.168.101.224
Swap usage: 0%

5. When connected to the Virtual Machine, validate content of the synced folder. According our configuration, this should map the host folder . where the Vagrantfile exist to /vagrant folder on the guest.

ls /vagrant
install.from.binaries.sh README.md Vagrantfile

💡Tip: Since content on synced folders is not part of machine lifecycle (start, stop and delete) it is useful for storing files that should be preserved.

6. When you are done for the day, exit the guest shell and then power-off the VM using vagrant halt. This will conserve host system resources.

vagrant halt
==> binaries: Attempting graceful shutdown of VM...

Cleanup

Finally, when you would like to remove all traces of this virtual machine, including virtual disks, use the vagrant destroy command.

vagrant destroy --force
==> binaries: Stopping the machine...
==> binaries: Deleting the machine...

💡Tip: The Vagrant box template will remain available on your host machine for future use. If you would like to remove it you need to use the vagrant box remove <box-name>.

Closing thoughts

In conclusion, Vagrant emerges as a powerful tool that revolutionizes the way we create and manage our development environments. With its simplicity, reproducibility, and portability, Vagrant eliminates the hassle of environment setup, ensuring consistent and reliable development workflows across teams and projects.

I had a blast writing this article and I would love to hear your thoughts and experience with Vagrant.

--

--