Tuesday 3 January 2017

Chapter 9. Manage Multiple Nodes at Once with Chef Server

We’ve shown you how much you can do with Chef without ever needing to install its server component. However, to get the full benefits of Chef, you need to set up a Chef Server in your production environment. Using a Chef Server is recommended when you need to manage more than one machine at a time with Chef, which is typically how Chef is used. Chef Server adds more capabilities that can be used in your cookbooks, including roles, environments, data bags, and powerful search.
As of this writing, there are three flavors of Chef Server, as detailed in Table 9-1. Although there are subtle differences, they all contain common features, including an API endpoint, data bags, environments, node objects, roles, and search. Some of these terms are new; we will cover them in the remaining chapters of the book.
Table 9-1. Types of Chef Server
FlavorDetails
Hosted Enterprise Chef
Formerly called Hosted Chef, Hosted Enterprise Chef is “Chef as a Service”—software as a service. It is cloud-based and highly scalable, and comes with an industry-standard service-level agreement. It requires no setup or configuration of the server itself.
Enterprise Chef On-Premises
Enterprise Chef On-Premises, formerly called Private Chef, is a Chef Server inside an organization’s firewall. It is designed to be deployed inside an organization’s infrastructure, and includes additional features on top of Hosted Enterprise Chef. Enterprise Chef On-Premises is most useful to organizations that must comply withHIPAA or PCI compliance issues, large organizations that wish to manage their own servers, and companies that require tight control and auditing of data.
Open Source Chef Server
As the name suggests, Open Source Chef Server is a free, open source version of Chef Server that includes a subset of premium Chef Server features available in Enterprise Chef, most useful to small organizations.
Chef Server is the centralized store for configuration data in your infrastructure. It stores and indexes cookbooks, environments, templates, metadata, files, and distribution policies. Chef Server is aware of all machines it manages, and in this way, Chef Server also acts as an inventory management system.
As of Chef 11, Chef Server is written in Erlang, a programming language designed with concurrency in mind. Chef Server is also composed of a web server, cookbook store, web interface, messaging queue, and backend database. Figure 9-1 shows how each of these pieces interact.
Architecture of the Chef Server
Figure 9-1. Chef Server architecture
Web server
The nginx web server is a simple reverse proxy server that acts as the front-end interface for Chef Server (Erchef). It also performs load balancing for Chef Server. All requests to the Chef Server API are routed through the nginx web server.
WebUI
The WebUI is the consumer-facing web application. It is a Ruby on Rails application that provides a web-guided user interface for interacting with Chef Server.
Chef Server
Erchef is the component of Chef Server that processes API requests. As its name suggests, Chef Server is written in Erlang for high concurrency and reliability. Even though Chef Server is written in Erlang, it is still capable of running Ruby code. In fact, writing Erlang Chef recipes is not supported at this writing. Erchef is the core API for Chef Server.
Bookshelf
The Bookshelf is the central store for all Chef cookbooks and cookbook contents (such as files, templates, definitions, metadata, and recipes). Each cookbook is automatically checksummed and versioned. Bookshelf is a flat-file database and is intentionally stored outside of Chef Server’s index.
Search Index
The Search Index is an Apache Solr server that handles the indexing and searching mechanism for various API calls, both internally and externally. The server is wrapped by chef-solr, which exposes a RESTful API.
Message queues
The queues handle all messages that are sent to the Search Index for parsing. The queues are managed by RabbitMQ, an open source queuing system. chef-expander pulls messages from the message queues, formats the messages, and then sends the messages to the Search Index.
Database
The database is a PostgreSQL persistent data store. Prior to Chef 11, the data store was CouchDB, but was moved to PostgreSQL due to CouchDB’s inability to scale.
In the first part of this chapter, we’ll install Enterprise Chef On-Premises Server in a sandbox environment using Test Kitchen. In order to set up the server in a virtual machine, you’ll need at least 2 GB of free memory on your machine—1.5 GB for Chef Server itself plus 512 MB for the accompanying node.
With Enterprise Chef On-Premises, you can manage up to five nodes for free, more than enough for learning all the enterprise capabilities of Chef Server. Appendix A covers the installation of Open Source Chef Server, should you not wish to provide contact information and don’t currently need the advanced capabilities of Enterprise Chef in your organization. With Open Source Chef Server, you can manage an unlimited number of nodes for free. Appendix B presents a similar overview of Hosted Enterprise Chef, should you wish to explore the Chef—it also offers the ability manage up to five nodes with its free tier.

How to Install Enterprise Chef Server Manually

As shown in Figure 9-2, go to http://www.getchef.com/contact/on-premises and provide your contact information to receive download details and installation instructions for Enterprise Chef Server.
Register to receive On-Premise Enterprise Chef download details
Figure 9-2. Register to receive On-Premise Enterprise Chef download details
Once you accept the agreement, you’ll be presented a download link page as shown in Figure 9-3. Bookmark this download link page in your web browser for future reference. Copy the link for the Red Hat Enterprise Linux 6 download, as shown.
Enterprise Chef Download links
Figure 9-3. Enterprise Chef Download links
To manually replicate a basic Enterprise Chef install in a cookbook, we first need to download the Enterprise Chef Server install package for Red Hat Enterprise Linux 6, as we’ll be installing on CentOS 6.5. To match the exercises in the book, use version 11.1.8. Use the Copy Link Address option on the download link to copy the full download URL to your clipboard.

NOTE

The download page may not match the images presented in this book exactly. The download and install procedure should be similar even if the web presentation is different.
The rest of the steps necessary to install Chef Server are displayed below the download link:
  1. Install the chef-server package.
  2. Run sudo private-chef-ctl reconfigure.

Install Enterprise Chef Server

Assuming you have sufficient resources to install Enterprise Chef Server locally along with a test node, let’s create an enterprise-chef cookbook that will install Enterprise Chef Server. To maintain consistency with Hosted Enterprise Chef, create the directory chef-repo/cookbooks and create the enterprise cookbook in that directory. Having a top-level chef-repo directory will help you handle the additional files necessary to manage Enterprise Chef beyond the cookbooks themselves. You’ll definitely be using more than one cookbook in your organization, so we suggest putting them in a chef-repo/cookbooks subdirectory.
Create the chef-repo/cookbooks directory and make it the current working directory.
Linux/Mac OS X:
$ mkdir -p chef-repo/cookbooks
$ cd chef-repo/cookbooks
Windows:
> mkdir chef-repo\cookbooks
> cd chef-repo\cookbooks
Then generate the enterprise-chef cookbook with chef generate cookbook or knife cookbook create, depending on whether you are using the Chef Development Kit or the Chef Client. We’re going to go through the cookbook creation steps quickly in this chapter. If you need a refresher on what each of these commands mean and the expected output, refer back to Chapter 7.
Chef Development Kit:
$ chef generate cookbook enterprise-chef
$ cd enterprise-chef
Chef Client:
$ knife cookbook create enterprise-chef --cookbook-path .
$ cd enterprise-chef
$ kitchen init --create-gemfile
$ bundle install
As shown in Example 9-1, edit the .kitchen.yml file to use the CentOS 6.5 basebox we prepared specifically for this book. Also, assign a private network address like we did in Chapter 7. This time, we’re going to use the IP address 192.168.33.34. If this conflicts with an address already being used on your local network, change it to a nonconflicting one. We also need more memory than the default 512 MB allocated, so add a customize:block with a memory: statement to increase the memory to 1.5 GB (memory is specified in megabytes only).

NOTE

Make sure that you use the chef_solo provisioner for this cookbook, as the in-memory Chef Server the chef_zero provisioner spawns will cause a conflict with the hands-on exercises coming up in Chapter 10. As of this writing, if you want to automate the installation of a Chef Server with Chef cookbooks, using Chef Solo is recommended so that the deployment code doesn’t get confused by the presence of the in-memory Chef Server used in Chef Zero.
Example 9-1. chefdk/chef-repo/cookbooks/enterprise-chef/.kitchen.yml
---
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
  - name: centos65
    driver:
      box: learningchef/centos65
      box_url: learningchef/centos65
      network:
      - ["private_network", {ip: "192.168.33.34"}]
      customize:
        memory: 1536

suites:
  - name: default
    run_list:
      - recipe[enterprise-chef::default]
    attributes:
Generate a default attributes file in attributes/default.rb.
Chef Development Kit:
$ chef generate attribute default
Chef Client:
$ touch attributes/default.rb
Add an attribute specifying the download URL for the Chef Server package that you obtained from the Enterprise Chef download link page. We recommend using the 11.1.8 version URL as shown in Example 9-2, as we wrote the examples for this chapter for this version of Chef.
Example 9-2. chefdk/chef-repo/cookbooks/enterprise-chef/attributes/default.rb
default['enterprise-chef']['url'] = \
'https://s3.amazonaws.com/opscode-private-chef/el/6/x86_64/'\
'private-chef-11.1.8-1.el6.x86_64.rpm'
Let’s take an initial stab at coding a recipe to replicate the manual steps to install Chef Server outlined in How to Install Enterprise Chef Server Manually. Enter in the first version of the code as shown in Example 9-3. Let’s go over some of the highlights of the code in the following paragraphs.
Rather than typing in long variable names like node['enterprise-chef']['url'], feel free to use temporary local variables in a recipe with shorter names, such as:
package_url = node['enterprise-chef']['url']
Remember that you have the full power of Ruby classes and methods available to you in your Chef recipes, so don’t be afraid to use it. For example, you can use the ::File.basename() method to extract the package name from the URL. The package name is the last component of the URL after the forward slash (“/”): private-chef-11.1.8-1.el6.x86_64.rpm. Refer to the Ruby core API documentation for more information on the ::File class:
package_name = ::File.basename(package_url)
Unfortunately, the package resource does not work with URLs, so we’re introducing a new resource, theremote_file resource. The remote_file resource will download files from a remote location. Rather than hardcoding a path like “/tmp” for the package download, Chef provides a variable you should use instead: Chef::Config[:file_cache_path]. Let Chef choose the best place to store temporary files for you. Pass the local path where you want to store the file as a string parameter to remote_file or as a name attribute; in this case, we use the package_local_path variable. The download URL should be passed to remote_fileas the source attribute.
The package resource should be familiar to you by now, as we used it in Chapter 7.
In order to execute the chef-server-ctl reconfigure, we need to introduce another new resource, theexecute resource. When you fail to find a resource that meets your needs, you can use the execute resource to run arbitrary shell commands. Pass the shell command you want to execute as a string parameter to the executeresource.
Here’s the full code listing shown in Example 9-3.
Example 9-3. chefdk/chef-repo/cookbooks/enterprise-chef/recipes/default.rb
#
# Cookbook Name:: enterprise-chef
# Recipe:: default
#
# Copyright (C) 2014
#
#
#

package_url = node['enterprise-chef']['url']
package_name = ::File.basename(package_url)
package_local_path = "#{Chef::Config[:file_cache_path]}/#{package_name}"

# omnibus_package is remote (i.e., a URL) let's download it
remote_file package_local_path do
  source package_url
end

package package_local_path

# reconfigure the installation
execute 'private-chef-ctl reconfigure'
Run kitchen converge to install Enterprise Chef Server, and use kitchen login to verify that the private-chef package was installed. The kitchen converge will take some time, perhaps as long as 10 to 15 minutes, as it needs to download an 800-MB installation package for Enterprise Chef:
$ kitchen converge default-centos65
$ kitchen login default-centos65
[vagrant@default-centos65 ~]$ rpm -q private-chef
private-chef-11.1.8-1.el6.x86_64
[vagrant@default-centos65 ~]$ exit
logout
Connection to 127.0.0.1 closed.

Introducing Idempotence

Although the recipe we created in Example 9-3 is a good first attempt, it is not idempotent. When Chef code is idempotent, it can run multiple times on the same system and the results will always be identical, without producing unintended side effects. All Chef default resources are guaranteed to be idempotent with the exception of the execute resource.
execute resources are generally not idempotent, because most command-line utilities can be run only once. They assume that a human being is interacting with the system and understands the state of the system. For example, assuming the file /learningchef/file1.txt exists, the following mv command will work the first time it is run, but it will fail the second time:
$ mv /learningchef/file1.txt /file1.txt
A great way to test to see if your recipe is idempotent is to run kitchen converge twice. When a recipe has no unintended side effects, there should be 0 resources updated on the second run.
Does our recipe pass the idempotency test? Sadly, no. Here’s a sampling of the output from an initial kitchen converge:
$ kitchen converge
-----> Starting Kitchen (v1.2.2.dev)
-----> Creating <default-centos65>...
...
       Starting Chef Client, version 11.14.2
       [2014-08-14T19:25:13-07:00] INFO: *** Chef 11.14.2 ***
       [2014-08-14T19:25:13-07:00] INFO: Chef-client pid: 2387
       [2014-08-14T19:25:15-07:00] INFO: Setting the run_list to
       ["recipe[enterprise-chef::default]"] from CLI options
       [2014-08-14T19:25:15-07:00] INFO: Run List is
       [recipe[enterprise-chef::default]]
       [2014-08-14T19:25:15-07:00] INFO: Run List expands to
       [enterprise-chef::default]
       [2014-08-14T19:25:15-07:00] INFO: Starting Chef Run for default-centos65
       [2014-08-14T19:25:15-07:00] INFO: Running start handlers
       [2014-08-14T19:25:15-07:00] INFO: Start handlers complete.
       Compiling Cookbooks...
       Converging 3 resources
...
       [2014-08-14T19:36:22-07:00] INFO: Chef Run complete in 666.814045747
       seconds

       Running handlers:
       [2014-08-14T19:36:22-07:00] INFO: Running report handlers
       Running handlers complete
       [2014-08-14T19:36:22-07:00] INFO: Report handlers complete
       Chef Client finished, 3/3 resources updated in 668.536290312 seconds
       Finished converging <default-centos65> (11m59.64s).
-----> Kitchen is finished. (12m33.89s)
Here’s the output from the second run. Chef mistakenly thinks there’s still stuff it needs to do—2/3 resources updated in this second run. If the recipe were truly idempotent, we’d see 0/3 resources updated. Chef would inspect the state of the system, recognize that nothing had changed since the last run—no one touched the node between the two runs—and perform no resource updates:
$ kitchen converge
-----> Starting Kitchen (v1.2.2.dev)
-----> Converging <default-centos65>...
...
       Converging 3 resources
       Recipe: enterprise-chef::default
         * remote_file[/tmp/kitchen/cache/private-chef-11.1.8-1.el6.x86_64.rpm]
         action create[2014-08-14T19:41:13-07:00] INFO: Processing
         remote_file[/tmp/kitchen/cache/private-chef-11.1.8-1.el6.x86_64.rpm]
         action create (enterprise-chef::default line 15)
        (up to date)
         * package[/tmp/kitchen/cache/private-chef-11.1.8-1.el6.x86_64.rpm]
         action install[2014-08-14T19:41:20-07:00] INFO: Processing
         package[/tmp/kitchen/cache/private-chef-11.1.8-1.el6.x86_64.rpm]
         action install (enterprise-chef::default line 19)

           - install version 11.1.8-1.el6 of package /tmp/kitchen/cache/
           private-chef-11.1.8-1.el6.x86_64.rpm

         * execute[private-chef-ctl reconfigure] action
         run[2014-08-14T19:41:28-07:00] INFO: Processing
         execute[private-chef-ctl reconfigure] action run
         (enterprise-chef::default line 22)
       [2014-08-14T19:41:39-07:00] INFO: execute[private-chef-ctl reconfigure]
       ran successfully

           - execute private-chef-ctl reconfigure

       [2014-08-14T19:41:39-07:00] INFO: Chef Run complete in 26.305913778
       seconds

       Running handlers:
       [2014-08-14T19:41:39-07:00] INFO: Running report handlers
       Running handlers complete
       [2014-08-14T19:41:39-07:00] INFO: Report handlers complete
       Chef Client finished, 2/3 resources updated in 27.930367706 seconds
       Finished converging <default-centos65> (0m30.94s).
-----> Kitchen is finished. (0m31.41s)
As mentioned earlier, most default Chef resources are idempotent. Notice that the remote_file resource is idempotent. It is reporting (up to date). The package resource is normally idempotent. We specifically crafted this example to show you a platform-specific quirk related to idempotency when it is used on RedHat-variant platforms, such as CentOS, which we’ll show you how to address.
There are some issues with the package and execute resources, however, as on the second kitchen converge run Chef:
  1. Reinstalled the rpm package, unnecessarily
  2. Executed chef-server-ctl reconfigure a second time
Let’s fix these idempotency issues in our code now. Example 9-4 has the final idempotent version of the code.
The first issue is a common one that Chef developers encounter with the package resource when they try to install from a downloaded rpm instead of using a package repository. Instead of using a package one-liner for a downloaded rpm, you need to tell the package resource to explicitly use the Chef::Provider::Package::Rpm provider using the provider attribute. You also need to specify the string representing the package name using the source attribute, like so:
package package_name do
  source package_local_path
  provider Chef::Provider::Package::Rpm
end

NOTE

You can use the rpm_package short name to specify Chef::Provider::Package::Rpm to the packageresource, if you prefer. The following code is equivalent to the preceding code:
rpm_package package_name do
  source package_local_path
end
Fixing the second issue with the execute resource is a little more involved. That’s why you should prefer built-in Chef resources over using the execute resource, because it’s up to you to make the execute resources idempotent.
One way to fix this issue is with a not_if guard to the execute resource. Guards are used to make a resource idempotent by allowing the resource to test for a desired state, and if the desired state is present, the resource should do nothing. In this case, we’ll test to see if the chef-server package is already installed, by adding a not_if clause to the execute resource as follows. not_if will test to see if the exit code of the command is 0; if so, the resource does nothing.

NOTE

If you need to test the opposite logic of not_if, there is also an only_if guard. It’s more typical to use only_if on Windows, given that a successful exit code for Windows commands is frequently the value 1 instead of 0. Take a look at http://bit.ly/common_functionality for more information.
execute "chef-server-ctl reconfigure" do
  not_if "rpm -q chef-server"
end
Although this is a reasonable way to address the issue, it’s a little clunky. You have to figure out a way to detect whether Chef Server is installed, and the method used in the previous example is not very reliable. A better approach is to trigger the execute when the package resource installs the package. You can trigger events in other resources with a notifies statement.
In order to use notifies, we’ll need to change the execute resource statement a bit. First, you’ll want to change the resource so it does nothing by default when execute is evaluated during the Chef run; we do this by adding an action :nothing attribute. Also, you’ll want to move the actual command line explicitly to the command attribute, so you can use a short name to trigger the execute block. By default, the name passed to the execute resource as a string parameter is used as the command attribute, which is great for a self-documenting one-liner. but not so great when you want to trigger the command by name. So let’s transform the executeresource like so:
# reconfigure the installation
execute 'reconfigure-chef-server' do
  command 'chef-server-ctl reconfigure'
  action :nothing
end
Then add the notifies attribute as follows. The notifies attribute takes three parameters: an action, the name of the resource to notify, and a timer indicating when the action should be perform. As shown in the following code block, we want to perform the :run action on the execute[reconfigure-chef-server] resource, and we want the action performed :immediately. For more information on notifies parameters, refer to the Chef documentation:
package package_name do
  source package_local_path
  provider Chef::Provider::Package::Rpm
  notifies :run, 'execute[reconfigure-chef-server]', :immediately
end
Example 9-4 shows what the final version of our idempotent code looks like.
Example 9-4. chefdk/chef-repo/cookbooks/enterprise-chef/recipes/default.rb
# Cookbook Name:: enterprise-chef
# Recipe:: default
#
# Copyright (C) 2014
#
#
#

package_url = node['enterprise-chef']['url']
package_name = ::File.basename(package_url)
package_local_path = "#{Chef::Config[:file_cache_path]}/#{package_name}"

# omnibus_package is remote (i.e., a URL) let's download it
remote_file package_local_path do
  source package_url
end

package package_name do
  source package_local_path
  provider Chef::Provider::Package::Rpm
  notifies :run, 'execute[reconfigure-chef-server]', :immediately
end

# reconfigure the installation
execute 'reconfigure-chef-server' do
  command 'private-chef-ctl reconfigure'
  action :nothing
end
Try running kitchen converge against this revised recipe, and note that it reports 0/2 resources updated, which is the result we are looking for; no resources are updated after running kitchen converge for the second time:
$ kitchen converge
-----> Starting Kitchen (v1.2.2.dev)
-----> Converging <default-centos65>...
...
       Converging 3 resources
       Recipe: enterprise-chef::default
         * remote_file[/tmp/kitchen/cache/private-chef-11.1.8-1.el6.x86_64.rpm]
         action create[2014-08-14T19:46:12-07:00] INFO: Processing
         remote_file[/tmp/kitchen/cache/private-chef-11.1.8-1.el6.x86_64.rpm]
         action create (enterprise-chef::default line 15)
        (up to date)
         * package[private-chef-11.1.8-1.el6.x86_64.rpm] action
         install[2014-08-14T19:46:20-07:00] INFO: Processing
         package[private-chef-11.1.8-1.el6.x86_64.rpm] action install
         (enterprise-chef::default line 19)
        (up to date)
         * execute[reconfigure-chef-server] action
         nothing[2014-08-14T19:46:20-07:00] INFO: Processing
         execute[reconfigure-chef-server] action nothing
         (enterprise-chef::default line 26)
        (skipped due to action :nothing)
       [2014-08-14T19:46:20-07:00] INFO: Chef Run complete in 8.771936095
       seconds

       Running handlers:
       [2014-08-14T19:46:20-07:00] INFO: Running report handlers
       Running handlers complete
       [2014-08-14T19:46:20-07:00] INFO: Report handlers complete
       Chef Client finished, 0/2 resources updated in 10.423184134 seconds
       Finished converging <default-centos65> (0m12.31s).
-----> Kitchen is finished. (0m12.86s)
Always check your recipes to see if they are idempotent before deploying them to production. If we had deployed the first version of this recipe in production, given that the chef-client usually runs on a periodic timer performing Chef runs, all our nodes would have been reinstalling the Chef Server package and reconfiguring the server every 15 minutes!

Configure Enterprise Chef Server

If your Enterprise Chef Server installed properly, you should be able to access the web admin console using the private_network IP address you configured in your .kitchen.yml. In our case, we used the address 192.168.33.34. After you dismiss a warning about the use of a self-signed SSL certificate, click on the Sign up link, as shown in Figure 9-4.
Sign up
Figure 9-4. Sign up
You will be prompted to create a user account. Enter in the required values, then click on the Submit button.Figure 9-5 shows the values we used for our user account; yours will be different, of course.
Once you click on the Submit button, the Enterprise Chef web UI indicates that the next step you should perform is to create an organization. Click on the Create link to create an organization, as shown in Figure 9-6.
Create a user
Figure 9-5. Create a user
An <organization> is the name of your company or organization. It is used as a unique identifier to authenticate your organization against Chef Server. Figure 9-7 shows how we filled out the organization fields in our setup.
Create an organization
Figure 9-6. Create an organization
Once you click on the Create Organization button, you will be prompted to save two files: the validation key and the knife configuration file, as shown in Figure 9-8:
  • <organization>-validator.pem
  • knife.rb
After you’ve download the <organization>-validator.pem and knife.rb files, click on your username link on the upper right-hand side of the web page as shown in Figure 9-9. Our username is misheska; yours will be different.
As shown in Figure 9-10, click on the Regenerate Private Key link to download the third and final configuration file you need <username>.pem.
Once you have these three files downloaded, go back to the root directory where you created chef-repo. Make it the current working directory. Then create a chef-repo/.chef directory.
Create new organization
Figure 9-7. Create new organization
Copy the <username>.pem<organization>-validator.pem, and knife.rb files to the chef-repo/.chef directory. Once you’ve copied these files, the chef-repo directory should resemble the following:
chef-repo/
├── .chef
│   ├── knife.rb
│   ├── <organization>-validator.pem
│   └── <username>.pem
└── cookbooks
   └── enterprise-chef
        ├── .kitchen
        ├── .kitchen.yml
        ├── Berksfile
        ├── Berksfile.lock
        ├── README.md
        ├── attributes
        │   └── default.rb
        ├── chefignore
        ├── metadata.rb
        └── recipes
            └── default.rb
Save the validation key and Knife configuration file
Figure 9-8. Save the validation key and Knife configuration file
Because we registered with the username “misheska” and the organization “learningchef,” our .chef directory contains the following:
  • misheska.pem
  • learningchef-validator.pem
  • knife.rb
The <username>.pem file is a unique identifier used to authenticate you against Chef Server. This should be treated like a password—do not share it with anyone and do not alter the contents of the file.
The <organization>.pem file is a unique identifier used to authenticate your organization against Chef Server. This should be treated like a password, but it must also be shared among all your Chef developers. Anyone needing access to your Chef organization will also need a copy of this file. Do not alter the contents of this file.
Click on your username link
Figure 9-9. Click on your username link
Unlike a .pem file, the knife.rb file is meant to be edited, altered, and customized. The knife.rb file is recognized as Ruby and read by Chef when it issues commands:
current_dir = File.dirname(__FILE__)
log_level                :info
log_location             STDOUT
node_name                "<username>"
client_key               "#{current_dir}/<username>.pem"
validation_client_name   "<organization>-validator"
validation_key           "#{current_dir}/<organization>-validator.pem"
chef_server_url          "https://default-centos65.vagrantup.com/\
organizations/learningchef"
cache_type               'BasicFile'
cache_options( :path => "#{ENV['HOME']}/.chef/checksums" )
cookbook_path            ["#{current_dir}/../cookbooks"]
Regenerate private key
Figure 9-10. Regenerate private key
Note that the chef_server_url field in our example uses a fake DNS hostname of default-centos65.vagrantup.com because that’s the hostname vagrant set up. If you try to visit the URL https://default-centos65.vagrantup.com/organizations/learningchef, you will discover that it is not valid.
Chef Server requires that hosts have valid fully qualified domain names set up in your local domain name service (DNS). In production, you would have your Chef Server hostname configured in your Domain Name System (DNS) server before installing Chef Server. Let’s add a temporary host entry for default-centos65.vagrantup.com in your local host database in lieu of making a DNS change, as we are just doing a book exercise.
Run one of the following commands to add a host entry. Following are the commands we ran on our machine. If you used an IP address other than 192.168.33.34, make sure it matches when you run the command.
Linux/Mac OS X:
$ sudo sh -c "echo '192.168.33.34 default-centos65.vagrantup.com' >> /etc/hosts"
Windows Command Prompt:
> echo 192.168.33.34 default-centos65.vagrantup.com >> \
%WINDIR%\System32\Drivers\Etc\Hosts
Windows PowerShell:
PS> ac -Encoding UTF8 $env:windir\system32\drivers\etc\hosts \
"192.168.33.34 default-centos65.vagrantup.com"
Now if you try to visit https://default-centos65.vagrantup.com in your web browser, your local host should think that this is a valid hostname.
You may add additional values to the knife.rb, such as EC2 credentials, proxy information, and encrypted data bag settings. Although certain pieces of the knife.rb will be common among your team members, the contents of the generally file should be unique to you and your machine. However, unless you have access keys and passwords in your knife.rb, you do not need to treat it like a password.

Testing the Connection

You should run the following commands from inside the Chef repository. Open your terminal or command prompt, and make chef-repo the current working directory. If you placed your Chef repo in a different location, use that instead:
$ cd ~/chef-repo
Now you can use knife, the command-line tool for Chef Server, to test your connection and authentication against Chef Server. At the time of this writing, Chef does not provide a “connection test” command. However, asking Chef Server to list the clients will verify
  • Your network can connect to Chef Server.
  • The authentication files are in the correct location.
  • The authentication files can be read by Chef.
  • The response from Chef Server is received by your workstation.
Issue the knife client list command on your terminal. You should see the following:
$ knife client list
learningchef-validator
If you get an error, check the following:
  1. You can access https://default-centos65.vagrantup.com:443 from a web browser.
  2. You are running commands from inside the chef-repo directory.
  3. The .chef directory contains two .pem files and a knife.rb.
  4. Your authentication files have the correct file permissions (they should be only user-readable).
If you have confirmed the preceding steps and are still unable to connect to Chef Server, please consult the Chef online documentation.
Now that you have verified that your host can connect to Chef Server, let’s create another cookbook for a node instance and register it to be managed by Chef Server.

Bootstrapping a Node

In Chef, the term “bootstrapping” refers to the process by which a remote system is prepared to be managed by Chef. This process includes installing Chef Client and registering the target node with Chef Server.

CREATE A NODE IN A SANDBOX ENVIRONMENT

Let’s use Test Kitchen to define a project that spins up a node in a sandbox environment, similar to what we did back in Chapter 5 before we learned how to create cookbooks.
Create a node directory alongside the chef-server cookbook you created in this chapter. This technically isn’t a cookbook—it’s just a Test Kitchen project—but putting it beside the chef-server cookbook directory makes it convenient to go back and forth between the two.
Create the directory ~/chef-repo/cookbooks/node, and make it the current working directory:
$ cd ~/chef-repo/cookbooks
$ mkdir node
$ cd node
The knife client list command should work even in this subdirectory. Verify this now:
$ knife client list
learningchef-validator
This node directory will just be a test kitchen project, not a cookbook, so run the following commands to create a .kitchen.yml file for Test Kitchen:
$ kitchen init --create-gemfile
$ bundle install
Edit the .kitchen.yml file, as in Example 9-5, to use the CentOS 6.5 basebox we prepared specifically for this book. Also assign a private network address like we did in Chapter 7. This time, we’re going to use the IP address 192.168.33.35. Make sure this address does not conflict with the IP address of your Chef Server, which should be 192.168.33.34.
Note that we also changed the suite name to be node, as this sandbox environment will be running our node, but we also have another sandbox environment running our Chef Server. Having different names will disambiguate the two environments.
Also, we’ve configured a synced folder pointing at the root chef-repo directory. As shown in Figure 9-11, Vagrant can keep directories on your host Chef development workstation synchronized with directories in the sandbox environment running on the guest.
Overview of the Virtualized Chef Training Environment
Figure 9-11. Overview of the Virtualized Chef Training Environment
The following synced_folders: stanza in a .kitchen.yml file ensures that the chef-repo directory on the host is kept in sync with the /chef-repo directory on the guest:
...
      synced_folders:
      - ["../../../chef-repo", "/chef-repo"]
...
The /chef-repo synced folder will be used later in Chapter 10 when we configure SSL verification with the server.
Example 9-5. chefdk/chef-repo/cookbooks/node/.kitchen.yml
---
driver:
  name: vagrant

provisioner:
  name: shell

platforms:
  - name: centos65
    driver:
      box: learningchef/centos65
      box_url: learningchef/centos65
      network:
      - ["private_network", {ip: "192.168.33.35"}]
      synced_folders:
      - ["../../../chef-repo", "/chef-repo"]

suites:
  - name: node
    attributes:
Spin up the node environment with kitchen create:
$ kitchen create

BOOTSTRAP THE NODE WITH KNIFE

Figure 9-12 is an overview of the setup we’ve configured so far in this chapter. We’ve configured a Chef server (or used Hosted Enterprise Chef), and we configured a knife.rb with the appropriate keys so we can communicate with the Chef server from our host, the administrator’s workstation. We’ve established that this communication channel works by verifying that knife client list produces the expected output.
Now let’s set up our node like we would in production by “bootstrapping” the node with knife bootstrap. ((((“Test Kitchen”,"in production environments”)))We won’t be able to use Test Kitchen in production!) When we run knife bootstrap on the our host, it will install Chef Client on the node and register it to be managed by Chef Server.
Nodes must have valid fully qualified domain names set up in your local domain name service (DNS) as well. Let’s add an entry to our local host database for the node just like we did for Chef Server.
Run one of the following commands to add a node entry. Following are the commands we ran on our machine. If you used an IP address other than 192.168.33.35, make sure it matches when you run the command.
Linux/Mac OS X:
$ sudo sh -c "echo '192.168.33.35 node-centos65.vagrantup.com' >> /etc/hosts"
Overview of our setup so far, before nodes
Figure 9-12. Overview of our setup so far, before nodes
Windows Command Prompt:
> echo 192.168.33.35 node-centos65.vagrantup.com >> \
%WINDIR%\System32\Drivers\Etc\Hosts
Windows PowerShell:
PS> ac -Encoding UTF8 $env:windir\system32\drivers\etc\hosts \
"192.168.33.35 node-centos65.vagrantup.com"
You also need to kitchen login to the node and configure the local host database on the node to provide it the name of the server when you run chef-client. Once complete, exit back out to your host prompt. As mentioned before, in production, you’d just make sure the DNS was configured with these hostnames before installing Chef Server and any nodes:
$ kitchen login node-centos65
Last login: Sat Aug 16 01:50:02 2014 from 192.168.33.1
Welcome to your Packer-built virtual machine.
[vagrant@node-centos65 ~]$ sudo sh -c "echo \
'192.168.33.34 default-centos65.vagrantup.com' >> /etc/hosts"
[vagrant@node-centos65 ~]$ exit
logout
Connection to 127.0.0.1 closed.

NOTE

If you get the error “No instances for regex node-centos65,” you forgot to change the suite name to node. Run kitchen destroy to clear the node instance with the incorrect name. Then refer to Example 9-5 and make sure the suites stanza resembles the following:
suites:
  - name: node
Run kitchen create again after making the correction.
Run the following command to bootstrap your node:
$ knife bootstrap --sudo --ssh-user vagrant --ssh-password \
vagrant --no-host-key-verify node-centos65.vagrantup.com
Connecting to node-centos65.vagrantup.com
node-centos65.vagrantup.com Installing Chef Client...
...
node-centos65.vagrantup.com Thank you for installing Chef!
node-centos65.vagrantup.com Starting first Chef Client run...
...
node-centos65.vagrantup.com Starting Chef Client, version 11.14.2
node-centos65.vagrantup.com Creating a new client identity for
node-centos65.vagrantup.com using the validator key.
node-centos65.vagrantup.com resolving cookbooks for run list: []
node-centos65.vagrantup.com Synchronizing Cookbooks:
node-centos65.vagrantup.com Compiling Cookbooks...
node-centos65.vagrantup.com [2014-08-16T01:56:43-07:00] WARN: Node
node-centos65.vagrantup.com has an empty run list.
node-centos65.vagrantup.com Converging 0 resources
node-centos65.vagrantup.com
node-centos65.vagrantup.com Running handlers:
node-centos65.vagrantup.com Running handlers complete
node-centos65.vagrantup.com Chef Client finished, 0/0 resources updated in
2.646571561 seconds
You can tell from the output that it successfully installed Chef Client, and even performed a courtesy Chef Client run.
To verify that the node is now registered on Chef Server, log into the web interface and click on the Nodes tab. Now you should see that you have a node registered with your Chef Server, as shown in Figure 9-13.
Node is registered with Chef Server
Figure 9-13. Node is registered with Chef Server
If you click on the link to the node, you should see that Chef Server displays information about the node as shown in Figure 9-14. The values you see under attributes should look familiar; these are the attributes generated automatically by ohai. They are stored on Chef Server for each node, and the data is searchable by all clients.
We’ll be using both Chef Server and node sandbox instances in the next chapter, so don’t kitchen destroythem just yet, as you don’t want to have to go through all the setup steps you performed in this chapter. If you need to stop the virtual machines temporarily because you plan on powering off your machine before tackling the next chapter, you can use the vagrant halt command. Unfortunately, as of this writing, Test Kitchen doesn’t have a way to halt or suspend virtual machines with the kitchen command, so you have to use the vagranttool that Test Kitchen uses behind the scenes instead.
Node detail
Figure 9-14. Node detail
In order to run vagrant halt to perform a graceful shutdown of your virtual machines, you’ll need to make sure the Vagrantfile directory that is used by vagrant to configure the virtual machine is the current working directory. As of this writing, the associated Vagrantfiles are located in the .kitchen directory that Test Kitchen uses for temporary files in a directory matching the suite name in the .kitchen.yml. To halt both virtual machines, run the following commands:
$ cd ~/chef-repo/cookbooks/enterprise-chef/.kitchen/kitchen-vagrant/
$ default-centos65 vagrant halt
==> default: Attempting graceful shutdown of VM...
$ cd ~/chef-repo/cookbooks/node/.kitchen/kitchen-vagrant/node-centos65
$ vagrant halt
==> default: Attempting graceful shutdown of VM...
When you want to restart them, run vagrant reload against both Vagrantfiles:
$ cd ~/chef-repo/cookbooks/enterprise-chef/.kitchen/kitchen-vagrant/
$ default-centos65 vagrant reload
==> default: Checking if box 'learningchef/centos65' is up to date...
...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
...
$ cd ~/chef-repo/cookbooks/node/.kitchen/kitchen-vagrant/node-centos65
$ vagrant reload
==> default: Checking if box 'learningchef/centos65' is up to date...
...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
...
$ cd ~/chef-repo

Bootstrap Chef Server with Chef Solo

Before we conclude this chapter, it is worth mentioning that you can use Chef Solo outside of Test Kitchen to automate the deployment of Chef Server in production. We expect to see most Chef Solo-based software migrate to Chef Local/Chef Zero, as we covered in Chef Client Modes. However, Chef Solo is still singularly useful for bootstrapping Chef Server itself, as some scripts get confused about which server to communicate with when you try to set up Chef Server using Chef Local/Chef Zero, because Chef Zero launches a second in-memory Chef Server.

NOTE

Although we expect Chef coders to transition to Chef Local/Chef Zero, there are some truly amazing systems still built on Chef Solo. One tool worth checking out if you use Mac OS X is SoloWizard, by Pivotal Labs. By filling out a simple web form you can have it automatically generate a Chef Solo-based script to auto-generate a Mac OS X development workstation. As of this writing, there is no Chef Local equivalent of this tool, but there might be one by the time you read this.
Here’s an overview of the steps required to use Chef Solo to bootstrap Chef Server:
  1. Install Chef Client (which includes chef-solo).
  2. Create /var/chef/cache and /var/chef/cookbooks directories. These are the default locations where chef-solostores state information and looks for cookbooks, respectively. (You can override these settings by supplying a .json file with configuration settings on the command line. See http://docs.getchef.com/chef_solo.html for more information.)
  3. Copy any necessary cookbooks down to the host.
  4. Run chef-solo.
Although we won’t go through another hands-on exercise bootstrapping Chef Server with Chef Solo, as you’ve done this already with Test Kitchen, here are the commands you would use to perform the preceding steps:
# install chef-solo
$ curl -L https://wwww.getchef.com/chef/install.sh | sudo bash
# create required directories
$ sudo mkdir -p /var/chef/cache /var/chef/cookbooks
# copy your cookbook code to create a Chef Server - Chef Software
# provides a cookbook for open source Chef Server
$ sudo mkdir /var/chef/cookbooks/chef-server
$ wget -qO- https://github.com/opscode-cookbooks/chef-server/archive/\
master.tar.gz | sudo tar xvzC /var/chef/cookbooks/chef-server \
--strip-components=1
# Run chef-solo to bootstrap Chef Server
$ sudo chef-solo -o 'recipe[chef-server::default]'

NOTE

As of this writing, there is no Windows version of Chef Server. It is available on the Linux platform exclusively. That is why you see only Linux commands in the preceding code block.

Summary

In this chapter, we’ve covered the basics of installing Chef Server. We showed you how to install a test Chef Server setup driven by Chef recipes in both sandbox environments using Test Kitchen, and on production servers using chef-solo.
We also introduced the concept of idempotence when we covered how to write Chef code to install Chef Server. Chef code is idempotent when it can run multiple times on the same system and the results will always be identical, without producing unintended side effects.
Although nearly all the default Chef resources are guaranteed to be idempotent, the execute resource is not. We showed you how to test that Chef code is idempotent by performing a Chef run twice in succession, such as with kitchen converge. On the second run, idempotent Chef code should display no resource updates.
In the next chapter, we’ll talk about the Chef Community Cookbook. We’ll highlight the chef-clientcookbook, an oft-used community cookbook people find useful in bootstrapping nodes in production.

1 comment: