Enhancing Development Environments: VirtualBox, Vagrant, and Docker

Jimin
8 min readMar 15, 2024

--

The complexity of modern software development requires tools that ensure not just efficiency but also consistency and scalability in development environments. VirtualBox, Vagrant, and Docker form a powerful trio that addresses these needs, allowing developers to manage and distribute environments with precision.

Why VirtualBox, Vagrant, and Docker?

The collaboration between VirtualBox, Vagrant, and Docker creates a layered approach to development environments, addressing different needs but working together seamlessly:

VirtualBox

Role

Virtualization Platform

How It Works

VirtualBox acts as a hypervisor, providing hardware virtualization that allows you to create and run multiple virtual machines (VMs) on a single physical host. Each VM can run its own operating system independently, making it possible to test applications across different OS environments without needing multiple physical devices.

Key Contributions

  • Isolation: Ensures that development, testing, and production environments can be isolated from one another, minimizing conflicts between different projects or versions.
  • Resource Efficiency: By running on the same physical hardware, it reduces the need for additional physical devices, optimizing resource usage.
  • Cross-Platform Compatibility: Enables developers to run and test applications on operating systems that may not be natively supported by their physical hardware (e.g., running a Linux-based environment on Windows).

Vagrant

Role

Environment Management Tool

How It Works

Vagrant provides a simple, command-line-based workflow for creating, configuring, and managing virtual machines. It uses a Vagrantfile to define the VM’s configuration in code, making it easy to replicate and share development environments.

Key Contributions

  • Configuration as Code: Allows development environments to be versioned, shared, and replicated easily, ensuring that all team members work in an identical setup.
  • Workflow Automation: Automates repetitive tasks such as provisioning, starting up, halting, and destroying VMs, speeding up the development process.
  • Seamless integration: Works on top of virtualization platforms (like VirtualBox) and can also integrate with software provisioning tools and containerization platforms (like Docker), providing a unified interface for managing development environments.

Docker

Role

Containerization Platform

How It Works

Docker packages applications an their dependencies into containers, which are lightweight, standalone, and executable software packages. Containers ensure that software runs reliably when moved from one computing environment to another by encapsulating everything needed to run the software, including the code, runtime, system tools, libraries, and settings.

Key Contributions

  • Consistency Across Environments: Containers offer an environment-agnostic runtime, meaning that an application in a Docker container will run the same way on your development machine, a colleague’s laptop, a test server, or a production server, regardless of the underlying OS or infrastructure.
  • Resource Efficiency: Containers share the host system’s kernel rather than requiring an OS per VM, making them more lightweight and faster to start than traditional virtual machines.
  • Isolation: While VMs isolate at the hardware level, Docker provides isolation at the application level, allowing for more granular control over the runtime environment of applications.

Integrating VirtualBox, Vagrant, and Docker

[Host Operating System (e.g., Windows, macOS, Linux)]
|
|-- [VirtualBox]
|
|-- [VM: Ubuntu (managed by Vagrant)]
|
|-- [Docker (installed inside VM)]
|
|-- [Docker Container 1: e.g., Tomcat]
|-- [Docker Container 2: e.g., MySQL]

Step 1: Install VirtualBox

Download and install VirtualBox from the official website. Follow the installation instructions for your operating system.

Note: There’s no need to manually create a virtual machine in VirtualBox after the installation. The subsequent steps using Vagrant will handle the VM creation and configuration automatically.

Step 2: Install Vagrant

  1. Download: Navigate to the Vagrant downloads page and download the version for your operating system.
    Mac (macOS)
    -
    Intel Macs: Choose x86 or x64 versions, depending on whether your Mac uses a 32-bit (older models) or 64-bit processor (most Macs from the past decade).
    - Apple Silicon Macs (M1, M2, etc.): Opt for ARM64 or any version specifically labeled for Apple Silicon.
    Windows & Linux
    -
    32-bit: Select versions labeled as i686 or x86.
    - 64-bit: Look for versions marked as AMD64 or x64.
  2. Install: Execute the installer and complete the setup. Restart your computer to ensure that Vagrant commands are accessible from the command line.

Step 3: Initialize Vagrant Project

  1. Create Project Directory: Make a directory on your system where you want to manage your Vagrant environments.
  2. Initialize Vagrant: Open a command line interface, navigate to your project directory, and run vagrant init. This will create a default Vagrantfile in your project directory.
mkdir ubuntu-tomcat-docker
cd ubuntu-tomcat-docker
vagrant init

Step 4: Configure Vagrantfile for Docker

The Vagrantfile is the blueprint for your Vagrant box. It tells Vagrant how to configure the virtual machine, including what software to install and how to set it up. For Docker integration, you will specify instructions within this file to set up Docker on your virtual machine and then use Docker to run your applications.

Edit Vagrantfile: Open the Vagrantfile in a text editor of your choice. For command line editors, you can use nano or vim. Windows users with Git Bash can also use these commands:

nano Vagrantfile

Modify it to set up Docker within your VM. Your configuration should include commands to install Docker and to run a Docker container with the necessary ports exposed. The Vagrantfile will look something like this:

Vagrant.configure("2") do |config|
# Specify the base VM box
config.vm.box = "generic/ubuntu1804"

# Configure VirtualBox provider settings
config.vm.provider "virtualbox" do |vb|
vb.name = "Ubuntu-Tomcat"
vb.memory = "1024"
end

# Install Docker on the VM
config.vm.provision :shell, inline: <<-SHELL
apt-get update
apt-get install -y docker.io
SHELL

# Pull and run the Docker container with Apache Tomcat
config.vm.provision "docker" do |d|
d.pull_images "tomcat:9.0"
d.run "apache_tomcat",
image: "tomcat:9.0",
args: "-p 8080:8080"
end

# Extend the VM boot timeout setting to 600 seconds
# This can prevent timeouts during VM boot, especially on slower machines.
config.vm.boot_timeout = 600
end

This script tells Vagrant to create an Ubuntu 18.04 VM, install Docker, and run a container exposing port 8080.

In a Vagrantfile, the : introduces symbols (lightweight identifiers) used for specifying options, || are used to pass variables into code blocks for configuration, and = is for assigning values, much like setting options or properties.

  1. Base VM Configuration: Utilizes generic/ubuntu1804 as the base image for the VM, ensuring you don’t need to manually create a VM image.
  2. VirtualBox Provider Settings: Customizes the VM, setting its name to Ubuntu-Tomcat and allocating 1024 MB of memory.
  3. Docker Installation: Automatically installs Docker inside the VM using a shell provisioner that runs apt-get update and apt-get install -y docker.io.
  4. Docker Container Setup: Pulls the tomcat:9.0 image from Docker Hub and runs it as a container named apache_tomcat, forwarding port 8080 from the container to the host machine. This setup demonstrates Vagrant's capability to manage Docker containers directly, including pulling images and running containers, all defined within the Vagrantfile.

Step 5: Accessing Apache Tomcat

  1. Start the VM: Use the command vagrant up to start your VM and deploy the Docker container as configured.
  2. Access Tomcat: Once Vagrant has finished setting up the VM and Docker container, open a web browser and go to http://localhost:8080 to see the Tomcat server running. Vagrant handles port forwarding from the VM to your host machine.

Step 6: Managing the Environment

  • SSH Access: Enter the VM at any time with vagrant ssh.
  • Stop the VM: Halt the VM using vagrant halt.
  • Resume the VM: Restart a halted VM with vagrant up.
  • Destroy the VM: Completely remove the VM from your system with vagrant destroy.

By following these steps, you’ll leverage the benefits of VirtualBox, Vagrant, and Docker, establishing a streamlined workflow for consistent and isolated development environments.

Bonus Step: Explore the Tomcat Directory Structure

Once you’ve accessed your VM through vagrant ssh, it’s time to explore the Tomcat directory to understand where different elements of the Tomcat server are located.

  • Check Running Containers: Use the following Docker command to list running containers:
    Look for the container running Tomcat, which you’ve named apache_tomcat. You should see it listed as running and the ports correctly mapped.
docker ps
  • Navigate to the Tomcat Directory: The Docker container runs independently of the file system of the VM managed by Vagrant. To explore Tomcat’s directory, you’ll need to attach to the running Docker container:
    This command will open a Bash shell inside the apache_tomcat container.
docker exec -it apache_tomcat /bin/bash
  • Tomcat Directory Structure: Inside the container, Tomcat is usually installed in the /usr/local/tomcat director, but this can vary based on the image. Navigate to the Tomcat home directory:
    Here, you’ll find subdirectories like bin, conf, webapps, and logs which hold scripts, configuration files, deployed web applications, and logs, respectively.
cd /usr/local/tomcat
ls -l
  • Quick Tour
    - bin/: Contains Tomcat startup, shutdown scripts.
    - conf/: Houses configuration files like server.xml.
    - webapps/: The location where web applications are deployed.
    - logs/: Contains server logs, useful for troubleshooting.

This step provides a glimpse into the structure and components of a Tomcat server. For a detailed walk-through of each configuration file and directory, keep an eye out for the next post, which will focus exclusively on Tomcat configuration.

Bonus Step: Deploying Your Web Application to Tomcat

After familiarizing ourselves with the Tomcat directory structure and ensuring our Dockerized Tomcat instance is up and running, the next step is deploying our web application. Tomcat utilizes WAR (Web Application Archive) files, which are packaged web applications in a specific format that Tomcat can automatically deploy. The webapps directory is where Tomcat looks for these WAR files to deploy them.

For a Dockerized Tomcat setup managed by Vagrant, we use Vagrant’s synced folders feature to bridge the file system between your host machine and the virtual environment. This allows us to easily transfer the WAR file into the Docker container running Tomcat.

  1. Update your Vagrantfile for synced folders:
    First, specify a directory on your host machine to sync with the Vagrant VM. This enables easy transfer of the WAR file. Add the following line to your Vagrantfile:
Vagrant.configure("2") do |config|
config.vm.synced_folder "host/path/to/war", "/vm/path/to/war"
end
  • Host Path (host/path/to/war): This is the path on your physical machine (host) where your WAR file or files are located. This path needs to exist on your host machine because it's where you'll place the WAR files you want to sync to the VM.
  • VM Path (/vm/path/to/war): This path is where the host's directory will appear inside your Vagrant-managed VM. You don't need to manually create this path inside the VM. Vagrant ensures this directory is created and contains all the contents of the host's synced folder when the VM boots up.

My WAR file is located at "C:\Users\Jimin Byun\Source\camunda\dinner-dmn\target\dinner-dmn-0.1.0-SNAPSHOT.war". Here is a tailored setup.

Vagrant.configure("2") do |config|
# Specify the base box
config.vm.box = "generic/ubuntu1804"

# Sync the 'target' directory from your project on the host
# to '/vm_target' inside the VM. This path in the VM is automatically created.
config.vm.synced_folder "C:/Users/Jimin Byun/Source/camunda/dinner-dmn/target", "/deployments"

# Ensure your network and provider settings are configured as needed
# For example, forward the Tomcat port if necessary
config.vm.network "forwarded_port", guest: 8080, host: 8080

# Provider-specific configuration
config.vm.provider "virtualbox" do |vb|
vb.name = "Ubuntu-Tomcat-Docker"
vb.memory = "2048"
end

# Provisioning scripts or Docker setup can be added here
# Example: Install Docker on the VM if not using a Docker provisioner
end

2. Apply your Vagrantfile changes:
If your Vagrant VM is already running, reload it to apply the synced folder settings:

vagrant reload

If the VM isn’t running, simply start it with:

vagrant up

3. Transfer the WAR File to Tomcat:
SSH into your Vagrant VM:

vagrant ssh

Inside the VM, use Docker to copy the WAR file from the synced folder to the Docker container running Tomcat:

docker cp /vm/path/to/war/YourApp.war <container_name_or_id>:/usr/local/tomcat/webapps/

Ensure you replace <container_name_or_id> with your container's actual name or ID, which you can find using docker ps.

docker cp /deployments/dinner-dmn-0.1.0-SNAPSHOT.war apache_tomcat:/usr/local/tomcat/webapps/

This command copies the dinner-dmn-0.1.0-SNAPSHOT.war file from the synced folder /deployments in your Vagrant VM to the webapps directory of the Tomcat server running inside your apache_tomcat Docker container. Tomcat will automatically deploy the WAR file shortly after.

4. Verify Your Deployment:
Access your deployed application by visiting http://localhost:8080/dinner-dmn-0.1.0-SNAPSHOT from your browser. This step assumes that port 8080 is forwarded correctly from the Docker container to your host machine.

--

--

Jimin

DevOps engineer and tech enthusiast. Sharing tech insights to simplify the complex. Let's connect on LinkedIn! https://www.linkedin.com/in/byun-jimin/