Chef Solo: Quick and easy cooking for one

Chef is a configuration management tool. It allows us to manage and automate servers configurations. It uses a Ruby DSL for writing blueprints—called recipes—that defines actions to be taken on the remote servers, e.g. install apache.

The Chef ecosystem is immense. Servers, nodes, workstations, agents, cookbooks; myriad of concepts. In this article I will just scratch the surface and present examples to get you running as quick as possible.

About Chef

In a full Chef organization there must exist a Chef server that stores configuration details of each server—or node—in the infrastructure. Developers/users write cookbooks (and recipes) locally on their workstations, and store the former in a Chef repository. Eventually, users use the knife command to upload data from the local Chef repository to the Chef server.

When running a small-sized infrastructure, chances are you do not need or want to setup a full Chef organization. Luckly, Chef provides a slimmer version called chef-solo, which allows configuring nodes without the need of a Chef server.

There is a knife plugin called knife-solo, that makes working with chef-solo easier and as powerful as with a Chef server.

Set up your kitchen

To keep everything organized, create a new project (git repo) and add knife-solo to the Gemfile.

$ git init chef-cookbooks
$ cd chef-cookbooks
$ bundle install --path vendor/bundle --binstubs

Create a basic knife configuration. Knife uses a RSA key pair to authenticate requests to the Chef Server. Even though you will not have one, knife still needs this client key. Create one using ssh-keygen. Ultimately, initialize your kitchen. Make sure to enable Librarian to help you manage your cookbooks dependencies.

$ bin/knife configure --defaults
$ ssh-keygen -f ~/.chef/$USER.pem
$ bin/knife solo init kitchen --librarian

Managing cookbooks

We will create a cookbook to install ruby using rbenv. Add the rbenv cookbook to the Librarians Cheffile. The dependencies will be installed in the cookbooks directory. There is no need to keep track of these files in your repository. Create your own cookbooks in the site-cookbooks directory.

# Cheffile
site "http://community.opscode.com/api/v1"

cookbook "rbenv", git: "https://github.com/RiotGames/rbenv-cookbook"
$ cd kitchen
$ ../bin/librarian-chef install
$ ../bin/knife cookbook create ruby -o site-cookbooks

I will cover the creation of cookbooks in another article. For now, will create the bare minimum to continue our example. Following the rbenv-cookbook documentation, add the rbenv dependency to your cookbook metadata.rb and in the default recipe use the rbenv_ruby command—aka lightweight resource and provider (LWRP).

# kitchen/site-cookbooks/ruby/metadata.rb
depends "rbenv"

# kitchen/site-cookbooks/ruby/recipes/default.rb
include_recipe "rbenv::default"
include_recipe "rbenv::ruby_build"
rbenv_ruby "1.9.3-p392" do
  global true
end

Setup node and cook

I like to configure my servers in my SSH client in a way that is easy to remember and use. Suppose I am working on a project called PetProject (pp) and I have three EC2 instances: web, app and db. My configuration looks like this:

# ~/.ssh/config
Host pp.*
  User ubuntu
  IdentityFile ~/.ssh/petproject.com/ubuntu

Host pp.web
  HostName ec2-54-215-126-10.sa-east-1.compute.amazonaws.com

Host pp.app
  HostName ec2-54-215-126-11.sa-east-1.compute.amazonaws.com

Host pp.db
  HostName ec2-54-215-126-12.sa-east-1.compute.amazonaws.com

Now I can easily ssh pp.web to connect to my web server. No need to remember IP addresses, DNS names, user names or passwords.

Use knife-solo to prepare your server. It will install in the server all dependencies Chef requires, and will create a local configuration file for this server in nodes/<hostname>.json.

Add your recipe to the run_list in the JSON file and cook your server.

$ ../bin/knife solo prepare pp.app
# edit nodes/pp.app.json as shown below
$ ../bin/knife solo cook pp.app
# kitchen/nodes/pp.app.json
{"run_list":["recipe[ruby]"]}

Finally, you have got yourself a brand new server with the latest Ruby 1.9.3 installed on it. Even better, now you have an automated task for it. In case you need to setup an app02 server, all you need is prepare and cook it and you are ready to go.