Setup Build Environment with Vagrant

2 minute read

As a system engineer, I have to manage various build envrionmets, build all the projects against one Ubuntu version became imposssible, one change for one project may break the build for another, Vagrant wass born to make this easier.

Vagrant supports many Linux distributions and also works on macOS.

To install vagrant on macOS, issue following command:

brew install vagrant virtualbox

or this for Ubuntu:

sudo apt install -y vagrant virtualbox

To get started, follow the steps below to create a virtual machine for Ubuntu 18.04:

  1. Create Vagrantfile
    vagrant init ubuntu/focal64
    

    This will create a Vagrantfile under current directory, this Vagrantfile may looks like this (with comments removed):

    Vagrant.configure("2") do |config|
      config.vm.box = "ubuntu/focal64"
    end
    

    Vagrantfile also contains some comments explaining how to configure this vm, for example we can install required packages and do environment setups, within this file.

  2. Bring up VM
    vagrant up
    
  3. ssh into VM
    vagrant ssh
    

For security research, you can use kalilinux with vagrant:

vagrant init kalilinux/rolling
vagrant up
# root/toor

Sharing file/folder with VM

By default, vagrant shares the current directory to the /vagrant directory of the VM:

vagrant@vagrant:~$ ls /vagrant/
Vagrantfile

If you need to share more directories with VM, configure it in Vagrantfile with:

  config.vm.synced_folder "/opt/shared", "/tmp/jenkins"

Copy file/folder with VM

Copy files or directories to VM can be done with File Provisioner:

  config.vm.provision "file", source: "/tmp/Dockerfile", destination: "/tmp/docker/Dockerfile"
  config.vm.provision "file", source: "~/path/to/host/folder", destination: "$HOME/remote/newfolder"

Create docker image in VM

Docker image can be built directly in VM with Docker Provisioner:

  config.vm.provision "docker" do |d|
    d.build_image "/tmp/docker", args: "-t ubuntu:1804"
  end

Bridged network

Remote access to node created by vagrant with ssh maybe required sometimes, for this case, we need to create a bridged network for the VM:

  config.vm.network "public_network", bridge: "enp54s0"

Simple example of Vagrantfile

This is the Vagrantfile file I used for creating jenkins node:

cat Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.hostname = "node"
  config.vm.box = "ubuntu/focal64"
  config.vm.network "public_network", bridge: "enp54s0"
  config.vm.synced_folder "/opt/shared", "/tmp/jenkins"
  config.vm.provision "file", source: "/tmp/Dockerfile", destination: "/tmp/docker/Dockerfile"
  config.vm.provision "docker" do |d|
    d.build_image "/tmp/docker", args: "-t ubuntu:1804"
  end
  config.vm.provider "virtualbox" do |vb|
    vb.gui = false
    vb.memory = "1024"
    vb.cpus = "1"
  end
end

Work with Ansible

Development envrionment setup can be done with the ansible_local provision, add

  config.vm.provision :ansible_local do |ansible|
    ansible.playbook    = "playbook.yml"
    ansible.verbose     = true
  end

to Vagrantfile, and create this example playbook:

- name: Ansible apt install
  become: yes
  hosts: all
  tasks:
    - name: Environment Setup
      apt:
        update_cache: yes
        name:
          - zsh
          - ripgrep
          - thefuck
          - fd-find
          - moreutils
          - docker.io
          - docker-compose
        state: present

The last step:

vagrant reload --provision

Execute remote commands with ssh

In case of the need for execute commands without ssh into the vm, do it like this:

vagrant ssh -c 'ls /'
vagrant ssh -- -t 'ls /'

For a real world example, below is the command I used for bootchart generation in workload perfboot for boot time benchmark with workload automation.

vagrant ssh -c "bootchart -f svg --show-pid -o /vagrant/bootchart.svg /vagrant/bootlog.tgz"

One-liners

vagrant up
vagrant up --provision           # Starting the VM
vagrant ssh
vagrant reload
vagrant reload --debug
vagrant reload --provision       # Restarting the VM
vagrant destroy
vagrant halt
vagrant status
vagrant box list
vagrant box update
vagrant box remove hashicorp/bionic64
vagrant box remove generic/ubuntu2004 --box-version 3.6.8
vagrant global-status | awk '/running/{print $1}'