Building the Vagrantfile
In the last post we looked into how to install Vagrant, took at brief look the Vagrantfile, a look at atlas, brought up a box and finally destroyed it. In this post we’ll look at some of the more advanced ‘Vagrantfile’ configurations to help you make the most out of your lab environments.
As you can guess the most important part of Vagrant is the ‘Vagrantfile’, so in this post we’ll take a look at some the options available.
As always a video covering all of the topics outlined has been included at the bottom of this post.
Unless you just need to spin up a machine to test a couple of commands, chances are, you will want to launch multiple systems. Let’s take a look at how you can launch multiple machines from a single ‘Vagrantfile’
Starting with the default ‘Vagrantfile’ in a text editor lets focus on the two config lines:
Vagrant.configure(2) do |config| config.vm.box = "base"
The first line, notes the beginning of the configuration file. The most important part being the word ‘config’ sandwiched between two pipes, like so |config|. The reason this is important is that any word between the pipes, operates as a variable that can be referenced by subsequent entries. When we look at the second line we see it starts with the variable of ‘config’ linking the box referenced on the second line to the configuration file in the first line.
This means in order to create multiple machines you simply nest your multiple systems within the configuration file. If that doesn’t make sense, hopefully it will in a second. Lets take an example, we want to create a new machine called ‘Web01’, so to do that we will take our config line
and instead of pointing to a box, we’ll define another variable explicitly for web01, like so:
config.vm.define "web01" do |web01|
web01.vm.box = "ubuntu/trusty64"
Putting that all together, you end up with the following configuration:
Vagrant.configure(2) do |config| config.vm.define "WebServer01" do |web01| web01.vm.box = "ubuntu/trusty64" end
NOTE: I prefer to add a line between the start of the configuration file and the box just to make it easier to read, but it is not necessary. Equally in large files it pays to add comments with a description of the devices purpose.
In order to add additional machines you simply add an additional set of config variables. For example to add a second servers called db01, the ‘vagrantfile’ would look like so:
Vagrant.configure(2) do |config| config.vm.define "WebServer01" do |web01| web01.vm.box = "ubuntu/trusty64" end config.vm.define "DatabaseServer01" do |db01| db01.vm.box = "ubuntu/trusty64" end end
To add more servers simply continue with the same process of adding a new config definition and variable. Hopefully you can now see what I mean by nesting machines within a configuration.
After you have added multiple machines the next question becomes how do you start them? In our previous post with one machine we simply typed ‘vagrant up’ and away we went, with multiple machines configured, this is still OK, but it will boot all boxes listed. In some circumstances if you have several systems you may not want to boot all of the machines at once, if that’s the case you can simply launch one system by specifying the variable name
vagrant up <name> e.g.
vagrant up web01.
Which will only launch machine ‘web01’
The same goes for accessing the system. With SSH being the primary way to access systems, typing vagrant ssh will generate an error message from vagrant saying that it is not specific enough, so you should instead favour explicitly specifying the machine you wish to access, such as vagrant ssh <name>.
Adding a hostname comes in handy when using multiple machines, that way when you connect into a system you will see that you are on the right box as opposed to listing the default hostname. To specify a unique hostname under the relevant machine simply add the line <variable>.vm.hostname = “<hostname>” like so:
Vagrant.configure(2) do |config| config.vm.define "WebServer01" do |web01| web01.vm.box = "base" web01.vm.hostname = "web01" end
With multiple machines, the next thing you need to focus on after defining them, is to determine how they are going to communicate, which brings us on to Networking.
As you might imagine this refers to the RFC1918 address space, where you can assign any non-routable IP address. One thing to point out is that it pays to keep away from the first and last IP in each range as these tend to be auto assigned for internal systems. That’s not to say you can’t use them, but you will most likely receive a warning message from Vagrant.
What about public IP addresses? in niche cases you may want to use public IPs though I struggle to see a requirement for this as vagrant boxes are fundamentally not secure, so exposing them to the Internet is probably not the best idea. However if you need to do this refer to the online documentation for more info.
For private addressing you have two approaches; assign the IP address yourself or allow it to be assigned via DHCP.
DHCP is the most convenient, though it does rely on you logging into the machine to identify the IP address, which can be a bit tiresome in a lab, so I tend to favour using static IP addresses. That said, to configure DHCP addresses simply configure the following:
Vagrant.configure(2) do |config|
config.vm.define “WebServer01” do |web01|
web01.vm.box = “base”
web01.vm.hostname = “web01”
web01.vm.network “private_network”, type: “dhcp”
If like me you prefer static IPs you can use the following config, the ‘netmask’ is entirely optional, but I prefer to know exactly what’s what.
Vagrant.configure(2) do |config| config.vm.define "WebServer01" do |web01| web01.vm.box = "base" web01.vm.hostname = "web01" web01.vm.network "private_network", ip: "10.1.1.11", netmask: "24" end config.vm.define "DatabaseServer01" do |db01| db01.vm.box = "ubuntu/trusty64" db01.vm.hostname = "web01" db01.vm.network "private_network", ip: "10.1.1.21", netmask: "24" end
Port forwarding allows vagrant to alter ports between you guest VM and your local host machine. This can be helpful if you have multiple platforms using the same IP address. An example is shown below:
Vagrant.configure(2) do |config| config.vm.define "WebServer01" do |web01| web01.vm.box = "base" web01.vm.hostname = "web01" web01.vm.network "forwarded_port", guest: 80, host: 8080 end end In this example, our local machine uses port 8080 whereas our guest responds on port 80.
One of my favourite features is that you can sync folders between your vagrant machine and your local host via the /vagrant directory. Any files in this folder will be accessible from any of your vagrant machines and your local host. You can also create additional shared folders if required.
Why is this good? well, it means that instead of being stuck with ‘nano’ or ‘vi’ you can use your local text editor to alter files. It also means you can push files directly into the vagrant environment without having to use a protocol to transfer files.
How do you use it? to transfer files from your local host to the guest simply add the files to the /vagrant directory or sub-directory, depending on how complex your environment is. Equally to share folders within the guest VM I find using symbolic links works well. For example to link the web root directory to the vagrant directory you could use a link within the machine such as
ln -fs /vagrant /var/www
When you start a box, you will be presented with the system as it was saved, bar any alterations you have applied such as hostname and networking. This means you will still need to install all of the applications you desire after logging in.
If you are familiar with Bash scripting then you can use this to your advantage, by provisioning your system and installing applications ahead of time!
The first step is to create your bash script, the example below we#’ll create a basic script called bootstrap.sh’ that will install apache web server for a Debian based system and create a symbolic link from the web root directory to the vagrant directory to allow file access.
#!/usr/bin/env bash apt-get update apt-get install -y apache2 ln -fs /vagrant /var/www
To use the script all you need to do is save it to the same directory as your ‘Vagrantfile’ and add the following line reference the script.
Vagrant.configure(2) do |config| config.vm.define "WebServer01" do |web01| web01.vm.box = "base" web01.vm.hostname = "web01" web01.vm.network "forwarded_port", guest: 80, host: 8080 web01.vm.provision :shell, path: "bootstrap.sh" end end
If you have multiple scripts you can of course create sub-directories and then reference them in the ‘Vagrantfile’.
If you decide to add a script or alter your Vagantfile configuration after your system has been booted, you could simply destroy your machine and rebuild it. Of course you may already have applied some ad-hoc configuration that you haven’t added to your script. A better approach might instead be to simply reload you machine and request it to re-read the vagrantfile which can be done using the following command:
vagrant reload <name> --provision
Finally a quick look at some of the commands you can use to verify the status of your Vagrant boxes.
To see boxes have been downloaded and installed locally into vagrant you can use the command:
vagrant box list
This will provide a list of all the boxes as well as their versions. On occasion new versions of boxes will be released and you’ll be presented with a reminder of this should you boot a box that is out of date. If you wish to be proactive about this however you can run the command:
vagrant box outdated --global
to review all of the boxes and search for updates. If updates are available you can then download them by running the following command:
vagrant box update
In the next post on Vagrant we’ll take a more detailed look at Atlas and how you can share your boxes.
Video Walk Through
A bit longer than usual I’m sorry to say and I had to rush through it so not as polished as I would have liked.