Vagrant is a virtualization tool used to create development environments that can be consistently and easily re-produced among team members. Vagrant builds a Virtual Machine (VM) based on the specifications in the Vagrantfile. It uses ruby syntax.

Install Vagrant

On windows you can use:

winget install vagrant

Other methods are available on the official site.

Initialize a project

Create a new directory and inside it execute

vagrant init hashicorp/bionic64

This will create a Vagrantfile inside it.

Vagrant supports multiple Providers, which allow it to create virtual machines on different technologies: VirtualBox, VMware, Hyper-V. Many options are standardized, but for some there exists custom configuration. You can check the provider's documentation for more details.

Edit the Vagrantfile file and update the virtualbox section to this:

Vagrantfile

  config.vm.provider "virtualbox" do |vb|
     vb.memory = "1024"
     vb.cpus = 2
  end

We are ready to start our first machine. On the command line in the same folder as the Vagrantfile do:

vagrant up

When the script finishes you will have a new VirtualBox VM up and running and ssh port forwarded so that you can connect to it.

Connecting to the VM

Vagrant generates custom SSH keys for each VM.

To connect to it you can do:

vagrant ssh

Or if you want to use you own ssh client you can get the connection information with:

vagrant ssh-config

You can work inside your vm and then exit it.

After exiting you can vagrant halt, vagrant suspend or even vagrant destroy your VM.

Check the documentation for more commands.

It is useful to automate not only the creation of the VM, but also its initial configuration.

Make sure that the VM is not running with:

vagrant status

Configure basic provisioning

Currently our machine is behind a NAT network and only forwarded ports are accessible. We will switch to a private host-only network and give a static ip to the VM.

Vagrantfile

...
   config.vm.network "private_network", ip: "192.168.33.20"
...

Find the provision example in the file and adapt it to install nginx. The provision instruction can be inline shell commands or a path to a bash script.

Vagrantfile

...
config.vm.provision "shell", inline: <<-SHELL
     apt-get update
     apt-get install -y nginx
  SHELL
...

Start Virtual Machine

Upon starting Vagrant reconfigures the VM, adding the new network configuration.

vagrant up

However, since we already created the VM before, it will not automatically apply the provision script (to avoid running it multiple times).

To force the provisioning use:

vagrant provision

We can now access our nginx at http://192.168.33.20

Cleanup

If we now longer require the virtual machine we can delete it with:

vagrant destroy

You can use a Vagrantfile to configure multiple machines. Options on config.vm Object apply to all VMs, but you can define custom settings inside a define block to create multiple machines.

We will create a web and db machine on the same network to deploy the guestbook app with a separate PostgreSQL server.

Vagrantfile

Vagrant.configure("2") do |config|
  config.vm.box = "debian/bullseye64"
  config.vm.provider "virtualbox" do |vb|
     vb.memory = "1024"
     vb.cpus = 2
  end

  config.vm.define "db" do |db|
    db.vm.hostname = "db"
    db.vm.network "private_network", ip: "192.168.33.10"
    db.vm.provision "shell", path: "postgres-provision.sh"
  end

  config.vm.define "web" do |web|
    web.vm.hostname = "web"
    web.vm.network "private_network", ip: "192.168.33.11"

  end
end

In addition to the Vagrantfile create a new postgres-provision.sh file which installs PostgreSQL and configures it to accept external connections.

postgres-provision.sh

#!/bin/bash

sudo apt-get update && sudo apt-get -y install postgresql

# set the default to listen to all addresses
sudo sed -i "/port*/a listen_addresses = '*'" /etc/postgresql/13/main/postgresql.conf

# allow any authentication mechanism from any client
sudo sed -i "$ a host all all all trust" /etc/postgresql/13/main/pg_hba.conf

# create db named guestbook and its user
sudo su postgres -c "createdb guestbook"
sudo su postgres -c "psql -d guestbook -c \"create user guestbook with encrypted password 'guestbook'\""
sudo su postgres -c "psql -d guestbook -c \"grant all privileges on database guestbook to guestbook\""


# restart the service to allow changes to take effect
sudo systemctl restart postgresql

Web Application

Setting up the web server

Start by logging into the web VM.

vagrant ssh web

First try to manually deploy the guestbook app using the version inside the 02_mvp_modules_sqlite3 folder. By default it uses sqlite, to switch to PostgreSQL edit gbmodel/__init__.py and add a .env file with the required environment variables (look at .env.sample).

Test that everything is running on http://192.168.33.11:5000/

Automate Web Application Provisioning

It is your assignment to create the web web-provision.sh script and link it inside the Vagrantfile.

Complete the scripts with the commands you tested with the manual install

cat > filename << EOF
HELLO=world
EOF

Task Progress Check

Take a screenshot showing the guestbook with its url as well as the virtual machine instances listed in the virtualbox dashboard.

Destroy and up the web VM to test it.

Going further (optional): Can you make a full setup with nginx and gunicorn?