Configuration Management with Chef on Debian, Part 1

17 Dec 2009

UPDATE: Part 2 now online

I'm familiar with configuration management tools like Cfengine, Puppet and slack. I am also familiar with using homebrewed tools which leverage trees of shell scripts to apply and manage a bunch of configuration files stored in version control. I am going to teach myself to use Chef on Debian Lenny. Follow along.

Firstly, idempotent. Chef is idempotent. Wha? This means that if you run the chef client on a node multiple times, you should never get a gradually changing state. Given the same recipes, the system should always return to an identical state after the Chef tools run on the system. Think about those shell scripts you have written in the past. Sometimes they are appending strings to files, or removing strings from files, or moving files around based on some algorithm. Well, Chef doesn't perform a single action out of context. It does every action, every time. And the actions Chef performs are defined in recipes. Collections of recipes are called cookbooks. These recipes are written in Ruby. Recipes can be grouped together in roles, so when you apply a role to a node that node will receive all the recipes in that role. Recipes can reference attributes in other recipes, and also apply other recipes.

The bulk of the Chef work is done by the chef-client which runs on the host you are managing with chef. It may or may not contact a chef-server. Using chef-solo you can do a bunch of useful things without needing a central chef-server.

There are quite a few different tools in use in the complete Chef stack. Wonderful tools like couchdb, Stomp, Merb. Luckily, thanks to great packages from Opscode, you can get all of this stuff installed without too much trouble.

Note on versions: Chef 0.8 branch is currently getting some very interesting sounding features. In fact, I remarked in IRC today that I fully expect Chef 0.8 to file my taxes and walk my dog. However, I am not going there yet. This post will be all about Chef 0.7 branch, specifically the version of Chef provided in the Opscode apt repositories. By the way, you should totally hang out in #chef on Freenode. The people in there are awesome, helpful and very very good looking.

This is it. Let's do things.

Set up your Git repository

The various pieces which end up becoming files on the filesystem are stored in version control. You can apparently use SVN, but we'll go with Git. You'll need a nice fresh Git repo. To this repo you'll add your Chef cookbooks and roles, etc. So you could either host your Git repo with GitHub (you'll probably want to use a private repo, the repo will most likely contain some private things) or you could set up you own Git repo using Gitosis.

Once you have your Chef repo setup, get the contents of the Opscode Chef Repository into your own repo. I'm not going to go into the details of how to do this using Git, mostly because you don't want Git advice from me. Nevertheless, you want to grab the contents of this repo, and merge them in your new repo:

git clone git://github.com/opscode/chef-repo.git

When all is said and done, your repo should have a structure similar to this:

certificates/
config/
config/client.rb.example
config/server.rb.example
config/rake.rb
config/solo.rb.example
cookbooks/
Rakefile
roles/
site-cookbooks/

More detailed info on the Chef Repository

Setup your 2 servers and your DNS

For the purposes of this tutorial, we'll assume you have 2 servers. You could have 17 servers, that would be fine too. We'll call server 1 chef.yourdomain.com and server 2 will be web1.yourdomain.com. Make sure these servers have Debian Lenny installed and you have created proper working DNS entries for the hostnames chef.yourdomain.com and web1.yourdomain.com

We'll be installing Chef server AND a Chef client on chef.yourdomain.com We'll be installing only a Chef client on web1.yourdomain.com

Setup your Chef server

There are Chef cookbooks which will allow Chef to bootstrap itself, which is a fairly cool concept. This isn't necessary, however, when using the Opscode chef packages for Debian, since they will provide everything we need for our Chef server in the packages. So we'll simply install the packages and be on our way.

First, we'll need these:

apt-get update
apt-get install -y curl git-core sudo

Now let's set up the Opscode repo and install Chef:

echo "deb http://apt.opscode.com/ debian contrib" > /etc/apt/sources.list.d/opscode.list
curl http://apt.opscode.com/packages@opscode.com.gpg.key | apt-key add -
apt-get update
apt-get install -y rubygems libshadow-ruby1.8 ohai chef chef-server

Done. Buy Opscode some beer! OK, now let's do some configuration of the Chef server. Firstly, Chef uses OpenID extensively for authentication. We need to have an OpenID provider, which we trust defined in the chef server config. If you don't have an OpenID provider that you use, head over to myOpenID and sign up for an account. You'll get an OpenID URL. Now we'll put that into the chef config at /etc/chef/server.rb by adding the following line at the bottom of that file:

authorized_openid_providers [ "https://chef.localdomain", "https://chef", "https://whateveryourswas.myopenid.com"]

Now we are going to generate a validation token for our Chef clients to use to auto-register themselves against the Chef server. You need a nice random string. If you lack the creativity to make one up, try this:

thedate=$( date ); thehost=$( hostname ); echo $thedate$thehost | sha256sum

Now put the string you came up with above into /etc/chef/server.rb by finding the validation_token line, uncommenting it and giving it a value:

validation_token   "2d926b251e18cb39b00350bb956d44ef8639b4ed33809194a7e2ed60ac5d772c"

Restart Chef server:

/etc/init.d/chef-server restart

Setup your Chef Repository

Now we need our Git repository created earlier. We are going to use this repository to store our recipes, and we are going to use tools inside this repository to manage Chef itself. How meta. So on your newly installed chef server, in your home directory, or whereever else, clone your git repo:

git clone git@yourgitserver.yourdomain.com:your_chef_repo.git
cd your_chef_repo

We are now going to use Rake to install the initial (blank) Chef cookbooks into place, and also to set up the SSL certificates which Chef uses when communicating between components. Obviously you'll want to use your Chef server's proper FQDN here.

rake install
rake ssl_cert FQDN='chef.yourdomain.com'

Now, try logging into the Chef web UI, at http://chef.yourdomain.com:4000

In the login field, enter the openID URL you configured earlier, eg: https://whateveryourswas.myopenid.com you'll be forwarded to the OpenID provider, asked to authenticate and sent back to Chef. You should now be logged in.

Set up your Chef client

OK, now that we have a working, albeit currently fairly useless, Chef server let's install our first Chef client. On your other host, web1.yourdomain.com, you could use this script I quickly put together to go from a freshly installed server to a working Chef client. Obviously you should substitute your auth_token you created earlier and your chef's DNS location.

#!/bin/bash
### VARIABLES ##########################################################
chef_server="chef.yourdomain.com"
chef_client=/usr/bin/chef-client
chef_client_config="/etc/chef/client.rb"
chef_auth_token="2d926b251e18cb39b00350bb956d44ef8639b4ed33809194a7e2ed60ac5d772c"

opscode_apt="deb http://apt.opscode.com/ debian contrib"

### ACTIONS ############################################################
apt-get update
apt-get install -y curl git-core sudo

echo $opscode_apt > /etc/apt/sources.list.d/opscode.list
curl http://apt.opscode.com/packages@opscode.com.gpg.key | apt-key add -
apt-get update
apt-get install -y rubygems ohai chef libshadow-ruby1.8

### SETUP CHEF CLIENT AND REGISTER #####################################
sed -i "s/localhost/$chef_server/g" $chef_client_config
$chef_client -t $chef_auth_token

### REMOVE THYSELF #####################################################
rm $0

If all went according to plan, this should now be a working chef client which knows where the chef server is, and has auto registered itself with the Chef server. Have a look at http://chef.yourdomain.com:4000/registrations and see if web1.yourdomain.com is listed there. If so you are halfway to Nirvana. So now we actually want to start doing things by creating cookbooks, roles, ie: actually doing shit.

Stay tuned for part 2 in this enthralling series. Contact me for movie rights.

UPDATE: Part 2 now online