Install VMs with Vagrant

A Vagrant project in order to install a couple of Virtual Machine on a server. It can be useful for set up a development environment or a test environment.

In this blog post, we will be discussing Vagrant, a powerful tool for managing virtual machines (VMs). With Vagrant, you can easily spin up multiple VMs with just a few commands, making it perfect for testing and development environments. We will demonstrate how to create a couple of VMs using VirtualBox as our provider.

To follow along with this tutorial, you should have both Vagrant and VirtualBox installed on your system. If you haven't installed them yet, you can find the installation instructions here:

Directory Structure

For this tutorial, we will be using the following directory structure:

.
├── Vagrantfile
└── install_common.sh

The Vagrantfile is the main configuration file for Vagrant, and the install_common.sh file contains any additional setup. We will be using this file to install common packages on both the master and slave VMs.

Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.



# Configurable variables

## Common configuration

boxVersion = "ubuntu/focal64"

## Master Configuration

masterName = "dmaster"
masterIp = "192.168.56.10"
masterCpus = 2
masterMemory = 2048

## Node Configuration

numberOfNodes = 2
nodeNamePrefix = "dnode"
nodeIpPrefix = "192.168.56.1"
nodeCpus = 1
nodeMemory = 1024

# Main Script

Vagrant.configure("2") do |config|
  # master server
  config.vm.define masterName do |dmaster|
    dmaster.vm.box = boxVersion
    dmaster.vm.hostname = masterName
    dmaster.vm.box_url = boxVersion
    dmaster.vm.network :private_network, ip: masterIp
    dmaster.vm.provider :virtualbox do |v|
      v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
      v.customize ["modifyvm", :id, "--memory", masterMemory]
      v.customize ["modifyvm", :id, "--name", masterName]
      v.customize ["modifyvm", :id, "--cpus", masterCpus]
    end
    config.vm.provision "shell", inline: <<-SHELL
      sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/g' /etc/ssh/sshd_config    
      service ssh restart
    SHELL
    dmaster.vm.provision "shell", path: "install_common.sh"
  end

  (1..numberOfNodes).each do |i|
    nodeName = nodeNamePrefix + i.to_s
    nodeIp = nodeIpPrefix + i.to_s
    config.vm.define nodeName do |dnode|
      dnode.vm.box = boxVersion
      dnode.vm.hostname = nodeName
      dnode.vm.network "private_network", ip: nodeIp
      dnode.vm.provider "virtualbox" do |v|
        v.name = nodeName
        v.memory = nodeMemory
        v.cpus = nodeCpus
      end
      config.vm.provision "shell", inline: <<-SHELL
        sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/g' /etc/ssh/sshd_config    
        service ssh restart
      SHELL
      dnode.vm.provision "shell", path: "install_common.sh"
    end
  end
end

This Vagrantfile will install and configure 1 Master VM and 2 Worker VMs. Here's a breakdown of the Vagrantfile:

  • The file starts by defining configurable variables, which allow you to customize the VM configurations, such as the VM's base box, CPU, memory, and IP address.
  • The Vagrant.configure("2") do |config| block is where the main configuration takes place.
  • The master VM is defined with the name dmaster, and its settings are applied within the config.vm.define masterName do |dmaster| block.
  • The worker VMs are created using a loop that iterates over the specified number of nodes (numberOfNodes). Each worker VM is assigned a unique name and IP address based on the nodeNamePrefix and nodeIpPrefix variables.
  • For each VM, the box version, hostname, and network settings are configured. Additionally, the provider-specific settings, such as memory and CPU allocation, are applied within the dnode.vm.provider "virtualbox" do |v| block.
  • Lastly, a shell provisioner is used to update the SSH configuration to enable ChallengeResponseAuthentication and restart the SSH service. The install_common.sh script is also executed, which you can customize to include any additional setup or installation steps.

install_common.sh


#!/bin/bash

## install common 


HOSTNAME=$(hostname)
IP=$(hostname -I | awk '{print $2}')
echo "START - install common - "$IP

echo "Add host name for ip"
host_exist=$(cat /etc/hosts | grep -i "$IP" | wc -l)
if [ "$host_exist" == "0" ];then
echo "$IP $HOSTNAME " >/etc/hosts
fi


echo "END - install common - " $IP

Creating the VMs

To create the VMs, follow these steps:

  • Create a new directory for your project and place the Vagrantfile and the install_common.sh files in it.
  • Open a terminal or command prompt and navigate to the directory containing the Vagrantfile.
  • Run the command vagrant up. Vagrant will download the required box (if not already cached) and create the master and worker VMs as specified in the Vagrantfile.

Accessing the VMs

Once the VMs are up and running, you can access them using the ssh command followed by the VM ip, for example:

  • To access the master VM: ssh vagrant@192.168.56.10 (The default password is vagrant)
  • To access the first worker VM: ssh vagrant@192.168.56.11 (The default password is also vagrant)
  • You can also verify the status of your VMs by running the vagrant status command.
  • You can stop the VMs by running the vagrant halt command.
  • You can destroy the VMs by running the vagrant destroy command.

Good Job! 🍾

In this blog post, we have demonstrated how to create multiple VMs using Vagrant and VirtualBox. By using a simple Vagrantfile, we can quickly create and manage VMs for testing and development purposes. Vagrant provides a powerful, flexible, and efficient way to manage your virtual environments.