How to Use Vagrant for Virtual Machine Management

In this blog, we will manage the VMs using Vagrant

Disadvantages of Managing Virtual Machines Manually

  1. Time-Consuming Setup
    Manually creating virtual machines (VMs) involves several steps, including downloading an operating system, configuring the VM settings, and installing necessary software. This repetitive process is not only time-consuming but also error-prone.

  2. Inconsistent Environments
    When multiple team members create VMs independently, variations in configurations often lead to inconsistencies, making it difficult to replicate environments across teams.

  3. Error-Prone Configuration
    Misconfigurations, such as incorrect network settings or conflicting dependencies, are common and require manual troubleshooting, which can lead to downtime or extended setup periods.

About Vagrant

Vagrant helps us to automate the provisioning, configuration, and management of virtualized environments.

Key Features:
- Works with multiple providers like VirtualBox, VMware, Hyper-V, and cloud platforms.
- Uses a configuration file (Vagrantfile) to define and manage VM settings.
- Supports provisioning with tools like Ansible, Chef, Puppet, and shell scripts.

Advantages of Vagrant

  1. Rapid Environment Setup
    Vagrant allows for quick provisioning of development environments using predefined base images (boxes). This eliminates the repetitive manual setup process.

  2. Consistent Environments
    By using a single Vagrantfile shared across teams, Vagrant ensures every developer works in an identical environment, reducing the “works on my machine” problem.

  3. Supports Provisioning Tools
    Integration with configuration management tools like Ansible and Puppet allows for automated and repeatable setups, reducing manual effort.

How it Works

  1. Vagrant reads them Vagrantfile to understand the required environment specifications.
    vagrant init, to generate the vagrant file.

  2. It automates the download of base images (called boxes).

  3. The tool configures and provisions the environment, ensuring consistency and applicability.

Example: Create a Vagrantfile using a custom vagrant box for VirtualBox.

Pre-requisite steps…

Note: I have used Ubuntu 22.04 server image

  1. Create the VM using the ISO file.
    Create one VM manually, and install the server using an ISO file,
    For better practice, use LVM to create a disk so that we can increase the partition whenever required.

  2. Create user.
    Create a user that we use in the vagrant file configuration.
    adduser minex

  3. Add user to sudo file
    Update the sudoers file, and add the entry of the above user.
    echo "minex ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/minex

  4. Install all the required packages
    Installing the following packages in the server.
    apt install -y net-tools vim git

  5. Set the timezone
    ln -sf /usr/share/zoneinfo/Asia/Kolkata /etc/localtime

  6. Convert .vdi to .box format
    Packaging the existing VM’s .vdi into .box format
    vagrant package --base Testing --output default-server.box
    Added the .box file in the vagrant’s box list

    vagrant box add default-server/jammy64 default-server.box
    Verify
    vagrant box list

Creating the vagrant file

  1. Using YAML-based configuration file for Vagrantfile.
    We are going to use the YAML configuration file, where we define all the variables for the vagrant file.

     # syntax
     require 'yaml'
     config_data = YAML.load_file('config.yaml')
    

    Using, the above syntax to load the config file.

     # config.yaml file for Vagrantfile
     box_name: "default-server/jammy64"
     name: test
     hostname: test-server
     username: "minex"
     password: "098765"
     memory: "1024"
     cpu: 1
    
     networks:
       bridge: true
       hostonly: false
    
     bridge:
       type: dhcp
       ip: "192.168.0.116"
       gateway: "192.168.0.1"
    
     hostonly:
       use_existing: false
       type: dhcp
       ip: "192.168.59.103"
       network: "VirtualBox Host-Only Ethernet Adapter #2"
    

    Explanation:
    - We use key-value pairs and dictionary variables.
    - Based on the selection of the network, whether bridge or host-only, update the respective dictionary with appropriate values.
    - In the Host-only network, we also have the option to choose the existing network or create a new one.

  2. Using the above-created custom vagrant box.

     # syntax
     config.vm.box = "default-server/jammy64"
    
  3. Create the condition-based networking setup.

     # Configure a bridge network
     if config_data['networks']['bridge']
       # Debugging output
       puts "Bridge setting: #{config_data['networks']['bridge']}"
       public_network_options = if config_data['bridge']['type'] == "dhcp"
                           # Debugging output
                           puts "DHCP setting: #{config_data['bridge']['type']}"
                           {
                             type: config_data['bridge']['type']
                           }
                         elsif config_data['bridge']['type'] == "static"
                           {
                             type: config_data['bridge']['type'],
                             ip: config_data['bridge']['ip'],
                             gateway: config_data['bridge']['gateway'],
                             netmask: "255.255.255.0", # Default netmask
                             nameservers: ["8.8.8.8", "8.8.4.4"] # Default nameserver
                           }
                         end
    
       # Debugging output
       puts "Public Network options: #{public_network_options.inspect}"
       config.vm.network "public_network", **public_network_options
     end
    
     # Configure a host-only network
     if config_data['networks']['hostonly']
       # Debugging output
       puts "Host-only setting: #{config_data['networks']['hostonly']}"
       private_network_options = if config_data['hostonly']['use_existing']
                           {
                             type: 'static', # Default to static type
                             ip: config_data['hostonly']['ip'],
                             virtualbox__intnet: config_data['hostonly']['network']
                           }
                         else
                           # Debugging output
                           puts "Use existing hostonly: #{config_data['hostonly']['use_existing']}"
                           {
                             type: 'static', # Default to static type
                             ip: config_data['hostonly']['ip']
                           }
                         end
    
       # Add DHCP or static type logic
       if config_data['hostonly']['type'] == "dhcp"
         private_network_options.delete(:ip) # Remove static IP if type is DHCP
         private_network_options[:type] = "dhcp"
       end
    

    Explanation:
    - Here, we have used the IF condition to check, and only execute if the respective network is selected.
    - We also used a puts command to print the output for debugging purposes.

  4. Override the SSH settings.
    By default, Vagrant uses their own SSH private key to access and configure the VMs, but we are using our password-based login through Vagrant.

     # syntax
     # Override the SSH settings
     config.ssh.username = username  # Replace with your username
     config.ssh.password = password  # Replace with your password
     config.ssh.insert_key = false
    
  5. Other config in the Vagrantfile
    There are also other configs in the Vagrantfile, such as memory, CPU, graphic controller, USB controller, etc.

  6. Enable provisioning (optional)
    We used the shell provisioning in the Vagrantfile, to install the required packages.

Refer to the Github repo for the full code.

Create VM using Vagrant file.

git clone https://github.com/minex970/vagrant
cd vagrant/default-server
# create config file using sample file.
cp sample-config.yaml config.yaml
# update the config as per the requirement
# creating vm
vagrant up
# access the server
vagrant ssh

Destroy the VM
vagrant destory

Stop VM
vagrant halt

SSH config
vagrant ssh-config, to get the SSH config of the current vagrant project.

For troubleshooting:

Use the following option while running up and ssh command, which was very helpful for debugging purposes.
vagrant up --debug OR
vagrant up --debug-timestamp

Subscribe to our newsletter, for such blogs.

Refer to the following link: https://minex.hashnode.dev/benefits-of-choosing-iac-to-address-it-infrastructure-issues

Thank you for reading…