When you have the need to create VM boxes that can run in multiple environments Packer is an excellent choice.
Out of the box Packer comes with support to build images for Amazon EC2, DigitalOcean, Google Compute Engine, QEMU, VirtualBox, VMware, and more.
Support for more platforms is on the way, and anyone can add new platforms via plugins.
For a local development environment we can leverage the advantages of using Vagrant to run VMs and test or develop right in a clean environment without inadvertently introducing settings or configurations of our host machine.
This allows to level the conditions of the development, staging, QA and production environments.
In this way we would avoid circumstances like:
I don’t know what happened. It ran fine in my machine.
Before we continue you will need these requirements:
- Virtualbox or VMware Fusion.
- Vagrant
- Git
Bento
First let me introduce Bento https://github.com/chef/bento
Bento is a project that encapsulates Packer templates for building Vagrant baseboxes. To use it we need to install Packer.
Installing Packer
To install Packer download the corresponding package from: https://www.packer.io/downloads.html
Extract the package and then add the folder to your PATH.
For example:
You could add it to your ~/.profile
file.
export PATH$PATH:/path/to/docker
Then to test in the terminal run:
~: docker
usage: packer [--version] [--help] <command> [<args>]
Available commands are:
build build image(s) from template
fix fixes templates from old versions of packer
inspect see components of a template
push push template files to a Packer build service
validate check that a template is valid
version Prints the Packer version
If you get the quick help of Docker then you’re on! Otherwise check the directory again and don’t forget to restart your terminal session.
Cloning Bento
Let’s create a directory where we will host the code:
~: cd ~ ~: mkdir baseboxes ~: cd baseboxes
The next step is to clone bento’s repository:
~/baseboxes: git clone https://github.com/opscode/bento.git Cloning into 'bento'... remote: Counting objects: 3129, done. remote: Compressing objects: 100% (7/7), done. remote: Total 3129 (delta 3), reused 2 (delta 1) Receiving objects: 100% (3129/3129), 652.65 KiB | 0 bytes/s, done. Resolving deltas: 100% (1986/1986), done. Checking connectivity... done.
Great! git clone created a directory called bento inside baseboxes. Let’s take a look into it:
~/baseboxes: ls bento/packer/ centos-5.11-i386.json freebsd-10.1-amd64.json oracle-6.6-x86_64.json solaris-10.5-x86.json centos-5.11-x86_64.json freebsd-10.1-i386.json packer_cache solaris-11-x86.json centos-6.6-i386.json freebsd-9.3-amd64.json rhel-5.11-i386.json ubuntu-10.04-amd64.json centos-6.6-x86_64.json freebsd-9.3-i386.json rhel-5.11-x86_64.json ubuntu-10.04-i386.json centos-7.0-x86_64.json http rhel-6.6-i386.json ubuntu-12.04-amd64.json debian-6.0.10-amd64.json macosx-10.7.json rhel-6.6-x86_64.json ubuntu-12.04-i386.json debian-6.0.10-i386.json macosx-10.8.json rhel-7.0-x86_64.json ubuntu-14.04-amd64.json debian-7.8-amd64.json macosx-10.9.json scripts ubuntu-14.04-i386.json debian-7.8-i386.json omnios-r151010j.json sles-11-sp2-i386.json ubuntu-14.10-amd64.json fedora-20-i386.json opensuse-13.2-i386.json sles-11-sp2-x86_64.json ubuntu-14.10-i386.json fedora-20-x86_64.json opensuse-13.2-x86_64.json sles-11-sp3-i386.json vagrantfile_templates fedora-21-i386.json oracle-5.11-i386.json sles-11-sp3-x86_64.json fedora-21-x86_64.json oracle-5.11-x86_64.json sles-12-x86_64.json floppy oracle-6.6-i386.json solaris-10.11-x86.json
We have baseboxes definitions for a number of Operating Systems that we can build with Bento. From there it is easy to create custom versions if necessary. Let’s take a look at one of them:
{ "builders": [ { "boot_command": [ "<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos-6.6/ks.cfg<enter><wait>" ], "boot_wait": "10s", "disk_size": 40960, "guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso", "guest_os_type": "RedHat_64", "http_directory": "http", "iso_checksum": "08be09fd7276822bd3468af8f96198279ffc41f0", "iso_checksum_type": "sha1", "iso_url": "{{user `mirror`}}/6.6/isos/x86_64/CentOS-6.6-x86_64-bin-DVD1.iso", "output_directory": "packer-centos-6.6-x86_64-virtualbox", "shutdown_command": "echo 'vagrant'|sudo -S /sbin/halt -h -p", "ssh_password": "vagrant", "ssh_port": 22, "ssh_username": "vagrant", "ssh_wait_timeout": "10000s", "type": "virtualbox-iso", "vboxmanage": [ [ "modifyvm", "{{.Name}}", "--memory", "480" ], [ "modifyvm", "{{.Name}}", "--cpus", "1" ] ], "virtualbox_version_file": ".vbox_version", "vm_name": "packer-centos-6.6-x86_64" }, { "boot_command": [ "<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos-6.6/ks.cfg<enter><wait>" ], "boot_wait": "10s", "disk_size": 40960, "guest_os_type": "centos-64", "http_directory": "http", "iso_checksum": "08be09fd7276822bd3468af8f96198279ffc41f0", "iso_checksum_type": "sha1", "iso_url": "{{user `mirror`}}/6.6/isos/x86_64/CentOS-6.6-x86_64-bin-DVD1.iso", "output_directory": "packer-centos-6.6-x86_64-vmware", "shutdown_command": "echo 'vagrant'|sudo -S /sbin/halt -h -p", "ssh_password": "vagrant", "ssh_port": 22, "ssh_username": "vagrant", "ssh_wait_timeout": "10000s", "tools_upload_flavor": "linux", "type": "vmware-iso", "vm_name": "packer-centos-6.6-x86_64", "vmx_data": { "cpuid.coresPerSocket": "1", "memsize": "480", "numvcpus": "1" } }, { "boot_command": [ "<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos-6.6/ks.cfg<enter><wait>" ], "boot_wait": "10s", "disk_size": 40960, "parallels_tools_flavor": "lin", "guest_os_type": "centos", "http_directory": "http", "iso_checksum": "08be09fd7276822bd3468af8f96198279ffc41f0", "iso_checksum_type": "sha1", "iso_url": "{{user `mirror`}}/6.6/isos/x86_64/CentOS-6.6-x86_64-bin-DVD1.iso", "output_directory": "packer-centos-6.6-x86_64-parallels", "shutdown_command": "echo 'vagrant'|sudo -S /sbin/halt -h -p", "ssh_password": "vagrant", "ssh_port": 22, "ssh_username": "vagrant", "ssh_wait_timeout": "10000s", "type": "parallels-iso", "prlctl": [ [ "set", "{{.Name}}", "--memsize", "480" ], [ "set", "{{.Name}}", "--cpus", "1" ] ], "prlctl_version_file": ".prlctl_version", "vm_name": "packer-centos-6.6-x86_64" } ], "post-processors": [ { "output": "../builds/{{.Provider}}/opscode_centos-6.6_chef-{{user `chef_version`}}.box", "type": "vagrant" } ], "provisioners": [ { "environment_vars": [ "CHEF_VERSION={{user `chef_version`}}" ], "execute_command": "echo 'vagrant' | {{.Vars}} sudo -S -E bash '{{.Path}}'", "scripts": [ "scripts/centos/fix-slow-dns.sh", "scripts/common/sshd.sh", "scripts/common/vagrant.sh", "scripts/common/vmtools.sh", "scripts/common/chef.sh", "scripts/centos/cleanup.sh", "scripts/common/minimize.sh" ], "type": "shell" } ], "variables": { "chef_version": "provisionerless", "mirror": "http://mirrors.kernel.org/centos" } }
From the previous snippet we can appreciate the amount of parameters that we can fine tune according to our needs. For example we can change the number of CPUs, the memory and disk size.
There are also some parameters specific to the provisioners (VirtualBox or VMware).
Building a basebox
Let’s change directory to:
cd bento/packer/
If we only have VMware or Virtualbox installed we can tell packer to build only a box to run in such an environment like so:
~/baseboxes/bento/packer: packer build -only=vmware-iso centos-6.6-x86_64.json
or:
~/baseboxes/bento/packer: packer build -only=virtualbox-iso centos-6.6-x86_64.json
Time to go for a coffee… Anyone?
After running the corresponding command bento-packer will:
- Download the ISO file of the selected OS
- Create a Virtual Machine
- VNC into it and start an unattended installation
- In my case it installs VMware tools since I’m using fusion
- Clean all the temporary files and cache packages
- Compress the resulting vm disk
Finally we see the following in the command line:
Build 'vmware-iso' finished. ==> Builds finished. The artifacts of successful builds are: --> vmware-iso: 'vmware' provider box: ../builds/vmware/opscode_centos-6.6_chef-provisionerless.box ~/baseboxes/bento/packer: cd ../builds/vmware/
Vagrant
Let’s list the installed boxes in vagrant. In my case:
~/baseboxes/bento/builds/vmware: vagrant box list chef/centos-6.5 (vmware_desktop, 1.0.0) chef/ubuntu-12.04 (vmware_desktop, 1.0.0) opscode-centos-6.5 (vmware_desktop, 0) opscode-ubuntu-12.04 (vmware_desktop, 0) phusion/ubuntu-14.04-amd64 (vmware_fusion, 2014.04.30)
Add
To use the newly created basebox we need to add it to vagrant:
~/baseboxes/bento/builds/vmware: vagrant box add --name opscode-centos-6.6 opscode_centos-6.6_chef-provisionerless.box ==> box: Adding box 'opscode-centos-6.6' (v0) for provider: box: Downloading: file:///Users/jorgemorales/baseboxes/bento/builds/vmware/opscode_centos-6.6_chef-provisionerless.box ==> box: Successfully added box 'opscode-centos-6.6' (v0) for 'vmware_desktop'!
Let’s list again to check that the box has been added:
~: vagrant box list chef/centos-6.5 (vmware_desktop, 1.0.0) chef/ubuntu-12.04 (vmware_desktop, 1.0.0) opscode-centos-6.5 (vmware_desktop, 0) opscode-centos-6.6 (vmware_desktop, 0) opscode-ubuntu-12.04 (vmware_desktop, 0) phusion/ubuntu-14.04-amd64 (vmware_fusion, 2014.04.30)
Test
Now let’s test the box. I will create a directory newproject and then initialize our new box:
~: mkdir newproject ~: cd newproject ~/newproject: vagrant init opscode-centos-6.6 A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant.
Run
Looking good. Why don’t we start the new machine.
~/newproject: vagrant up Bringing machine 'default' up with 'vmware_fusion' provider... ==> default: Cloning VMware VM: 'opscode-centos-6.6'. This can take some time... ==> default: Verifying vmnet devices are healthy... ==> default: Preparing network adapters... ==> default: Starting the VMware VM... ==> default: Waiting for the VM to finish booting... ==> default: The machine is booted and ready! ==> default: Forwarding ports... default: -- 22 => 2222 ==> default: Configuring network adapters within the VM... ==> default: Waiting for HGFS kernel module to load... ==> default: Enabling and configuring shared folders... default: -- /Users/jorgemorales/repos/newproject: /vagrant
Excellent! The box is up and running ready to take our code or test whatever we need.
Stop
For now let’s stop the machine and remove the vm file.
~/newproject: vagrant halt ==> default: Attempting graceful shutdown of VM... ~/newproject: vagrant destroy default: Are you sure you want to destroy the 'default' VM? [y/N] y ==> default: Deleting the VM...
Remove
If necessary we can remove the box from our vagrant list:
~: vagrant box remove opscode-centos-6.6 Removing box 'opscode-centos-6.6' (v0) with provider 'vmware_desktop'... ~: vagrant box list chef/centos-6.5 (vmware_desktop, 1.0.0) chef/ubuntu-12.04 (vmware_desktop, 1.0.0) opscode-centos-6.5 (vmware_desktop, 0) opscode-ubuntu-12.04 (vmware_desktop, 0) phusion/ubuntu-14.04-amd64 (vmware_fusion, 2014.04.30)
And that’s it! Enjoy.
What are you waiting for? It’s time to start coding something awesome!