In this blog post, we will demonstrate how Vagrant can simplify the provisioning and configuration workflow for virtual machine environments, and look at concepts such as shared folders, networking and port forwarding as well as provisioning options.
Vagrant is suitable as interface between virtualization software (VirtualBox, Hyper-V, Docker, VMWare, AWS, ...) and configuration or automation tools such as Puppet, Chef or SaltStack.
As such Vagrant simplifies creating and maintaining portable virtual software development environments.
As described in the introduction, Vagrant is used to provision various virtualization software sitting on top of it. For the purpose of this blog post, we have chosen VirtualBox, which we will install as a prerequisite for Vagrant.
Below we will discuss the installation of VirtualBox and Vagrant under Linux Debian, Linux CentOS/RHEL7, MacOS and Windows.
2.1.1 VirtualBox
$ sudo apt-get update $ sudo apt-get install virtualbox
2.1.2 Vagrant
$ sudo apt-get install vagrant
2.2.1 VirtualBox
$ sudo cd /etc/yum.repos.d/ $ sudo wget http://download.virtualbox.org/virtualbox/rpm/rhel/virtualbox.repo $ sudo yum update $ sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm $ sudo yum install binutils gcc make patch libgomp glibc-headers glibc-devel kernel-headers kernel-devel dkms $ sudo yum install VirtualBox-5.2
2.2.2 Vagrant
$ sudo yum -y install ruby $ sudo gem install vagrant
2.3.1 VirtualBox
$ sudo brew cask install virtualbox
2.3.2 Vagrant
$ sudo brew cask install vagrant
2.4.1 VirtualBox
https://www.virtualbox.org/wiki/Downloads
2.4.2 Vagrant
box
(add, remove, etc.) boxes global-status
returns the status of all active Vagrant environments on the systemhalt
shuts down the running machine(s) that Vagrant managesinit
initializes the current directory by generating an initial Vagrantfile.provision
executes any configured provisioners against the running machines managed by Vagrant.reload
(equals halt && up
): usually required for changes made to the Vagrantfile to take effectssh
starts an SSH session in a running Vagrant machine and allows access to its shellup
creates and configures guest machines in accordance to the VagrantfileThe Vagrantfile describes among other things the machine type required for a project, as well as settings / instructions concerning the configuration and provisioning of these machines.
As a rule, one Vagrantfile is created for each project, which should be versioned - e.g. via GiT. This enables other developers involved in the project to read out the code, execute Vagrant and thus already have the project up-and-running.
The syntax of the Vagrantfile is Ruby, but knowledge of Ruby programming is not absolutely necessary in order to make changes to the Vagrantfile or to understand its structure, since these are mostly simple variable assignments.
Vagrant requires at least a definition of the image to be used for the box.
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" end
If vagrant up
is now executed in the project directory (in which the Vagrantfile is located), Vagrant will already create a box with Ubuntu as operating system.
In the simplest case, one Vagrantfile is used to define configuration and provisioning parameters for a single guest machine. Often the infrastructure of a project consists of several machines, which should be known to each other or be able to communicate.
The following is an example of a quite simple Vagrantfile, which contains descriptions for several guest machines:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" end end
Often the project contains files / folders / executables, which have to be transferred to the guest machine. Of course, these could be transferred via
We extend the above multi-machine Vagrantfile to share the folder on the db
guest machine where the database files are stored between host and guest.
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" end end
Usually, once a guest machine has been created and started up via Vagrant, it is further provisioned, software is delivered, installed and configured, and it is ensured that the corresponding services are running.
For such purposes, Vagrant can be combined with automation tools such as Puppet, Ansible, Chef or SaltStack. In order not to go beyond the scope of this blog post, we will limit ourselves to two possibilities which Vagrant itself offers: inline-provisioning via directly stating the corresponding commands in the Vagrantfile, and provisioning by executing, for example, an appropriately prepared shell script.
We want to install a Postgres database server on the db
guest machine:
4.4.1 Inline-Provisioning
For this we use the option vm.provision
:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" db.vm.provision "shell", inline: "sudo apt-get update && sudo apt-get install -y postgresql" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" end end
4.4.2 Provisioning via Shell-Script
Alternatively, we can also move the commands into a shell script and execute that script in its entirety. This makes sense with more extensive commands compared to inline provisioning, especially in regards to readability and maintainability. Again the vm.provision
option is used, and the relative path to the corresponding shell script is specified on the host machine:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" db.vm.provision "shell", path: "provision-db.sh" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" end end
Vagrant provides some high-level networking options, such as port forwarding, connecting to a public network, or creating a private network. These options also work as abstraction layers across multiple providers (VirtualBox, VMWare, ...).
4.5.1 Private Networks
Vagrant private networks allow you to access the guest machine via an IP address that is not publicly accessible from the global Internet.
Several guest machines within a private network can communicate with each other, which is especially advantageous for multi-machine setups.
The easiest way to create a private network is to assign a private IP address via DHCP using the vm.network
option and the private_network
identifier:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.network "private_network", type: "dhcp" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" db.vm.provision "shell", path: "provision-db.sh" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" web.vm.network "private_network", type: "dhcp" end end
Sometimes, however, it is advantageous to define the IP address statically in advance. This is also possible via vm.network
and private_network
identifier:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.network "private_network", ip: "10.0.2.1" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" db.vm.provision "shell", path: "provision-db.sh" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" web.vm.network "private_network", ip: "10.0.2.2" end end
4.5.2 Public Networks
Unlike private networks, Vagrant public networks can be publicly accessible from the global Internet.
Again, the simplest way is to assign a dynamic IP address via DHCP using the vm.network
option and the public_network identifier:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.network "public_network" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" db.vm.provision "shell", path: "provision-db.sh" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" web.vm.network "public_network" end end
And an example using a static IP address:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.network "public_network", ip: "192.168.0.1" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" db.vm.provision "shell", path: "provision-db.sh" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" web.vm.network "public_network", ip: "192.168.0.2" end end
If more than one network interface is available on the host machine, you can define the desired interface to which the guest machine should build a bridge by adding a :bridge
clause as follows:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.network "public_network", bridge: "en0" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" db.vm.provision "shell", path: "provision-db.sh" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" web.vm.network "public_network", bridge: "en0" end end
Sometimes it is necessary to redirect a port of the host machine to a corresponding port in a Vagrant guest machine. Let's say our webserver runs virtualized in a corresponding Vagrant guest machine on port 80
; we will most likely want to forward port 80
of the host machine accordingly.
This also makes sense for local development environments; for example, port 8080
of the host machine could be forwarded to port 80
of the guest machine, which allows the developer to access the virtualized web server in the local browser at http://localhost:8080
.
We implement the latter in our Vagrantfile from above:
Vagrant.configure("2") do |config| # database server config.vm.define "db" do |db| db.vm.box = "ubuntu/bionic64" db.vm.synced_folder "./data/", "/usr/local/pgsql/data/" db.vm.provision "shell", path: "provision-db.sh" end # web server config.vm.define "web" do |web| web.vm.box = "ubuntu/bionic64" web.vm.network "forwarded_port", guest: 80, host: 8081 end end
Tags
Table of Content
Comments
No comments yet