Cooking with Chef 101
Posted by ezmobius Sat, 31 Jan 2009 19:00:00 GMT
One of the things I really like about chef is that it scales down as well as up. You can get started using it right away with chef-solo and then graduate to chef-client and chef-server when you need more cowbell. And then as you grow you can horizontally partition each component of the chef infrastructure, web servers, couchdb servers with messaging queues to decouple each part so you can go from tiny to large and in charge with the same recipes.
So let’s get started with a simple set of recipes to install some rubygems, and setup some directories for deploying a few apps with capistrano after chef configures the base system.
First you will need to clone my chef-101 repo from github:
http://github.com/ezmobius/chef-101
$ git clone git://github.com/ezmobius/chef-101.git
First we need to get chef installed.
$ sudo gem install chef ohai --source http://gems.opscode.com --source http://gems.rubyforge.org
(the two sources are needed so that dependencies can be installed form rubyforge even though chef is hosted on gems.opscode.com. Rubygems quirk but at least it works this way ;)
Now lets just run the recipe set so you can see chef in action! (this will install a few rubygems on your system so you can skip this step if you want. It will also create a few directories underneath /data.)
You will need to change the user attribute in the config/dna.json file to a user that exists on your system before this will run(unless you have an ‘ez’ user like I do ;)
$ cd chef-101 $ sudo chef-solo -l debug -c config/solo.rb -j config/dna.json
You will see a bunch of output scroll by, first you will see the ohai output, ohai is the systems information collector. The you will see a bunch of info about compiling recipes and applying recipes. It will end with this line if it was successful:
INFO: Chef Run complete in 1.093957 seconds
Congratulations! You are now cooking with fire. Now let’s take a look at the recipes and how they are put together so you know how this thing works.
First off what is this dna.json file we passed in to chef-solo?
{
"apps": [
"beast",
"mephisto"
],
"user": "ez",
"gems": [
{
"name": "rake",
"version": "0.8.3"
},
{
"name": "tmm1-amqp",
"source": "http://gems.github.com",
"version": "0.6.0"
}
],
"recipes": ["main"]
}
This is what I am calling the JSON DNA of your recipe set. These are the parameters that will be available in your recipes as the node object. So you can see we have some applications, a username and some rubygems to install.
You can see that we have set the “recipes” array to contain ‘main’. Let’s take a look at the main recipe, as this is our entry point into the system.
include_recipe 'gems'
include_recipe 'applications'
template "/data/someservice.conf" do
owner node[:user]
mode 0644
source "someservice.conf.erb"
variables({
:applications => node[:apps]
})
end
You can see that we use include_recipe, this command will pull in the named recipe and run it in place top down right there in the recipe. This allows for chef recipes to run in deterministic order every time, if you want one resource to run before another, just make sure it comes first in the recipe run.
So we are including the gems recipe and then the applications recipe. After that we render a template for some config file. You can see that the template resource is easy to define, you name it with the target location, and you tell it what permissions, what the source template is named and any variables to pass in to the template.
Now on to the gems recipe:
node[:gems].each do |gem|
gem_package gem[:name] do
if gem[:version] && !gem[:version].empty?
version gem[:version]
end
if gem[:source]
source gem[:source]
end
action :install
end
end
What we’re doing here is looping over all of the gems in our dna.json hash and creating a gem_package resource for each gem. Gems can have versions and sources, but we take care to not set these if they are empty. If you don’t specify a version it will install the latest available and if you don;t specify a source it will default to rubyforge.org. finally we say action :install, which will run the install action for each rubygem. This will not install the gems again if they are already installed on your system.
Now let’s look at the applications recipe:
directory "/data" do
owner node[:user]
mode 0755
end
node[:apps].each do |app|
cap_directories = [
"/data/#{app}/shared",
"/data/#{app}/shared/config",
"/data/#{app}/shared/system",
"/data/#{app}/releases"
]
cap_directories.each do |dir|
directory dir do
owner node[:user]
mode 0755
recursive true
end
end
end
First we create the /data directory and give it the permissions we want and owner we specified in our json. Then we loop over all the applications from our json and create the proper set of capistrano deploy directory resources for each app.
So ends our first installment of chef-101, this should be a good starting point for you to take on more advanced recipes.
Pro tip: once you have a nice set of recipes, you can create a tarball of them, put them on an http server somewhere and then use a quick capistrano script to fire the off on remote machines.
Say we create a chef-101.tgz recipe set and we put it up on the web at http://foo.com/chef-101.tgz. We can then log into a server with chef-solo installed vias ssh or via a cap script and run this command:
$ sudo chef-solo -l debug -j /etc/chef/dna.json -r http://foo.com/chef-101.tgz
That will download the recipe set, untar it and run chef on it with the json you passed in. You will first need to create /etc/chef directory and put a solo.rb as well as your dna.json in there.
/etc/chef/solo.rb should look like this:
cookbook_path "/etc/chef/recipes/cookbooks" log_level :info file_store_path "/etc/chef/recipes/" file_cache_path "/etc/chef/recipes/"
Hope you liked cooking with chef-101. See you all next time.
Searching...




