Chef is a configuration management tool written in Ruby and Erlang. It uses a domain-specific language (DSL) for writing system configuration recipes. Chef helps solve the problem of configuring and maintaining (configure/manage) a company’s servers. Chef utilizes a declarative approach, meaning we specify what the final configuration should be and not have to specify exactly all the steps needed to make it happen.
Chef falls under the whole Infrastructure as Code idea. Benefits include:
Chef Infra is a systems and cloud infrastructure automation framework. Each organization is made up of one or more ChefDK Installations, a single Chef server, and every node that will be configured and maintained by Chef Client. Cookbooks (and recipes) are used to tell Chef Client how to do the actual configuration.
Some important tools in ChefDK are:
Chef cares about the end result, not the steps to get there.
So what does this all mean? There’s two ways to define our configuration, either Imperative vs Declarative.
An imperative configuration might look like:
yum install -y nginx
systemctl start nginx
systemctl enable nginx
We have to be very specific about what happens.
A declarative configuration might look like:
package "nginx"
service "nginx" do
action [:enable, :start]
end
The above doesn’t care if we’re running systemd.
ChefDK defines a common workflow for cookbook development as:
rpm -Uvh chef-server-core.rpm
) to install Chef Server and provide a few utilitieschef-server-ctl reconfigure
to configure its own serviceschef-server-ctl service-list
to see what services the chef server manages
E.g. okshelf
nginx
oc_bifrost
oc_id
opscode-chef-mover
opscode-erchef
postgresqlchef-server-ctl user-create USER_NAME FIRST_NAME LAST_NAME EMAIL 'PASSWORD' --filename FILE_NAME
E.g. chef-server-ctl user-create will Will Liu william.q.liu@gmail.com 'mypassword' --filename /home/user/will.pem
where we will output an RSA key used to interact with the Chef server from a workstation later onchef-server-ctl org-create SHORT_ORG_NAME 'FULL_ORG_NAME' --association_user USER_NAME --filename FILE_NAME
E.g. chef-server-ctl org-create myorg 'My Organization' --association_user will --filename my-validator.pem
where the
–association_user flag takes an existing user's username and associates it with the
admin security group
The
–filename` flag stores the organization’s validator pemchef-server-ctl install chef-manage
Then run chef-server-ctl reconfigure
and then chef-manage-ctl reconfigure
ChefDK is the development kit that we will install on our workstation (e.g. our labtop)
rpm -Uvh chefdk.rpm
chef --version
chef –version
Chef Development Kit Version: 2.5.3
chef-client version: 13.8.5
delivery version: master (73ebb72a6c42b3d2ff5370c476be800fee7e5427)
berks version: 6.3.1
kitchen version: 1.20.0
inspec version: 1.51.21which ruby
eval "$(chef shell-init bash)"
chef generate repo generated-chef-repo
When we use a chef generator, it makes it easier to follow standard Chef development practicesknife
utility. Configure it with knife configure
knife node list
, knife ssl fetch
)A Chef Node is the server that we will configure using chef. We won’t ssh into this server and will basically just run knife commands on it.
knife
with -x
(for ssh USERNAME), -N
(pass in a node name), -P
(for password)
In our chef-repo, we will run knife bootstrap myserverlocation.someplace.com -N web-node-1 -x user -P 'mypassword' --sudo
knife node list
and you should see web-node-1
chef-client
and we’ve now
setup chef-client
to run on our ‘web-node-1’chef-client
process:client.rb
file and Ohai attributesnode
object from Chef server if this isn’t the first chef-client
run.
After the pull, the node object is rebuilt based on the node’s current state.run-list
- compiles the list of roles and recipes to be appliedrun-list
run-list
chef-client
run finishes successfully. Also execute the exception and report handlers in the proper order.chef-client
waits until the next time it is executedThe Chef Supermarket is basically like the pypy, rubygems, or dockerhub, except we have a lot of cookbooks and plugins. Even though there is a public supermarket, you can run the supermarket code and deploy a private supermarket.
There is Berkshelf (kinda like Ruby’s Bundler) to install and manage cookbook dependencies and versions (aka dependency manager) There is also Stove used to version and publish cookbooks to a supermarket (either public or private)
We use test kitchen (aka Kitchen) as a testing harness that allows us to easily provision environments
using a variety of platforms and backends so that we can test our infrastructure code. The basic structure
for a kitchen.yml
file looks like:
driver:
name: driver_name
provisioner:
name: provisioner_name
verifier:
name: verifier_name
transport:
name: transport_name
platforms:
- name: platform-version
driver:
name: driver_name
- name: platform-version
suites:
- name: suite_name
run_list:
- recipe[cookbook_name::recipe_name]
attributes: { foo: "bar" }
excludes:
- platform-version
- name: suite_name
driver:
name: driver_name
run_list:
- recipe[cookbook_name::recipe_name]
attributes: { foo: "bar" }
includes:
- platform-version
A lot of times you’ll run into DNS / Network issues. You can usually add -V
to make your command more verbose
to get more feedback. You can modify the /etc/hosts
if you want to connect to servers without needing DNS to propogate.
Chef Cookbooks are the unit of configuration and policy distribution. A cookbook defines a scenario and contains everything that is required to support that scenario. You can only have one cookbook of a specific name. Use underscore instead of dashes in names.
recipes/
dir) - specify the resources to use and the order in which they are to be appliedattributes/
dir) - an attribute can be defined in a cookbook (or a recipe) and then used to override
the default settings on a node. Attributes in the default.rb
file are loaded first and then additional
attribute files (if present) are loaded in lexical sort orderfiles/
) - a file distribution is a specific type of resource that tells a cookbook how to distribute files,
including by node, by platform, or by file version.libraries/
) - a library allows the use of arbitrary Ruby code in a cookbook, either as a way to extend the
Chef Infra Client language or to implement a new classresources/
) - a custom resource is an abstract approach for defining a set of actions and
(for each action) a set of properties and validation parameterstemplates/
) - a template is a file written in markup language that uses Ruby statements to solve
complex configuration scenariosmetadata.rb
) - file that contains information about the cookbook such as the cookbook name, version, descriptionAn attribute is a specific detail about a node. Attributes are used by Chef Infra Client to understand:
There are six types of attributes to determine the value that is applied to a node during a Chef Infra Client run.
Most of the time, we just use default
default
- a default attribute is automatically reset at the start of every Chef Infra Client run and has the
lowest attribute precedence. Use default
as often as possible in cookbookes.
Other attribute types include: force_default
, normal
, override
, force_override
, automatic
Files are managed using the following resources:
cookbook_file
resource to manage files that are added to nodes based on files that are located in
the /files
directory in a cookbookfile
resource to manage files directly on a noderemote_file
resource to transfer files to nodes from remote locationstemplate
resource to manage files that are added to nodes based on files that are located in the
/templates
directory in a cookbookA library allows arbitrary Ruby code to be included in a cookbook, usually included in the /libraries
directory.
You can do anything in a library file. Good use cases include:
A recipe is the most fundamental configuration element within the organization. A recipe is:
A recipe has to be assigned to a run-list using the appropriate name (as defined by the cookbook directory and namespace). For example, with the following structure:
cookbooks/
apache2/
recipes
default.rb
mod_ssl.rb
We have two recipes (`default.rb` and `mod_ssl.rb`), which if it translated to a run-list, would look like:
{
'run_list': [
'recipe[cookbook_name::default_recipe]',
'recipe[cookbook_name::recipe_name]'
]
}
The `default_recipe` does not need to be specified since it is implied.
A resource is a statement of configuration policy that:
package
, template
, service
)A resource is a Ruby block with four components: a type, a name, one (or more) properties (with values), and one (or more) actions. The syntax looks like:
type 'name' do
attribute 'value'
action :type_of_action
end
Every resource has its own set of actions and properties. Depending on the resource type, there is always
a default action (e.g. package
resource has the default action :install
and the name of the package defaults to
the name
of the resource)
For example, a resource that is used to install a tar.gz
package for version 1.16.1 looks like:
package 'tar' do
version '1.16.1'
action :install
end
This can also be written as:
package 'tar'
The resource block that installs a tar.gz package would look like this since we do not have to specify the default action of install.
package 'tar' do
version '1.16.1'
end
https://docs.chef.io/resource_common.html
You can specify a lot of different actions for your resource, including :nothing
(which does nothing). This looks like:
service 'memcached' do
action :nothing
end
https://docs.chef.io/resource_common.html
You can specify a lot of different properties for your resource. Some more common properties that are common to every resource include:
ignore_failure
- true
or false
to continue running a recipe if a resource fails for any reasonretries
- the number of attempts to catch exceptions and retry the resourceretry_delay
- the retry delay in secondssensitive
- ensure that sensitive resource data is not logged by Chef Infra ClientAn example of using one of these properties looks like:
service 'apache' do
action [ :enable, :start ]
retries 3
end
Guards
Additional Resource Properties include guards, which is evaluated during the execution phase and tells
Chef Infra Client whether or not it should continue executing a resource. A guard accepts either a string
value or a Ruby block value and has a prefix of not_if
or only_if
.
execute 'bundle install' do
cwd '/myapp'
not_if 'bundle check' # this is run from /myapp
end
Lazy Evaluation
In certain cases the value for a property cannot be known until the execution phase of a Chef Infra Client run. Use a lazy evaluation looks like this:
attribute_name lazy { code_block }
Notifies
A resource can notify another resource to take action when its state changes.
notifies :action, 'resource[name]', :timer
An example might look like:
template '/etc/nagios3/configures-nagios.conf' do
# other parameters
notifies :run, 'execute[test-nagios-config]', :delayed
end
By default, notifications are delayed until the very end of a Chef Infra Client run. You can run the action
immediately with :immediately
instead of :delayed
A cookbook template is an Embedded Ruby (ERB) template that is used to dynamically generate static text
files. Templates can have Ruby expressions and statements and are a great way to manage configuration files.
Use a template resource to add cookbook templates to recipes. The idea is to transfer files from the COOKBOOK_NAME/templates
dir to a specific path located on a host that is running Chef Infra Client.
Syntax
template '/etc/motd' do
source 'motd.erb'
owner 'root'
group 'root'
mode '0755'
end
This means that /etc/motd
is the location where the file gets created and motd.erb
is the template used.
Data bags store global variables as JSON data. Data bags can be loaded by a cookbook or accessed during a search.
Berkshelf is a dependency manager for Chef cookbooks. You can depend on community cookbooks and have them safely included in your workflow. Berkshelf is included in ChefDK.
You can chef generate cookbook -b
or --berks
to create a Berksfile in the root of the cookbook.
The Berksfile will be placed alongside the cookbook’s metadata.rb
file (which has your cookbook’s
dependencies to the metadata.rb file).
Metadata.rb
name 'my_first_cookbook'
version '0.1.0'
depends 'apt', ~> 5.0
Berksfile
source 'https://supermarket.chef.io'
metadata
If you run berks install
, the apt cookbook will be downloaded from Supermarket into the cache.
You can upload all cookbooks to your Chef Infra Server with berks upload
A Berksfile describes the set of sources and dependencies to use in a cookbook and is usually
used with the berks
command. A Berksfile is a Ruby file that has sources, dependencies, and options
specified.
source "https://supermarket.chef.io"
metadata
cookbook "NAME" [, "VERSION_CONSTRAINT"] [, SOURCE_OPTIONS]
A source defines where Berkshelf should look for cookbooks. Sources are processed in the order that they are defined in and stops processing as soon as a suitable cookbook is found. Sources can be Supermarket, Chef Infra Server, or local Chef repository.
To add a private Supermarket, which will be preferred:
source "https://supermarket.example.com"
source "https://supermarket.chef.io"
To add a Chef Infra Server:
source "https://supermarket.chef.io"
source :chef_server
To add a local Chef repository:
source "https://supermarket.chef.io"
source chef_repo: ".."
The location and authentication details for the Chef Infra Server is taken from the user’s config.rb
by default