Vagrant is a great way of managing VMs for development, testing, or anything else you might be up to. Using a handy Vagrantfile, you can customise your virtual box and share the same specs with your team. Vagrant also has several handy provisioners to set up the box during the init stage.
But Vagrant is not limited to what you can do with a single box. It can handle advanced, multi-instance use-cases as well. You can use it to test master-slave replication by bringing up 2 machines. Or test your entire stack by connecting a web server, an API server, and a backend.
I’m going to create a master-slave network by bringing up 2 vagrant boxes and connecting them. My Vagrantfile looks like this:
Vagrant.configure("2") do |config|
config.vm.define "master" do |master|
master.vm.hostname = "mm-master"
master.vm.box = "ubuntu/hirsute64"
master.vm.network "private_network", ip: "192.168.20.10"
master.vm.provision "shell",
inline:"apt-get update; apt-get -y install nginx"
end
config.vm.define "slave_1" do |slave_1|
slave_1.vm.hostname = "mm-slave-1"
slave_1.vm.box = "ubuntu/hirsute64"
slave_1.vm.network "private_network", ip: "192.168.20.11"
slave_1.vm.provision "shell",
inline:"apt-get update; apt-get -y install nginx"
end
end
There are a few things happening in the code above:
- There are 2
config.vm.define
blocks: one for the master, and one for the slave. Within each block, we’re configuring the hostname, box image, IP address, and provisioning. - Each block has its own scope, so variables and configs defined in
master
have no effect inslave_1
. - The order of execution is outside -> in. First the outermost commands will get processed, then the ones deeper in.
When you run vagrant up
, you should see output this like:
Bringing machine 'master' up with 'virtualbox' provider...
Bringing machine 'slave_1' up with 'virtualbox' provider...
==> master: Importing base box 'ubuntu/hirsute64'...
==> master: Matching MAC address for NAT networking...
==> master: Checking if box 'ubuntu/hirsute64' version '20210820.0.0' is up to date...
==> master: Setting the name of the VM: a_master_1630213713542_21834
==> master: Clearing any previously set network interfaces...
==> master: Preparing network interfaces based on configuration...
master: Adapter 1: nat
master: Adapter 2: hostonly
==> master: Forwarding ports...
master: 22 (guest) => 2222 (host) (adapter 1)
==> master: Running 'pre-boot' VM customizations...
==> master: Booting VM...
==> master: Waiting for machine to boot. This may take a few minutes...
master: SSH address: 127.0.0.1:2222
master: SSH username: vagrant
master: SSH auth method: private key
master:
master: Vagrant insecure key detected. Vagrant will automatically replace
master: this with a newly generated keypair for better security.
master:
master: Inserting generated public key within guest...
master: Removing insecure key from the guest if it's present...
master: Key inserted! Disconnecting and reconnecting using new SSH key...
==> master: Machine booted and ready!
==> master: Attempting graceful shutdown of VM...
==> master: Booting VM...
==> master: Waiting for machine to boot. This may take a few minutes...
==> master: Machine booted and ready!
==> master: Checking for guest additions in VM...
==> master: Setting hostname...
==> master: Configuring and enabling network interfaces...
==> master: Mounting shared folders...
master: /vagrant => /private/tmp/a
==> master: Running provisioner: shell...
master: Running: inline script
...
==> slave_1: Importing base box 'ubuntu/hirsute64'...
==> slave_1: Matching MAC address for NAT networking...
==> slave_1: Checking if box 'ubuntu/hirsute64' version '20210820.0.0' is up to date...
==> slave_1: Setting the name of the VM: a_slave_1_1630213830494_36151
==> slave_1: Fixed port collision for 22 => 2222. Now on port 2200.
==> slave_1: Clearing any previously set network interfaces...
==> slave_1: Preparing network interfaces based on configuration...
slave_1: Adapter 1: nat
slave_1: Adapter 2: hostonly
==> slave_1: Forwarding ports...
slave_1: 22 (guest) => 2200 (host) (adapter 1)
==> slave_1: Running 'pre-boot' VM customizations...
==> slave_1: Booting VM...
==> slave_1: Waiting for machine to boot. This may take a few minutes...
slave_1: SSH address: 127.0.0.1:2200
slave_1: SSH username: vagrant
slave_1: SSH auth method: private key
slave_1:
slave_1: Vagrant insecure key detected. Vagrant will automatically replace
slave_1: this with a newly generated keypair for better security.
slave_1:
slave_1: Inserting generated public key within guest...
slave_1: Removing insecure key from the guest if it's present...
slave_1: Key inserted! Disconnecting and reconnecting using new SSH key...
==> slave_1: Machine booted and ready!
==> slave_1: Checking for guest additions in VM...
slave_1: The guest additions on this VM do not match the installed version of
slave_1: VirtualBox! In most cases this is fine, but in rare cases it can
slave_1: prevent things such as shared folders from working properly. If you see
slave_1: shared folder errors, please make sure the guest additions within the
slave_1: virtual machine match the version of VirtualBox you have installed on
slave_1: your host and reload your VM.
slave_1:
slave_1: Guest Additions Version: 6.0.0 r127566
slave_1: VirtualBox Version: 6.1
==> slave_1: Setting hostname...
==> slave_1: Configuring and enabling network interfaces...
==> slave_1: Mounting shared folders...
slave_1: /vagrant => /private/tmp/a
==> slave_1: Running provisioner: shell...
slave_1: Running: inline script
...
Vagrant Commands
Most of the Vagrant commands work for several boxes just as they do for a single one. The usual Vagrant commands work fine, and by default they work on all boxes at once. But some only work per box and need the name of whichever box you want to target.
vagrant ssh <machine-name>
: SSH alone won’t work any more. You’ll have to specify the name of the machine you want to ssh into.vagrant up
orvagrant up <machine-name>
: You can bring up all machines at once or one specific machine. There is also an option to specify which machines should come up when you doup
without a machine name.vagrant status
orvagrant status <machine-name>
: Show the status of all machines or a specific one.vagrant suspend
orvagrant suspend <machine-name>
: Pause all machines or a specific one.vagrant resume
orvagrant resume <machine-name>
: Resume all machines or a specific one.vagrant halt
orvagrant halt <machine-name>
: Shutdown all machines or a specific one.vagrant destroy
orvagrant destroy <machine-name>
: Destroy all machines or a specific one.vagrant snapshot <sub-command> <machine-name>
: Snapshot a single machine or the entire environment.
The official Vagrant doc on multi-machine setups has more info.