vagrant
Table of Content
Vagrant¶
Vagrant enables the creation and configuration of lightweight, reproducible, and portable development environments
I use Vagrant with Hyper-V to quickly prepare a test environment.
https://developer.hashicorp.com/vagrant/docs/providers/hyperv/limitations
Two limitations to note first:
- cannot configure nic using Vagrantfile
- cannot specify which virtual switch to use using Vagrantfile
There must be DHCP running on LAN, and the VM created by Vagrant will configure its NIC via DHCP through the virtual switch of the host Windows machine. Which virtual switch to use will be asked when Vagrant builds each VM.
installation¶
https://developer.hashicorp.com/vagrant/tutorials/getting-started/getting-started-install
# search and confirm the ID and version
winget search vagrant
# install
winget install Hashicorp.Vagrant
# confirm the installation
vagrant --version
quick start¶
# create a directory on desired drive
Set-Location F:\
mkdir vagrant
cd vagrant
mkdir test
cd test
# init using vagrant officially recommened ubuntu image, eol in 2023
vagrant init hashicorp/bionic64
# edit Vagrantfile
Vagrantfile¶
It's a ruby file to configure Vagrant. vagrant init
command will generate a Vagrantfile
to start working with.
# -*- 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.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.box = "hashicorp/bionic64"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Disable the default share of the current code directory. Doing this
# provides improved isolation between the vagrant box and your host
# by making sure your Vagrantfile isn't accessible to the vagrant box.
# If you use this you may want to enable additional shared subfolders as
# shown above.
config.vm.synced_folder ".", "/vagrant", disabled: true
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
#
# Hyper-V
config.vm.provider "hyperv" do |h|
h.vmname = "testubuntu"
h.cpus = 2
h.memory = 2048
h.enable_virtualization_extensions = true
h.linked_clone = true
h.vm_integration_services = {
guest_service_interface: true,
heartbeat: true,
shutdown: true,
time_synchronization: true,
key_value_pair_exchange: true,
}
end
# Enable provisioning with a shell script. Additional provisioners such as
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# apt-get update
# apt-get install -y apache2
# SHELL
end
build VM using Vagrant¶
Run vagrant up
to start the VM build. Vagrant asks to choose the virtual switch to use. I select "External VM Switch" I created when setting up Hyper-V here.
box¶
There seem to be many up-to-date boxes uploaded by "roboxes" with "generic" namespace such as "generic/ubuntu2210". See Roboxes Website here.
multiple VM¶
https://developer.hashicorp.com/vagrant/docs/multi-machine
https://developer.hashicorp.com/vagrant/docs/vagrantfile/tips#loop-over-vm-definitions
Multiple VMs can be build with a single Vagrantfile
, vagrant up
command.
I will create a set of 5 Debian VMs to create a Kubernetes cluster.
Here is the Vagrantfile
to build VMs named vcp and vworker1-4 using "generic/debian12" box.
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
# common config
config.vm.provider "hyperv"
config.vm.network "public_network"
config.vm.synced_folder ".", "/vagrant", disabled: true
config.vm.box = "generic/debian12"
# control plane
config.vm.define "vcp", primary: true do |vcp|
vcp.vm.hostname = "vcp"
end
# worker node
(1..4).each do |i|
config.vm.define "vworker#{i}" do |node|
node.vm.hostname = "vworker#{i}"
end
end
# provider
config.vm.provider "hyperv" do |h|
h.cpus = 2
h.memory = 2048
h.enable_virtualization_extensions = true
h.linked_clone = true
h.vm_integration_services = {
guest_service_interface: true,
heartbeat: true,
shutdown: true,
time_synchronization: true,
key_value_pair_exchange: true,
}
end
end
Here is the result.
╰─❯ get-vm
Name State CPUUsage(%) MemoryAssigned(M) Uptime Status Version
---- ----- ----------- ----------------- ------ ------ -------
k8s_vcp_1707806419070_95468 Running 0 2048 00:13:35.7240000 Operating normally 9.0
k8s_vworker1_1707806649152_50802 Running 0 2048 00:09:45.7320000 Operating normally 9.0
k8s_vworker2_1707806752033_33478 Running 0 2048 00:08:02.9010000 Operating normally 9.0
k8s_vworker3_1707806821736_75591 Running 0 2048 00:06:52.9660000 Operating normally 9.0
k8s_vworker4_1707806890423_84706 Running 0 2048 00:05:43.8540000 Operating normally 9.0
╰─❯ vagrant status
Current machine states:
vcp running (hyperv)
vworker1 running (hyperv)
vworker2 running (hyperv)
vworker3 running (hyperv)
vworker4 running (hyperv)
This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.
snapshot¶
# save
vagrant snapshot save {vm name} {snapshot name}
# list
vagrant snapshot list {vm name}
# restore
vagrant snapshot restore {vm name} {snapshot name}
Example.
# save
╰─❯ vagrant snapshot save vworker4 init
==> vworker4: Snapshotting the machine as 'init'...
==> vworker4: Snapshot saved! You can restore the snapshot at any time by
==> vworker4: using `vagrant snapshot restore`. You can delete it using
==> vworker4: `vagrant snapshot delete`.
# list
╰─❯ vagrant snapshot list
==> vcp:
init
==> vworker1:
init
==> vworker2:
init
==> vworker3:
init
==> vworker4:
init
# restore
╰─❯ vagrant snapshot restore vworker4 init
==> vworker4: Attempting graceful shutdown of VM...
==> vworker4: Restoring the snapshot 'init'...
==> vworker4: Starting the machine...
==> vworker4: Waiting for the machine to report its IP address...
vworker4: Timeout: 120 seconds
vworker4: IP: 192.168.1.40
==> vworker4: Waiting for machine to boot. This may take a few minutes...
vworker4: SSH address: 192.168.1.40:22
vworker4: SSH username: vagrant
vworker4: SSH auth method: private key
==> vworker4: Machine booted and ready!
==> vworker4: Setting hostname...