© 2019 Matt Thomas

Getting started with Vagrant

Matt Thomas - Full Stack Web Developer

St. Louis Full Stack Web Development Meetup - Nov. 2017

Follow along: https://goo.gl/9Emvb8

What is Vagrant?

Vagrant allows for the repeatable creation and management of virtual machine (VM) development environments.

The VM can contain numerous applications or systems in order to support development.

Created by HashiCorp

Vagrant supports many configuration management systems
including shell (Bash & Powershell), Ansible, Salt, Puppet & others.

Vagrant vs Docker

Docker is a container platform that is intended to run single processes or small applications.
Docker containers share the host OS but isolate the processes between containers.
(The host still has access to the container processes)
Docker images share layers thus reduces the storage required for duplicate containers.

If the lifecycle of your application lives in containers, stick with containers.


Vagrant VMs have a complete guest OS and full isolation...
including processes, file system & network interfaces from host system and other VMs.
Vagrant itself doesn't have an equivalent to Docker layers,
Providers such as VirtualBox provide Linked Clones.

If your application lifecycle ends on steel or in virtual machines then give Vagrant a try.

Can Vagrant and Docker co-exist? Sure, Docker is a Vagrant provider.

Installing Vagrant

and supporting software

Grab Vagrant from https://www.vagrantup.com
and install per instructions in Getting Started

Warning: Do NOT install using a package manager such as yum, apt or rpm.
The version will most likely be outdated and cause issues.

Install VM Provider

Make sure that VirtualBox, Docker, VMWare or Hyper-V is available.

VirtualBox is the default provider,
but VMWare is recommended as it provides better performance and stability.
I have never had any issues with VirtualBox stability, can't comment on performance.

Service providers such as
Rackspace, AWS (EC2 and VPC), Google & Linode
have custom providers.

Install Provisioning System

You may use shell scripts (I.E.: BASH), Ansible, Chef or Puppet

I have had good success using BASH scripts for personal project environments.
In an enterprise environment, a DevOps team might use a configuration management system.

More about this later...

Initialization of Project

Run the init command in the root of the project folder.
This will create a Vagrantfile in your project.


                    $ vagrant init [box]
                

                    $ vagrant init ubuntu/xenial64
                

The Vagrantfile is a Ruby script and can be updated in any programmic way you like as long as it returns the appropriate values.
There are examples of consuming JSON configuration files using Rudy and translating the JSON objects into the necesary Rudy values.

If you plan on using Puppet for provisioning you might consider PuPHPet, "a gui configurator for the Vagrant automation tool" to create your Vagrantfile & Puppet configuration.

Initial Vagrantfile


                    Vagrant.configure("2") do |config|
                        # Every Vagrant development environment requires a box. You can search for
                        # boxes at https://atlas.hashicorp.com/search.
                        config.vm.box = "base"
                    end
                

The Vagrantfile will initially have a lot of comments but only 1 line is initially important... "config.vm.box"

The initial configuration will not work as the provided box "base" doesn't exist.

Finding a Box

You have to search for a box from the Vagrant website that suits your needs.
(https://app.vagrantup.com/boxes/search)
You will find boxes available for various needs including...
full LAMP stacks, Rudy, Docker, Chef, Puppet.


                    $ vagrant box add "ubuntu/xenial64"
                

Popular boxes are "hashicorp/precise32" or "hashicorp/precise64" (Ubuntu 12.04)
Or "laravel/homestead" if you are a Laravel developer, you may have heard of it :P
I prefer "ubuntu/xenial64" (Ubuntu 16.04)

Vagrant Boxes are equivalent to Docker Images.

Update the Vagrantfile


                    Vagrant.configure("2") do |config|
                        # Every Vagrant development environment requires a box. You can search for
                        # boxes at https://atlas.hashicorp.com/search.
                        config.vm.box = "ubuntu/xenial64" # Ubuntu 16.04
                    end
                

You are now ready to start your Vagrant box with the image selected.

Additional Vagrantfile configuration options

  • Configuration and Software Provisioning
  • Network
  • Synced Folders
  • Provider Specific Configuration

Provisioning the Guest

Inline shell script provisioning


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

                        rm -rf /var/www/html
                        ln -fs /vagrant /var/www/html

                        systemctl restart apache2
                    SHELL
                

Associating a provisioning shell script


                    config.vm.provision "shell", path: "./scripts/update.sh"
                

Network Configuration


                    config.vm.hostname = "vagrant-intro-simple"

                    config.vm.network "forwarded_port", guest: 80, host: 8787
                

Multiple config.vm.network entries are allowed.

Synced Folders


                    config.vm.synced_folder "src/", "/srv/website"
                

By default, the root of your project on the host is shared to the guest at /vagrant.

Caveat: There is a VirtualBox bug related to sendfile which can result in corrupted or non-updating files. You should deactivate sendfile in any web servers you may be running.

                        In Nginx:
                        sendfile off;

                        In Apache:
                        EnableSendfile Off
                    

Provider Specific Configuration

Provider specific configurations can be contained within a conditional section to be only applied appropriately.


                    config.vm.provider "virtualbox" do |v|
                        # Overrides the name of the VM in VirtualBox
                        v.name = "test-vagrant"
                        v.linked_clone = true
                    end

                    config.vm.provider "vmware_fusion" do |v|
                        v.linked_clone = false
                        v.vmx["memsize"] = "1024"
                        v.vmx["numvcpus"] = "2"
                    end
                

When using linked clones 2 VMs will be created initially, 1 base VM and 1 cloned VM.
Additional cloned VM will only create a single additional VM.
The base VM will not be started, just used as a "data source", do NOT delete the base VM!

Managing the VM with Vagrant

Starting the VM


                    $ vagrant up
                

This will download the box image, if not cached, create the VM and start it in headless mode
The VM will be named after the folder the Vagrantfile exists appended by a timestamp.

Stopping the VM


                    $ vagrant halt
                

Other useful commands


                    $ vagrant status        # outputs status of the vagrant machine(s)

                    $ vagrant ssh           # connect to the machine using ssh

                    $ vagrant rdp           # connects to machine via RDP

                    $ vagrant provision     # provisions an existing machine

                    $ vagrant destroy       # remove VM - Danger Will Robertson!

                    $ vagrant validate      # validates the Vagrantfile
                

Managing Boxes


                    $ vagrant box               # manages boxes: installation, removal, etc.

                    $ vagrant box add           # add box to the local cache

                    $ vagrant box list          # list boxes in local cache

                    $ vagrant box outdated      # check if boxes in use are up to date

                    $ vagrant box prune         # removes old versions of cached boxes

                    $ vagrant box remove        # remove box from local cache

                    $ vagrant box repackage     # package a new box to local cache

                    $ vagrant box update        # update the box to the latest version
                

Simple Vagrantfile


                Vagrant.configure("2") do |config|
                    # set the box
                    config.vm.box = "ubuntu/xenial64"

                    # make the vm accessible from the host
                    config.vm.network "forwarded_port", guest: 80, host: 8787

                    # give it a unique hostname so we don't collide with other VMs with the same box
                    config.vm.hostname = "vagrant-intro-simple"

                    # do some stuff after the initial boot... i.e.: install apache and create a link to the source code
                    config.vm.provision "shell", inline: <<-SHELL
                    apt-get update
                    apt-get install -y apache2

                    rm -rf /var/www/html
                    ln -fs /vagrant /var/www/html

                    systemctl restart apache2
                    SHELL
                end
            

Multiple VM Vagrantfile


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

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

                        config.vm.define "web" do |web|
                            web.vm.hostname = "vagrant-intro-multi-web"
                            web.vm.network "forwarded_port", guest: 80, host: 8887
                            web.vm.network "private_network", ip: "10.0.0.10"

                            web.vm.provision "shell", path: "./scripts/apache.sh"
                        end

                        config.vm.define "db" do |db|
                            db.vm.hostname = "vagrant-intro-multi-db"
                            db.vm.network "private_network", ip: "10.0.0.20"

                            db.vm.provision "shell", path: "./scripts/mariadb.sh"
                        end
                    end
                

Provisioning Scripts - Apache


                    #!/usr/bin/env bash

                    echo "Installing Apache, PHP, xDebug & MariaDB client"
                    apt-get install -y apache2 libapache2-mod-php php php-mcrypt php-curl php-mysql php-xdebug mariadb-client-core-10.0
                    echo "Creating Apache html folder soft link"
                    rm -rf /var/www/html
                    ln -fs /vagrant /var/www/html

                    echo "Enabling mod_rewrite"
                    a2enmod rewrite
                    echo "Disabling sendfile as per vagrant suggestion"
                    cat <<- EOF >> /etc/apache2/apache2.conf

                    EnableSendfile Off
                    EOF

                    echo "Adding xDebug configuration"
                    cat <<- EOF >> /etc/php/7.0/mods-available/xdebug.ini
                    xdebug.remote_enable=1
                    xdebug.remote_port=9000
                    xdebug.remote_connect_back=1
                    xdebug.idekey=PHPSTORM
                    EOF

                    echo "Restarting Apache"
                    systemctl restart apache2
                

Provisioning Scripts - MySQL/MariaDB


                    #!/usr/bin/env bash
                    echo "Installing MariaDB"
                    apt-get install -y mariadb-server

                    echo "Creating vagrant user and vagrant_intro database"
                    mysql -u root -e "CREATE USER 'vagrant'@'10.0.0.10' IDENTIFIED BY 'password';
                    GRANT ALL PRIVILEGES ON * . * TO 'vagrant'@'10.0.0.10';
                    CREATE USER 'admin'@'%' IDENTIFIED BY 'password';
                    GRANT ALL PRIVILEGES ON * . * TO 'admin'@'%';
                    FLUSH PRIVILEGES;
                    CREATE DATABASE vagrant_intro;
                    CREATE TABLE vagrant_intro.users(id int(10) unsigned auto_increment primary key, username varchar(255) not null);"

                    echo "Updating bind address in /etc/mysql/mariadb.conf.d/50-server.cnf to 0.0.0.0 to allow external connections."
                    sudo sed -i "s/.*bind-address.*/\bind-address = 0.0.0.0/" /etc/mysql/mariadb.conf.d/50-server.cnf

                    echo "Restarting MySQL"
                    systemctl restart mysql
                

Integrations with Development Environments

Vagrant is integrated into numerous IDEs including Eclipse, Komodo and Jetbrains products.

Interacting with VirtualBox headless VMs

Most Vagrant VMs are headless, but can still be managed from VirtualBox.
It is not recommended though as any changes made in VirtualBox will not be reproducable.

Next Steps...

Questions

Additional Resources

Vagrant vs Docker: Which is better for WordPress development?
How is Docker different from a normal virtual machine?
Hashicorp Ecosystem

Feedback...

Print the presentation
Created with Reveal.js