Introduction to Vagrant and Packer
This post features a tutorial on how to use Vagrant in combination with Packer for the creation of virtual machines or in short VM’s. Vagrant and Packer are useful tools in order that enable provisioning of multiple development environments at the same time. During any time in the tutorial you can skip ahead and download the discussed files, this way you could immediately skip to the Vagrant part.
When working on projects one of the main obstacles developers face is code that runs well on their system but not on the systems of other developers. A way to solve this is creating a virtual machine for each development environment. However, one of the problems you might encounter creating “Golden Images” is that it takes a lot of time and effort to maintain these; if it is necessary to upgrade to a new JDK or change to another IDE all the developers must get that newly created VM which can be multiple gigabytes in size.
This is where Vagrant in combination with Packer might offer a solution. Instead of creating a new golden image each time Packer is used to create a base box with a running OS which is thereafter further configured by Vagrant.
In our case we are going to build one using Packer; but there are a lot of boxes already available at https://atlas.hashicorp.com/boxes/search. The boxes found on this page are boxes created by the community and come in almost every flavor.
Prerequisites
This tutorial is done using Windows as host OS. However, it is possible to do this tutorial on Linux with little to no modification. For this tutorial the following sofware is needed:
- Virtualbox
- Vagrant
- Packer
- Notepad++ or similar (needed for Unix/Linux EOL)
- Example software package jdk-8u91-linux-x64.rpm
Vagrant versus Packer
Packer
{
"provisioners": [
{
}
],
"post-processors": [
{
}
],
"builders": [
{
}
]
}
Builders are used to tell Packer where to get the ISO file and it contains a basic configuration for the VM; more importantly it contains the configuration used to configure the base of the OS.Provioners contain commands that are executed after the VM has booted once, this should only be used for basic provisioning such as adding a specific driver, software repository, desktop environment or something similar.
Creating the base box
Lets start by setting up a folder for our Packer box. Create a new folder on a hard drive where you have got enough empty disk space. Depending on the choice of OS the size of the base box might be between 1-20GB. Call the folder you just created RedHat_BaseBox. For your convenience it might be a good idea to put the Packer executable in this directory.
Lets get started by creating the builder block mentioned before, create a new text file, name this redhat_rhel_7.2.json and configure it to have Unix/Linux EOL. It is suggested to use an advanced editor such as Notepad++. Open the file and place the previously mentioned structure into it, after doing so edit the building block making it look similar to the one displayed below.
"builders": [
{
"type": "virtualbox-iso",
"boot_command": [
"<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg<enter><wait>"
],
"boot_wait": "10s",
"guest_os_type": "RedHat_64",
"http_directory": "http",
"iso_url": "file:////C:/Isos/rhel-server-7.2-x86_64-dvd.iso",
"iso_checksum": "03f3a0291634335f6995534d829bd21ffaa0d000004dfeb1b2fb81052d64a4d5",
"iso_checksum_type": "sha256",
"ssh_username": "tutorial",
"ssh_password": "P@ssw0rd",
"ssh_wait_timeout": "9000s",
"ssh_port": 22,
"shutdown_command": "echo 'tutorial' | sudo -S shutdown -P now",
"guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso",
"virtualbox_version_file": ".vbox_version",
"vboxmanage": [
[ "modifyvm", "{{.Name}}", "--memory", "2048" ],
[ "modifyvm", "{{.Name}}", "--cpus", "1" ]
]
}
]
I will not go into the specifics since Packer is well documented on the Hashicorp site. Some important command I do highlight.
- boot_command: This tells Packer what to execute after booting from the ISO. Key input is simulated with brackets; commonly used commands are <tab>, <enter>, <wait10>. In this case we feed it an URL to a kickstart file for Red Hat, this simplifies the installation.
- boot_wait: This tells Packer to wait for a given amount of time when it boots the first time from the ISO.
- guest_os_type: This tells Packer how to configure your VM for a specific OS.
- http_directory: This tells Packer the directory where to find our configuration file(s).
- ssh_*: These tell Packer how to connect to our VM, this is needed for provisioning.
- vboxmanage: Tells Packer how to configure for a specific VM, like Virtualbox.
The next step is configuring the provisioners, keep in mind that its better to do most of the provisioning in Vagrant. The scripts listed below are used in the following fashion:
- base.sh: Add some packages, repositories and some little configuration.
- virtualbox.sh: This will install Virtualbox specific things like the Virtualbox Guest Additions. These additions are a minimal requirement for using Vagrant on this box.
- cleanup.sh: This is where some files left from the installation will be cleaned on your VM.
- zerodisk.sh: This basically optimizes disk space on your VM (it is suggested leaving this time consuming option off if you are currently debugging the whole Packer process).
"provisioners": [
{
"type": "shell",
"execute_command": "echo 'tutorial_user'|sudo -S bash '{{.Path}}'",
"override": {
"virtualbox-iso": {
"scripts": [
"scripts/base.sh",
"scripts/virtualbox.sh",
"scripts/cleanup.sh",
"scripts/zerodisk.sh"
]
}
}
}
],
Post-processors tell Packer what kind of output file we want.
"post-processors": [
{
"type": "vagrant",
"override": {
"virtualbox": {
"output": "redhat-7-2-x64-virtualbox.box"
}
}
}
],
In order to keep this tutorial on topic it is possible to use the following kickstarter file for the Red Hat image. This file is configured to work with the json script shown above, just place the file in the earlier created http directory. The scripts are also available at this link, after downloading put them in a folder called scripts. The directory structure should look similar to this:
If for some reason it you where unable to get the json file working properly it is possible to download the file here.
Now we are ready to run Packer; before you start make sure the firewall does not block the ssh port. Open a command line prompt and browse to your directory (or ctrl + alt + right mouse -> Command prompt here).
Run the following command to validate your configuration file:
packer validate redhat_rhel_7.2.json
If there where no errors run the following command to start the Packer process:
packer build redhat_rhel_7.2.json
Building the image may take a while (15 minutes) depending on the speed of your CPU and hard drive, if all went successful there should be an image called redhat-7-2-x64-virtualbox.box in your working directory.
Note: It is possible to create multiple boxes with one configuration file at the same time. This could be used to create a VMWare, Virtualbox and Amazon box in one go. However, this is out of the scope of this tutorial.
Vagrant configuration
Now that we’re done with creating a base box we can start to provision it with Vagrant. If you skipped ahead and just want to follow the Vagrant part of the tutorial you can download the box at this link. The Vagrant configuration file is much like Packer, it is sort of divided into three sections. Before we are going into depth, first create a new directory in which to put the Vagrant configuration. In this directory create a file without any extension with the name “Vagrantfile”. Open this file in a text editor and use the following instructions.
The first section basically tells Vagrant how the VM should be named and what user to use to connect to it with SSH.
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# Boxname
config.vm.box = "RedHat7"
config.vm.hostname = "rhel7"
# ssh user and password needed for provisioning
config.ssh.username = "tutorial"
config.ssh.password = "P@ssw0rd"
The following section describes the configuration for Virtualbox. This has a direct influence on how your Virtualbox is configured and performs, useful settings like hardware acceleration or the ability to copy/paste to your VM can be set here.
config.vm.provider "virtualbox" do |vb|
# Display the VirtualBox GUI when booting the machine
vb.gui = true
vb.cpus = 2
# Setup videocard
vb.customize ["modifyvm", :id, "--vram", "128"]
vb.customize ["modifyvm", :id, "--accelerate3d", "on"]
# Setup copy/pasta
vb.customize ["modifyvm", :id, "--clipboard", "bidirectional"]
vb.customize ["modifyvm", :id, "--draganddrop", "bidirectional"]
# Configure memory
vb.memory = "4096"
end
The final section is meant for provisioning, normally this is configured in such a way that a user can type “vagrant up” allowing him to start working at a fully configured VM within 5 minutes.
This tutorial has a Vagrant file that only contains minimal provisioning.
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
# privileged false is possible because we disabled asking for password "NOPASSWD" in packer,
# further down the installation this will later be removed in ./setup_user.sh
config.vm.provision "shell", privileged: false, inline: <<-SHELL
cd /vagrant/scripts
# Install additional software
./software_install.sh
# Set environment variables
sudo ./environment_variables.sh
# Setup user
./setup_user.sh
echo "Installation done, enjoy your desktop"
SHELL
end
As in the Packer part of the tutorial, the scrips can be downloaded at this location. Like before put them in a scripts folder. Download the earlier mentioned JDK and put the rpm file in the scripts folder. The directory structure should look similar to the following screenshot.
If it looks like this you’re all set to start Vagrant, if you don’t have the correct Vagrant file yet you can download it right here. Open a command prompt and browse to your folder and run the following command:
vagrant box add RedHat7 "../packer/redhat-7-2-x64-virtualbox.box"
This adds the box you just created to a predetermined folder of the VM you are using, in this case Virtualbox, changing this location is possible in Virtualbox. The fourth parameter is paramount, this tells vagrant how your box is called, notice that it is the same as the “config.vm.box” parameter in the Vagrant file. The fifth parameter is the location of the file Packer had as output. This command may take a while depending on the size of your previously build box.The next step is starting the Vagrant VM you have configured. The first time you run the following command it can take some time for it to complete depending on how much provisioning is done and how fast your machine is. In this tutorial it should run quite fast.
vagrant up
The VM will be booted earlier than Vagrant finishes its provisioning, it is recommended that you wait until Vagrant is done. For repeated use, your VM can be found in Virtualbox and you could also start it by running “vagrant up” again in your working directory.
If you somehow broke your VM or you want to start over with a fresh system you can simply type the following command:
vagrant destroy