Tuesday 3 January 2017

Chapter 6. Manage Nodes with Chef Client

Now that you have a sandbox environment in which to experiment with Chef, let’s set up your guest system to be managed by Chef. In this chapter, you will use Test Kitchen to install Chef Client on your guest virtual machine so it can run Chef recipes. As a reminder, in Chapter 4 you learned that a Chef recipe is a file that contains Chef code.

What Is a Node?

Before we show you how to install Chef Client on the guest with Test Kitchen, let’s first introduce some Chef-specific terminology to describe the different types of machines that we are now using.
The machine on which you author Chef code is referred to as the Chef Developer’s Workstation or Chef Administrator’s Workstation. Your host machine is your Chef Developer Workstation. In Chapter 2 you installed the Chef Development Kit on your host so that you have all the tools necessary to write Chef recipes using a programmer’s editor and to manage changes to your Chef code with a source control system.
A machine that is managed by Chef is called a node. A machine is managed by Chef when it runs Chef recipes to ensure the machine is in a desired configuration, as shown in Chapter 4. A node can be a physical machine, a virtual machine, a cloud instance, or a container instance—it makes no difference to Chef. As long as the node has Chef Client installed, it can be managed by Chef and it can run Chef recipes.
Because the Chef Development Kit is a superset of Chef Client, you could install the Chef Development Kit on a node. This is what we did in Chapter 4, making your host act as both a Chef Developer Workstation and as a node managed by Chef. However, the Chef Development Kit is about double the footprint of Chef Client. All of the extra tools included with the Chef Development Kit are for writing Chef code, not running Chef code. In Chapter 5 we made the case that in real-world production environments, these roles are split between two different machines because you do not write Chef code on every machine in your infrastructure. We are using Test Kitchen to manage a sandbox environment running CentOS 6 as a guest virtual machine. Now we need to make the guest virtual machine a Chef node.

NOTE

Chef uses the generic term node because Chef is not limited to managing servers or compute nodes. Chef can manage other components in your infrastructure as well, such as switches, routers, and storage.
Going forward, we will refer to your guest virtual machine sandbox environment simply as a node. In fact, you might not even be using a guest virtual machine if you decided to opt for the alternative setups covered on http://learningchef.com. So it makes sense to just refer to the “other machine” being managed by Chef generically as a node.

Create a New Sandbox Environment for a Node

Within the directory structure you have created for this book’s code, as outlined in Create a Directory Structure for Your Code, create a project directory to contain the sandbox environment for your node. Create a new directory called node and make it the current directory, just like you did in Chapter 5. For example:
$ mkdir node
$ cd node
Then perform the same steps you performed in Chapter 5, running kitchen init to generate all the required Test Kitchen configuration, and bundle install to install the supporting gems:
$ kitchen init --create-gemfile
$ bundle install
In this case, running bundle install is really not necessary, because you are setting up a sandbox environment identical to the one you created in Chapter 5. However, it is a good idea to acquire the habit of running bundle install after kitchen init.
Edit the .kitchen.yml file as shown in Example 6-1, and make it resemble the state of the final .kitchen.yml when you finished Chapter 5.
Example 6-1. node/.kitchen.yml
---
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
  - name: centos65
    driver:
      box: learningchef/centos65
      box_url: learningchef/centos65

suites:
  - name: default
    run_list:
    attributes:
Then run kitchen create to spin up a new sandbox environment to serve as your node. As before, it will use the cached version of the learningchef/centos65 box, and Test Kitchen will not try to download the box again if it sees the box in the cache. So the sandbox environment should start fairly quickly, in less than a minute:
$ kitchen create default-centos65

Installing Chef Client with Test Kitchen

Use the kitchen login command to connect to your node (a.k.a. the sandbox environment), and access the command prompt of the node running CentOS 6. Then, check to see if the Chef Client is installed on the node by running chef-client --version:
$ kitchen login default-centos65
Last login: Fri Jul  4 14:48:27 2014 from 10.0.2.2
Welcome to your Packer-built virtual machine.
[vagrant@default-centos65 ~]$ chef-client --version
-bash: chef-client: command not found
Nope, doesn’t seem to be installed. How do we install chef-client on the node? Although we could follow the instructions again for Installing Chef Client on Linux from Chapter 2 by running the following, don’t do this:
curl -Lk https://www.getchef.com/chef/install.sh | sudo bash
There is an easier way. Type in the exit command on your node to get back to your host prompt:
[vagrant@default-centos65 ~]$ exit
logout
Connection to 127.0.0.1 closed.
Double-check to make sure that the prompt being displayed is actually your host prompt (no vagrant@default-centos65). Run the command kitchen setup default-centos65 to install chef-client:
$ kitchen setup default-centos65
-----> Starting Kitchen (v1.2.2.dev)
-----> Converging <default-centos65>...
       Preparing files for transfer
       Berksfile, Cheffile, cookbooks/, or metadata.rb not found so Chef will
       run with effectively no cookbooks. Is this intended?
       Removing non-cookbook files before transfer
-----> Installing Chef Omnibus (true)
       downloading https://www.getchef.com/chef/install.sh
         to file /tmp/install.sh
       trying wget...
       trying curl...
...
-----> Setting up <default-centos65>...
       Finished setting up <default-centos65%gt; (0m0.00s).
-----> Kitchen is finished. (0m19.93s)
If you inspect the output, the kitchen setup command installed the chef-client for you. The kitchen setup command is used to run a provisioner. Provisioner is a generic term for any kind of configuration management software, as Test Kitchen can be used with other configuration management tools besides Chef. By default, Test Kitchen is configured to use the ChefSolo provisioner, which installs Chef Client without configuring the tools to use a Chef Server. kitchen setup will automatically install chef-client for you using the commands you entered in Chapter 2 if chef-client is not present.
If you run kitchen list now, you’ll notice that the Last Action column changed from Created to Set Up:
$ kitchen list
Instance          Driver   Provisioner  Last Action
default-centos65  Vagrant  ChefSolo     Set Up
Now, if you log into the guest node, you should see that chef-client is present:
$ kitchen login default-centos65
Last login: Sat Jul  5 09:15:07 2014 from 10.0.2.2
Welcome to your Packer-built virtual machine.
[vagrant@default-centos65 ~]$ chef-client --version
Chef: 11.14.6

Your First Chef-Client Run

Although you could use chef-apply to execute code in a Chef recipe file like you did in Chapter 4, the chef-client tool is more commonly used in production environments. chef-client provides the ability to execute Chef code across multiple recipe files, which we’ll see more of in Chapter 7. In order to manage real-world production environments, you’ll be running a lot of Chef code. In order to make maintenance easier, one normally spreads production code across multiple recipe files. Although chef-apply will do in a pinch for simple management tasks, you’ll end up using chef-client most of the time to manage a node with Chef.
To show you the basics of using chef-client, let’s create a new Chef recipe file that prints out some of the information Chef maintains about each node.
The log resource can be used to print out strings from a recipe. For example, the statement log "Hello" in a recipe would write out the string Hello. Let’s give this a try. We assume you’re still logged in to the node environment. Create the file hello.rb on the node with the following command:
[vagrant@default-centos65 ~]$ echo 'log "Hello, this is an important message."' \
 > hello.rb

NOTE

In Chapter 7 we will show you how to edit files in your host environment and automatically transfer them to the node. Until then, we’re showing you commands that you can use to create hello.rb in lieu of using editors such as vi or nano directly on the node. Some readers might not feel comfortable with editing files in a Linux-based operating environment. Feel free to use a text editor instead.
This command will produce the following source file.
Example 6-2. node/hello.rb
log "Hello, this is an important message."
When you use chef-client to perform actions in a recipe, this is referred to as a Chef run. Execute your first Chef run by entering chef-client --local-mode hello.rb --log_level info on a command line. The --local-mode option will prevent chef-client from trying to time out looking for a nonexistent Chef Server. We will introduce Chef Server later in this book. The --log_level info option is also necessary because by default, chef-client will only print errors, not informative messages. This option will tell chef-client to print out the strings from any Chef::Log.info commands.

NOTE

Chef requires administrator changes to run. If you are running User Account Control (UAC) on Windows, make sure you Run As Administrator.
When you run chef-client --local-mode hello.rb on the node, the output should resemble the following:
[vagrant@default-centos65 node]$ chef-client --local-mode hello.rb
[2014-08-14T12:28:43-07:00] WARN: No config file found or specified on command
line, using command line options.
[2014-08-14T12:28:43-07:00] WARN: No cookbooks directory found at or above
current directory.  Assuming /learningchef.
Starting Chef Client, version 11.14.6
resolving cookbooks for run list: []
Synchronizing Cookbooks:
Compiling Cookbooks...
[2014-08-14T12:28:46-07:00] WARN: Node default-centos65.vagrantup.com has an
empty run list.
Converging 1 resources
Recipe: @recipe_files::/learningchef/hello.rb
  * log[Hello, this is an important message.] action write


Running handlers:
Running handlers complete
Chef Client finished, 1/1 resources updated in 2.308460088 seconds
During your chef-client run, the following output indicates that “Hello, this is an important message.” was written to the log for the Chef run:
log[Hello, this is an important message.] action write
To actually see the log message, you need to change the chef-client log level. Every message written to the log has severity level. The levels, in order of priority from lowest to highest, are debuginfowarnerror, and fatal. The log resource uses the info level as a default, which is appropriate for your “Hello” message. However, chef-client only prints out messages of severity warn or greater unless you change the chef-client log level.
To change the log level, use the --log_level option. The --log_level option takes a parameter (--log_level <level>), changing the lowest level severity messages chef-client will write to its log. If you add the option --log_level info to your chef-client command line, it will display the log message you just added. Try that now:
[vagrant@default-centos65 learningchef]$ chef-client --local-mode hello.rb \
  --log_level info
[2014-08-14T12:30:43-07:00] WARN: No config file found or specified on command
line, using command line options.
[2014-08-14T12:30:43-07:00] WARN: No cookbooks directory found at or above
current directory.  Assuming /learningchef.
[2014-08-14T12:30:43-07:00] INFO: Starting chef-zero on host localhost, port
8889 with repository at repository at /learningchef
  One version per cookbook
...
Converging 1 resources
Recipe: @recipe_files::/learningchef/hello.rb
  * log[Hello, this is an important message!] action write[2014-08-14T12:30:45
  -07:00] INFO: Processing log[Hello, this is an important message.] action
  write (@recipe_files::/learningchef/hello.rb line 1)
[2014-08-14T12:30:45-07:00] INFO: Hello, this is an important message.


[2014-08-14T12:30:45-07:00] INFO: Chef Run complete in 0.040486944 seconds

Running handlers:
[2014-08-14T12:30:45-07:00] INFO: Running report handlers
Running handlers complete
[2014-08-14T12:30:45-07:00] INFO: Report handlers complete
Chef Client finished, 1/1 resources updated in 2.275689636 seconds
By default, chef-client prints out log messages to the screen. Now that you have reset the log level, you see your important message in the log output (along with some other messages that are also at the info level of severity).

NOTE

If you would prefer to write the chef-client log to a file, use the --logfile <LOGLOCATION> option (or the short form -l).

Chef Client Modes

Chef Client can operate in one of three modes:
  • Local mode
  • Client mode
  • Solo mode
When chef-client is running in local mode, it simulates a full Chef Server instance in memory. Any data that would have been saved to a server is written to the local directory. The process of writing server data locally is called writeback. This is why client-client created the nodes/ directory. Local mode was designed to support rapid Chef recipe development by using Chef Zero, the in-memory, fast-start Chef server.
On the other hand, when chef-client is running in client mode, it assumes you have a Chef Server running on some other system on your network. In production, this is how most people use Chef. In client mode, chef-client is an agent (or service/daemon) that runs locally on a machine managed by Chef. Chef Server is a centralized store for the information needed to manage infrastructure with Chef. It is recommended that you set up Chef Server when you need to manage more than one machine at a time.
Before chef-client local mode was implemented in version 11.8, the only way to run Chef recipes without a Chef Server was to use chef-solochef-solo offers an additional client mode called solo mode. Solo mode provides a limited subset of Chef functionality intended to be able to run Chef locally. chef-solo does not support writeback. In most cases, local mode is far more convenient to use than solo mode. Eventually, Chef software plans to retire solo mode once local mode has feature parity with solo and when the majority of customers have migrated to chef-client 11.8 or higher. Solo mode is most popular in places still using older versions of Chef.

Ohai

When Chef Client performs a Chef run, a separate command-line tool called ohai is used to collect system information. Ohai exposes this collection of node information to Chef as a set of automatic attributes.
Try running ohai yourself, so you can see what information is being collected about your node. On our system, ohai generates 1058 lines of output, so make sure you pipe the command’s output through the more command to present the information a screen at a time. You don’t have to look through the whole output. When you’re done, hit the q key to exit back to the command line:
$ ohai | more
{
  "network": {
    "interfaces": {
      "lo": {
        "mtu": "16436",
        "flags": [
          "LOOPBACK",
          "UP",
          "LOWER_UP"
        ],
        "encapsulation": "Loopback",
        "addresses": {
          "127.0.0.1": {
            "family": "inet",
            "prefixlen": "8",
            "netmask": "255.0.0.0",
            "scope": "Node"
          },
...
--More--
As you can see, ohai collects a lot of information about the current state of the computer: networking configuration, CPU state, operating system type and version, memory consumption, and much, much more.
As an example, let’s take a look at this subset of the information generated by ohai. As you can see in the following, ohai collects the node’s IP address, MAC address, OS information, hostname, and even that we are running in a virtualized guest:
{
...
  "ipaddress": "10.0.2.15",
  "macaddress": "08:00:27:1C:AD:B6",
...
  "os": "linux",
  "os_version": "2.6.32-431.el6.x86_64",
  "platform": "centos",
  "platform_version": "6.5",
  "platform_family": "rhel",
...
  "virtualization": {
    "system": "vbox",
    "role": "guest"
  }
...
  "hostname": "default-centos65"
...
}
In the next section, we’ll access the information collected by ohai in our Chef code. Let’s learn more about how the information gets into Chef and how we can refer to this information in our code.
ohai output is in JavaScript Simple Object Notation (JSON) form. JSON is a commonly used format for machine-readable output, as it can be easily parsed into the object-representation used for programming languages like Ruby. Although you can run ohai as a standalone application, this is not very common. Instead, the output of ohai is intended to be read by machines, specifically by chef-client and associated tools, so JSON is the perfect format. chef-client reads the JSON output from ohai and converts it into a node object, which is accessible by your Chef code.
You can refer to the node’s IP address in your code with the following attribute. An attribute is a variable maintained by Chef. In your code, you specify a quoted string in a pair of brackets with the name used as an index in the collection, then Chef will return the value. In this case, we want to know the IP address. By referring to the prior ohai output, Chef knows the index name is ipaddress:
node['ipaddress']
We used an attribute variable in our Chef code back in Recipes Specify Desired Configurationnode is another attribute Chef makes available to your code. It contains all the information generated by running ohai on the node. Similar to the ENV attribute we used in Recipes Specify Desired Configuration, the node attribute is a collection of name/value pairs.
Name/value pair collections can be nested. This is what is indicated in the multiple levels of indentation in the ohai output. So to access the kind of virtualization software we are using (the “virtualization system”), use the following nested set of name/value pair references, because system is a name/value pair within the virtualization collection:
node['virtualization']['system']

NOTE

The string variant node['virtualization']['system'] tends to be the most commonly used node attribute form. However, because attribute expressions are evaluated as a Mash, you’ll encounter Chef code that uses the other possible Mash variants:
  • node[:virtualization][:system]
  • node['virtualization']['system']
  • node.virtualization.system
Use a form that makes the most sense to you.
Let’s make these examples more concrete by using them in a Chef recipe.

Accessing Node Information

As we discussed in the last section, chef-client collects a lot of information about the state of a node using ohai. Collecting this information is necessary so that Chef can intelligently reason how to put the node into the desired configuration specified in a recipe. Chef does not keep this information to itself. It makes this information available to your Chef code as a node attribute. An attribute is a variable maintained by Chef.
Let’s use the log resource that you just used in Your First Chef-Client Run to print out some node information. Create a new file called info.rb on the node with the following sequence of commands:
[vagrant@default-centos65 ~]$ cat << EOF > info.rb
> log "IP Address: #{node['ipaddress']}"
> log "MAC Address: #{node['macaddress']}"
> log "OS Platform: #{node['platform']} #{node['platform_version']}"
> log "Running on a #{node['virtualization']['system']} \
> #{node['virtualization']['role']}"
> log "Hostname: #{node['hostname']}"
> EOF
This command will produce the source file seen in Example 6-3.
Example 6-3. node/info.rb
log "IP Address: #{node['ipaddress']}"
log "MAC Address: #{node['macaddress']}"
log "OS Platform: #{node['platform']} #{node['platform_version']}"
log "Running on a #{node['virtualization']['system']} \
#{node['virtualization']['role']}"
log "Hostname: #{node['hostname']}"
The syntax using #{<variable>} to print out information contained in variables should be familiar to you. This is similar to what we did in Chapter 4 to access #{ENV['HOME']}. In this case, the variable is node instead of ENV.
The log statements in Example 6-3 will produce the following output during your Chef run. The use of logstatements to show the content of attributes is a recommended Chef recipe debugging technique:
INFO: IP Address: 10.0.2.15
INFO: MAC Address: 08:00:27:1C:AD:B6
INFO: OS Platform: centos 6.5
INFO: Running on a vbox guest
INFO: Hostname: default-centos65
You should still be logged into the node. Perform a Chef run using chef-client in local mode. This time we will use the short options for --local-mode and --log_level. The output should resemble the following:
[vagrant@default-centos65 learningchef]$ chef-client --local-mode info.rb \
--log_level info
...
Starting Chef Client, version 11.14.6
...
Converging 5 resources
Recipe: @recipe_files::/learningchef/info.rb
  * log[IP Address: 10.0.2.15] action write[2014-08-14T12:36:05-07:00] INFO:
  Processing log[IP Address: 10.0.2.15] action write (@recipe_files::/learning
  chef/info.rb line 1)
[2014-08-14T12:36:05-07:00] INFO: IP Address: 10.0.2.15


  * log[MAC Address: 08:00:27:1C:AD:B6] action write[2014-08-14T12:36:05-07:00]
  INFO: Processing log[MAC Address: 08:00:27:1C:AD:B6] action write
  (@recipe_files::/learningchef/info.rb line 2)
[2014-08-14T12:36:05-07:00] INFO: MAC Address: 08:00:27:1C:AD:B6


  * log[OS Platform: centos 6.5] action write[2014-08-14T12:36:05-07:00] INFO:
  Processing log[OS Platform: centos 6.5] action write (@recipe_files::/learning
  chef/info.rb line 3)
[2014-08-14T12:36:05-07:00] INFO: OS Platform: centos 6.5


  * log[Running on a vbox guest] action write[2014-08-14T12:36:05-07:00] INFO:
  Processing log[Running on a vbox guest] action write (@recipe_files::/learning
  chef/info.rb line 5)
[2014-08-14T12:36:05-07:00] INFO: Running on a vbox guest


  * log[Hostname: default-centos65] action write[2014-08-14T12:36:05-07:00]
  INFO: Processing log[Hostname: default-centos65] action write (@recipe_files
  ::/learningchef/info.rb line 6)
[2014-08-14T12:36:05-07:00] INFO: Hostname: default-centos65


[2014-08-14T12:36:05-07:00] INFO: Chef Run complete in 0.039998861 seconds

Running handlers:
[2014-08-14T12:36:05-07:00] INFO: Running report handlers
Running handlers complete
[2014-08-14T12:36:05-07:00] INFO: Report handlers complete
Chef Client finished, 5/5 resources updated in 2.354730576 seconds
Notice that chef-client printed out the relevant information about your node. Your data should be similar, but some of the details, such as the IP Address, will most likely be slightly different. Chef collects a great deal of information about the state of a target machine in the node object. This node information is used to make intelligent decisions about how to automatically place the node into a desired configuration.
Run the exit command to get back to the host prompt, then run kitchen destroy default-centos65. This will shut down the VM and destroy the instance in your virtualization software, as you are done with this instance for now:
[vagrant@default-centos65 ~]$ exit
logout
Connection to 127.0.0.1 closed.
$ *kitchen destroy default-centos65*
-----> Starting Kitchen (v1.2.2.dev)
-----> Destroying <default-centos65>...
       ==> default: Forcing shutdown of VM...
       ==> default: Destroying VM and associated drives...
       Vagrant instance <default-centos65> destroyed.
       Finished destroying <default-centos65> (0m2.98s).
-----> Kitchen is finished. (0m3.24s)

Summary

In this chapter, we introduced the concept of a node. Because Chef can manage things other than personal computers, such as network switches and embedded storage systems, Chef uses the more generic term node to refer to the entities managed by Chef instead of server or host.
Any entity managed by Chef must have Chef Client installed. We showed you how to use Test Kitchen to install Chef Client on a node while writing Chef code. In Bootstrap the Node with Knife, we’ll show you how the knife bootstrap command is used to install Chef Client on production nodes, as Test Kitchen isn’t intended for production use.
You learned how to use the chef-client tool to perform a Chef run. This is how Chef manages a node. chef-client reads recipes during a Chef run. Recipes indicate a desired configuration through resources, and Chef determines the optimal sequence of steps in order to put the node into the desired state. Chef is able to reason intelligently about the node configuration, because it collects detailed information about the state of a node in an associated node attribute based on the information collected by ohai. We also showed you how you can access the information in your Chef recipes.
In the next chapter, we’ll show you how to organize multiple recipe files into a cookbook. We’ll also show you how you can run chef-client on a node using Test Kitchen on your host instead of hopping back and forth between the host and the guest.

No comments:

Post a Comment