Simulating Chef upgrades using Vagrant
Wouldn't it be awesome to simulate production changes quickly? Chances are you're using Chef in production. We'll see how to use both Chef cookbooks with Vagrant, as well as how to simulate Chef version upgrades between environments. This kind of setup is the beginning of a good combination of infrastructure as code.
Getting ready
To step through this recipe, you will need the following:
- A working Vagrant installation
- A working VirtualBox installation
- An Internet connection
How to do it…
Let's start with a minimal virtual machine named prod
that simply boots a CentOS 7.2, like we have in our production environment:
Vagrant.configure("2") do |config| config.vm.box = "bento/centos-7.2" config.vm.define "prod" do |config| config.vm.hostname = "prod" config.vm.network "private_network", type: "dhcp" end end
Vagrant Omnibus Chef plugin
Now, if we want to use Chef code, if we want to use Chef code (Ruby files organized in directories that form a unit called a 'cookbook' that configure and maintain a specific area of a system), we first need to install Chef on the Vagrant box. There're many ways to do this, from provisioning shell scripts to using boxes with Chef already installed. A clean, reliable, and repeatable way is to use a Vagrant plugin to do just that—vagrant-omnibus. Omnibus is a packaged Chef. Install it like any other Vagrant plugin:
$ vagrant plugin install vagrant-omnibus Installing the 'vagrant-omnibus' plugin. This can take a few minutes... Installed the plugin 'vagrant-omnibus (1.4.1)'!
Then, just add the following configuration in your VM definition of the Vagrantfile and you'll always have the latest Chef version installed on this box:
config.omnibus.chef_version = :latest
However, our goal is to mimic production, maybe we're still using the latest in v11.x series of Chef instead of the latest 12.x, so instead let's specify exactly which version we want:
config.omnibus.chef_version = "11.18.12"
Now that we're using a new plugin, our Vagrantfile won't work out of the box for everybody. Users will have to install this vagrant-omnibus plugin. If you care about consistency and repeatability, an option is to add the following Ruby check at the beginning of your Vagrantfile:
%w(vagrant-vbguest vagrant-omnibus).each do |plugin| unless Vagrant.has_plugin?(plugin) raise "#{plugin} plugin is not installed! Please install it using `vagrant plugin install #{plugin}`" end end
This code snippet will simply iterate over each plugin name to verify that Vagrant returns them as installed. If not, stop there and return a helpful exit message on how to install the required plugins.
A sample Chef recipe
This part of the book isn't about writing Chef recipes (read more about it later in the book!), so we'll keep that part simple. Our objective is to install the Apache 2 web server on CentOS 7 (httpd
package), and start it. Here's what our sample recipe looks like (cookbooks/apache2/recipes/default.rb
); it does exactly what it says in plain English:
package "httpd" service "httpd" do action [ :enable, :start ] end
Vagrant and Chef integration
Here's how, in our VM definition block, we'll tell Vagrant to work with Chef Solo (a way of running Chef in standalone mode, without the need of a Chef server) to provision our box:
config.vm.provision :chef_solo do |chef| chef.add_recipe 'apache2' end
As simple as that. Vagrant this up (vagrant up
), and you'll end up with a fully provisioned VM, using the old 11.18.12 version, and a running Apache 2 web server.
Our manual tests can include checking that the chef-solo version is the one we requested:
$ chef-solo --version Chef: 11.18.12
They can also check if we have httpd
installed:
$ httpd -v Server version: Apache/2.4.6 (CentOS)
Also, we can check if httpd
is running:
$ pidof httpd 13029 13028 13027 13026 13025 13024
Note
Various other options than chef-solo exist, such as chef-client and chef-zero.
Testing the Chef version update
So we simulated our production environment locally, with the same CentOS version, the apache2 cookbook used in production, and the old Chef version 11. Our next task is to test if everything is still running smoothly after an upgrade to the new version 12. Let's create a second "staging" VM, very similar to our production setup, except we want to install the current latest Chef version (12.13.37 at the time of writing, feel free to use :latest
instead):
config.vm.define "staging" do |config| config.vm.hostname = "staging" config.omnibus.chef_version = "12.13.37" config.vm.network "private_network", type: "dhcp" config.vm.provision :chef_solo do |chef| chef.add_recipe 'apache2' end end
Launch this new machine (vagrant up staging
) and we'll see if our setup still works with the new major Chef version:
$ vagrant ssh staging $ chef-solo --version Chef: 12.13.37 $ httpd -v Server version: Apache/2.4.6 (CentOS) $ pidof httpd 13029 13028 13027 13026 13025 13024
So we can safely assume, as far as our testing goes, that the newest Chef version still works correctly with our production Chef code.
There's more…
Here are more ways of controlling a Vagrant environment, and use even better Chef tooling inside it.
Controlling default Vagrant VMs
You may not always want to boot both production and staging vagrant virtual machines, especially when you just want to work on the default production setup. To specify a default VM:
config.vm.define "prod", primary: true do |config|
[…]
end
To not start automatically a VM when issuing the vagrant up
command:
config.vm.define "staging", autostart: false do |config|
[…]
end
Berkshelf and Vagrant
Chances are, if your production environment is using Chef, you're also using Berkshelf for dependency management and not 100% local cookbooks (if you aren't, you should!).
Vagrant work pretty well with a Berkshelf enabled Chef environment, using the vagrant-berkshelf
plugin.
Note
Your workstation will need the Chef Development Kit (Chef DK: https://downloads.chef.io/chef-dk/) for this to work correctly.
Testing with Test Kitchen
This setup is in fact so close to what's used to make infrastructure code testing that you'll see a lot of similarities in the dedicated section of this book.