Bootstrap YET infrastructure using Opscode Chef - part 1

In this article, we will details all the required steps to build up an environment to host this web site on Nginx using Chef on HP Cloud.

Chef Repo cloning

First of all, if you don’t already have a Chef Repository available on your Chef Workstation, you can clone one easily :

% git clone git://
% cd chef-repo

Install cookbooks to your newly cloned repo

% knife cookbook site install build-essential apt chef-client sudo

For Nginx and users cookbooks install the latest version available on github

% git clone
% git clone

Patch Nginx cookbook to correct default_site_enabled. See my pull request for details or the following file cookbooks/nginx/definitions/nginx_site.rb

Upload cookbooks to Hosted Chef

% knife cookbook upload --all

Create a new Environment for HP Cloud

File chef-repo/environments/hpcloud.json
  "name": "production_hpcloud",
  "description": "HP Cloud Servers",
  "json_class": "Chef::Environment",
  "chef_type": "environment",
  "override_attributes": {
  "datacenter": "hpcloud"

% knife environment from file environments/hpcloud.json
% knife environment show production_hpcloud

Create a new bootstrap cookbook

This cookbook will

  • install base packages: htop, iftop, vnstat, vim, tree, curl, ntp, ntpdate, byobu
  • create new unix accounts from users data bag
  • put the users in their respective groups.

% knife cookbook create bootstrap and copy source file linked below

File cookbooks/bootstrap/recipes/default.rb

Create a new yet_site cookbook

This cookbook will

  • create /var/www/yet owned by www-data:www-data
  • add all data bag users which are declared in www-data to this group by using opscode users cookbook, it require a depends "users" in cookbook metadata.rb
  • enable Nginx Yet site by symlinking sites-enabled/yet to sites-available/yet

% knife cookbook create yet_site to create the cookbook, see content below.

File cookbooks/yet_site/recipe/default
include_recipe "users"
include_recipe "nginx"

directory "/var/www" do
  owner "www-data"
  group "www-data"
  mode "0775"

directory "/var/www/yet" do
  owner "www-data"
  group "www-data"
  mode "0775"

users_manage "www-data" do
  group_id 33

template "#{node['nginx']['dir']}/sites-available/yet" do
  source "yet.erb"
  owner "root"
  group "root"
  mode 00644

nginx_site "yet" do
  action :enable
File cookbooks/yet_site/templates/default/yet.erb
server {
  listen 80;

  root /var/www/yet;

Create a base role

A role with configuration common to all nodes

File roles/base.rb
name "base"
description "Base role applied to all nodes."

  :authorization => {
    :sudo => {
      :users => ["ubuntu", "brauns"],
      :passwordless => true

Note: chef-client recipe used to configure our node with a chef daemon which runs every 1800 seconds with a variance of 20 seconds and a log file at /var/log/chef.

% knife role from file roles/base.rb to upload the new role to the Chef Server

Create a yet_server role

This role will be used to apply Nginx, yet_site recipes and disable default_site.

File chef-repo/roles/yet_server.rb
name            "yet_server"
description     "YET static site server"

run_list        "recipe[nginx]", "recipe[yet_site]"

  "nginx" => {
    "default_site_enabled" => false

% knife role from file roles/yet_server.rb to upload the new role to the Chef Server

Create databags for unix accounts

users cookbook consume a databag to create sysadmin account on all nodes, it’s recommended to use the same user account as on your Chef Workstation.

First, create a new data bag file named after the user you want to create:

% mkdir -p data_bags/users
% vi data_bags/users/$USER.json

    "id": "USER",
    "ssh_keys": "ssh-rsa AAAAB3N...9Ve4w7b",
    "groups": [ "admin", "adm", "www-data" ],
    "uid": 2000,
    "shell": "\/bin\/bash"

ssh_keys should contains your ~/.ssh/ key, use an Array if you have multiple keys to upload.

% knife data bag create users to create users databag on the Chef Server

% knife data bag from file users $USER.json to add the first sysadmin

Bootstrap an HP cloud instance using Knife

We can now bootstrap a new node using the command below which assume you’ve prepared your environment for knife hp

% knife hp server create --flavor 100 --image 48335 --ssh-key USERNAME -N --ssh-user ubuntu -r 'role[base],role[yet_server]'

Refer to our HP cloud cheatsheet for details.

% knife search node "role:yet_server" to check if your node is provisionned correctly

Note: On Ubuntu 12.04 (precise), knife bootstrapping use the following template


You can change it and provide it with the –template argument, place it under ~/.chef

This template do the following :

  • configure env variable http_proxy to knife_config[:bootstrap_proxy]
  • install packages: *Ruby 1.8-dev, build-essential, wget, libruby1.8, rubygems
  • update gems
  • install gems: ohai, chef
  • create /etc/chef directory
  • output validation_key to /tmp/validation.pem, modifies (remove blank lines, mode 0600) and sent to /etc/chef
  • output encrypted_databag_secret to /tmp, modifies (remove blank lines, mode 0600) and send to /etc/chef
  • output Ohai hints if @chef_config[:knife][:hints] to /etc/chef/ohai/hints
  • output config_content -> /etc/chef/client.rb
  • output first_boot.json -> /etc/chef/first-boot.json which contains {“run_list”:[]}
  • start chef

Note: to Synchronise clock on your Chef Workstation if it is out of sync:

% sudo systemctl restart ntpd.service

Add host attribute to node

% knife node edit
  "name": "",
  "chef_environment": "production_hpcloud",
  "normal": {
    "host": "",
    "tags": [

    "runit": {
      "sv_bin": "/usr/bin/sv",
      "chpst_bin": "/usr/bin/chpst",
      "service_dir": "/etc/service",
      "sv_dir": "/etc/sv"
    "chef_client": {
      "bin": "/usr/bin/chef-client"
    "ohai": {
      "plugins": {
  "run_list": [

Associate role to node

If you didn’t provide run_list info while boostrapping with -r argument, you now have to associate your node with your run_list

% knife node run_list add 'role[base], role[yet_server]'

Run chef-client

You can force a chef-client run on your remote node with:

% knife ssh -x ubuntu "sudo chef-client"

Note: Don’t forget to update your DNS record to resolve your host FQDN

Rake tasks for Rsync deployment

# encoding: utf-8

ssh_user              = "ubuntu@yet"
remote_root           = "/var/www/yet/"

desc "builds and deploy"
task :deploy do
  puts "*** compile yet ***"
  system "nanoc compile"
  puts "*** deploy yet ***"
  system "rsync -avz --delete output/* #{ssh_user}:#{remote_root}"

Update default security group

to allow TCP/80,

% hpcloud securitygroups:rules:add default tcp -p 80..80
% hpcloud securitygroups:rules default

refer to my HP Cloud cheatsheet to install hpcloud.

Upgrade all nodes packages

% knife ssh 'name:*' -x ubuntu "sudo aptitude upgrade -y"

SSH Configuration

Note: to avoid SSH issues, switch to non StrictHostKeyChecking for HP Cloud IP addresses in your /etc/ssh/ssh_config

Host 15.*
  StrictHostKeyChecking no

Links, Tips & Tricks

  • /usr/lib/update-notifier/apt-check -p --human-readable list packages available for update on Ubuntu/Debian
  • knife search node "name:NODENAME" -F json dump a node to JSON to get all its details
  • knife ssh -x ubuntu "id:*" "uptime" Get uptimes of all your hosts or run any command remotly
  • Attributes docs and samples on Chef Wiki.
  • A Brief Chef Tutorial (From Concentrate) article
  • Some Awk one liners


As you’ve seen in this article, building up YET using Opscode Chef is great because if we need to build it again we won’t have to redo all the steps one more time. It is just necessary to bootstrap a new node in any cloud, he will then come up ready to receive our content using Rsync and will publish it using Nginx. If you want, you can now procede to part 2.