Table of contents

Basic Cookbook Design Patterns

How to use and design cookbooks. There are some common design patterns:

  • In a cookbook, we can use recipes and resources defined in other cookbooks.
  • This leads to the need for dependency management.
  • Berkshelf was created according to these patterns to help with dependency management.

Library Pattern

  • A library cookbook has no recipes.
  • It only defines one or more resources.
  • It cannot be used directly; it has to be used from another cookbook.
  • In that scenario, the resource defined in the library cookbook is used in one or more of the cookbook’s recipes.
  • The library cookbook has to be defined as a dependency in the cookbook that is using it.
  • There are cookbooks that have both resource definitions and recipes. They can be used both as library cookbooks and as concrete cookbooks.

Application Pattern

This is a generic pattern that has been implemented as a cookbook. The Application cookbook can be used to deploy applications in multiple containers. Based on this cookbook are the following concrete cookbooks:

  • application_java (deploying Java apps in Tomcat and other containers)
  • application_nginx (configuring Nginx as a reverse proxy for services)
  • application_php (deploying PHP web apps)
  • application_python (deploying Django web apps)
  • application_ruby (deploying Ruby on Rails web apps)
  • This pattern is commonly used for service deployments.

Environment Pattern

  • A cookbook that contains the definition of a full environment.
  • It can be used instead of roles to express the expected state of one or more systems.
  • This means that you can have environment cookbooks that are used by all your systems.
  • Or environment cookbooks that are used only by one system that performs a specific role.
  • It is an alternative to Chef roles.
  • The benefit compared to roles is that environment cookbooks can be versioned.

Wrapper Pattern

  • Sometimes we want to use a community cookbook with a few modifications in places.
  • We could fork it, but that is not best practice because we miss any upstream updates.
  • The best practice is to create a wrapper around it.
  • Our cookbook will override any attributes and recipes we need to change and include the rest of them.
  • The wrapper pattern can be used both for cookbooks and roles.

Chef-Related Toolchain - ChefWorkstation

  • The Chef ecosystem relies on Ruby. Although Chef Server versions 12+ have been rewritten using Erlang, the ecosystem still uses Ruby.
  • In the old days, we had to install the various tools manually.
  • These days, the best way to get all Chef-related tools is to use the Chef Workstation Kit.
  • ChefWorkstation contains:
    • An embedded Ruby.
    • All the basic gems and utilities needed for Chef development, such as:
      • knife/chef - tools that can be used to manipulate Chef resources: create/delete cookbooks, upload cookbooks to Chef Server, bootstrap nodes, etc.
      • rubocop - a linter.
      • foodcritic - another linter.
      • test kitchen - a Chef code testing tool.
      • inspec - an infrastructure code testing framework.

Chef Workstation Structure

  • A Chef workstation is a system that can be used to manage Chef resources.
  • In the simplest terms, it’s a collection of files and directories with a specific structure.

Dependency Management in a Chef Workstation

  • Dependency management is done using Berkshelf.
  • This is where all of our cookbooks and their versions are documented.
  • The Berkshelf tool is called to download all the cookbooks from their repos and vendor them on disk.
  • Berkshelf also manages any transitive dependencies. If there are dependency conflicts, Berkshelf crashes, and we need to resolve the dependency errors before we can proceed.

Update Scripts

We have created wrapper scripts for a number of very common operations:

update-everything.sh - Update all resources to Chef Server.

./vendor-cookbooks.sh
./push-to-chef.sh

vendor-cookbooks.sh - Download all cookbooks from their repos to cookbooks directory.

rm Berksfile.lock
berks vendor cookbooks

push-to-chef.sh - Push to Chef Server all cookbooks found in cookbooks directory and all roles found in roles directory.

knife cookbook upload --all
knife role from file roles/*.json

In order for all these to work, we need a .chef directory with the necessary keys and configuration files.

knife.rb Configuration File

knife.rb is the master configuration file for a Chef workstation. It defines which keys will be used to communicate with Chef Server and the URL of the server. It is used by the knife and chef utilities.

current_dir = File.dirname(__FILE__)
log_level                :info
log_location             STDOUT
node_name                "coe_admin"
client_key               "#{current_dir}/coe_admin.pem"
validation_client_name   "dicoe-validator"
validation_key           "#{current_dir}/dicoe-validator.pem"
chef_server_url          "https://chef.dca.diginsite.net/organizations/dicoe"
cookbook_path            ["#{current_dir}/../cookbooks"]
ssl_verify_mode          :verify_none

Chef Resource Management Shell Scripts

Chef resource management can be done either through its proprietary web interface or by command line using the chef and knife utilities. We have created a number of function-oriented wrapper scripts to make things simpler. These scripts can be found under the coe_knife_tools directory.

getnodestatus.sh

A simple script that retrieves the full status of the cluster:

#!/bin/bash
knife status

gethostnames.sh

Retrieve the hostnames and Chef node names of all the systems:

#!/bin/bash
for i in $(knife node list)
do
  knife node show $i -a fqdn
done

editnoderunlist.sh

A simple wrapper that allows us to edit the definition of a Chef node:

#!/bin/bash
NODENAME=$1
export EDITOR='vim'
knife node edit $1