Why Chef Should Manage Deploying Your Application

This post and my previous one on Puppet aren't necessarily beginner guides, but offer some hooks to get started. Instead they focus on what makes Puppet and Chef unique and why you might want to choose each. You should use something, whether it's one of these tools, or some of the other ones available. Let's dig into Chef to see why it provides a great solution for deploying the infrastructure around your application.

Components vs. Application

A keen observer will notice a subtle difference between the titles with the same intent, managing servers and the software needed for your business and application. I chose the wording based on what I see as each tool's strengths. I've shown how Puppet does a nice job of managing the components necessary in your infrastructure. Below I intend to highlight the features that make Chef compelling and a worthy choice when it comes time to deploying your application.

Identity Crisis

In this article I intend to focus on the portion of Chef called Chef Solo. There are two reasons for this, the first being that a vast majority of users that I know using Chef forgo using the server portion and secondly because Chef Server suffers from an identity crisis. Chef Solo identifies more with a library than a separate piece of infrastructure. Chef Server attempts to place this library into an infrastructure management system that wants to be the source for all configuration data but at the cost of taking away those library tendencies from Chef Solo. It has been a while since I've taken a close look at Chef Server, but this has more to do with Chef Solo providing a powerful set of features for deploying applications and less to do with Chef Server attempting to fill a role that I find unnecessary in a majority of the environments where one would use Chef.

If you do decide to take a closer looker at Chef, I recommend checking out the hosted solution provided by Opscode (the company behind Chef). I have zero experience with it, but of the Chef Server users I know, the majority of them chose this solution over the self-installed version. Their reports of the service have been glowing and many swear by it. They offer five nodes for free to try out the service.

The Chef DSL

One of Chef's greatest strengths is in it's domain specific language (DSL) for managing resources. In fact this was one of the major goals of the project early on, to provide a Ruby DSL for installing, configuring and monitoring software (compared to Puppet's unique manifest language). The language is succinct and defines a number of resources similar to other tools. Here is the example I gave in my Puppet article:

file { "/etc/ntp.conf":
    owner => root,
    group => root,
    mode => 0644,
    source => "puppet:///ntpd/ntp.conf"
}

Here it is written in Chef:

cookbook_file "/etc/ntpd.conf" do
    owner "root"
    group "root"
    mode 0644
    source "ntpd.conf"
end

The same structure applies here as it did previously, we've defined a type (cookbook_file), a resource name ("/etc/ntpd.conf") and the attributes specific to this definition. In fact, in most cases, the dialect for Puppet, and the dialect for Chef are quite similar. Where Chef shines is in the ability to write Ruby right alongside the Chef DSL, providing the developer a lot of power when working to deploy their application. No longer is infrastructure a separate concept, it can now be manipulated right alongside your application as if you were writing a Rake task.

Here is an example of how you might use Ruby in your resource definitions. This example provides a method for maintaining a Java installation on two different operating systems:

case node[:platform]
when 'gentoo'
    package "dev-java/sun-jdk"
when 'ubuntu'
    package "openjdk-6-jdk"
end

You can see this is a standard Ruby 'case' statement. The 'node[:platform]' is an attribute of Ohai, the "facts" system that comes along with Chef. This gives you access to information regarding your host system inside Chef (analogous to Puppet's Facter).

This advantage is why Chef can be thought of more as a library used to deploy your application rather than a system for managing your infrastructure. Some may suggest I am splitting hairs, but the distinction is significant. If you are a Ruby developer today looking for a fast way to manage the deployment of your Rails application, you should be using Chef. It will provide you with a plethora of options for dropping your application on a server and configuring the required pieces. Tweaking config files alongside your Rails controllers in the same repository is exactly what agile development is all about. Your application doesn't just become another component, say as it would in a Puppet structure, but rather your infrastructure becomes another layer of your application.

Ohai's Plugin System

Chef also provides a very powerful plugin system to Ohai. This system can be utilized to inject application logic, first class attributes and other things directly into the 'node' object used by Chef runs. This system is unknown by a vast majority of users. This can be attributed to the minimal documentation provided. I hope more people take advantage of this feature as it is the cleanest method to provide application specific data alongside Chef's provided mechanisms.

Deterministic by Nature

Another strength provided by Chef is its deterministic behavior. When constructing Chef cookbooks, order of specification equals order of execution and is a valuable constraint for defining your resources, which provides developers and operations with a "fail fast" mentality in order to rapidly iterate over deployment. This also allows Chef's code base to be significantly smaller than the competition as there are fewer moving parts in a Chef run.

These features enable Chef Solo to shine in rapid deployment and provisioning.

Getting Started with Chef Solo

Compared to Puppet getting started with Chef Solo requires a bit more structure, but this doesn't detract from the ease of getting a system in place. There are helper tools that can be used with Chef Solo, though they are designed more for Chef Server interaction.

Let's take a look at what a Chef directory structure might look like:

/etc/chef/
    cookbooks/
        cookbook1/
            attributes/
                default.rb
            libraries/
                ruby_code.rb
            recipes/
                default.rb
            files/
                default/
            templates/
                default/
        cookbook2/
    node.json
    solo.rb

This is a bit more advanced, but ultimately you need a cookbook path, a 'solo.rb' and a json file that contains configuration details. Cookbook structure is rather rigid and provides access to a lot of local (to the cookbook) configuration which makes sharing cookbooks easy. A good example set of cookbooks can be found here (though some configurations may be out of date).

The entry point for a Chef Solo run is the 'solo.rb', here is an example of what a typical configuration might look like:

log_level :debug
log_location "/var/log/chef.log"
file_store_path File.join(File.dirname(__FILE__))
file_cache_path File.join(File.dirname(__FILE__))
cookbook_path [
    File.join(File.dirname(__FILE__), "cookbooks")
]

If you are familiar with Ruby, this is relatively straightforward. What we are doing here is using Ruby to construct paths based on the location of the 'solo.rb' file. The most significant setting here is the 'cookbook_path' which is how Chef finds its cookbooks.

You define a run list of the cookbooks on your cookbook path in a json file (in our example 'node.json'). This file is relatively straightforward and can be used to define attributes used by your cookbooks. This file can be as simple as:

 { "run_list": "main" }

In this example we might have a 'main' cookbook that specifies conditions on which other recipes are included. This is a common method for providing a bit of flexibility to recipe declaration on a per node basis. Here's a snippet of what a "main" recipe might look like:

case node[:platform]
when 'ubuntu'
    require_recipe 'ubuntu_base'
    require_recipe 'ubuntu_packages'
when 'gentoo'
    require_recipe 'gentoo_base'
    require_recipe 'gentoo_packages'
end

There are a number of methods that are used to achieve this, specifically roles.

Conclusion

The community around Chef is growing and for good reason. Chef provides developers, especially Ruby developers, with a method for programming their infrastructure. This turns out to be invaluable. At Engine Yard we use Chef in some rather adventurous ways that allow us a lot of flexibility when designing our infrastructure.

I encourage you to explore how Chef might fit into your deployment strategy. If you have questions you might find the Chef IRC channels and Chef mailing lists to be valuable resources. Feel free to post comments below and I may be able to help also.