Contributors20 minutes | Key points: - You ran
knife bootstrap to associate your node with the Chef server and do an initial check-in. Bootstrapping is a one-time process. - The
knife ssh command enables you to update your node's configuration when your cookbook changes.
|
During the bootstrap process, your node downloaded and installed chef-client, downloaded the latest cookbooks, and executed the run-list.
Chef provides information about your node that you can access from your cookbooks. Here you'll update the home page to display your node's fully-qualified domain name (FQDN). You'll practice updating your cookbook, uploading it to the Chef server, and seeing the changes appear on your node.
You already learned how to use the template resource to reference the HTML file for your home page. You're ready to add placeholders to your HTML file that are filled in with info about your node as the recipe runs.
1. Add template code to your HTML
When you bootstrapped your node, the Chef server created what's called a node object for you. This node object contains a number of attributes that describe the node, and these attributes are saved on the Chef server. When a recipe runs, a node object is loaded into the program. Chef loads the node's attributes from the Chef server into memory. You can access these attributes from your Chef recipes.
For our home page, we want to display the server's fully qualified domain name (FQDN). To do so, we access the fqdn attribute of the node object. On the local workstation copy of your learn_chef_apache2 cookbook, change index.html.erb to look like this.
Editor: ~/learn-chef/cookbooks/learn_chef_apache2/templates/index.html.erb
1
2
3
4
5
| <html>
<body>
<h1>hello from <%= node['fqdn'] %></h1>
</body>
</html> |
The <%= %> syntax enables you to provide placeholders in your template file. Placeholders are replaced with their values when chef-client runs. You'll work more with templates in future modules, but you can read more now in the Chef documentation.
| Keep in mind that this example is for illustrative purposes. In practice, you might modify your web server's configuration file to specify the server's hostname or other info instead of its web page content. |
2. Update your cookbook's version metadata
Before you upload an updated cookbook to Chef server, you should always update your cookbook's version metadata to ensure that each version is tied to a specific set of functionality.
Your cookbook's metadata.rb file holds the cookbook's version. When you run the
chef generate cookbook command to create your cookbook, the initial version is set to 0.1.0. Here's what your learn_chef_apache2 cookbook's metadata.rb file looks like.
Editor: ~/learn-chef/cookbooks/learn_chef_apache2/metadata.rb
1
2
3
4
5
6
7
8
9
| name 'learn_chef_apache2'
maintainer 'The Authors'
maintainer_email 'you@example.com'
license 'all_rights'
description 'Installs/Configures learn_chef_apache2'
long_description 'Installs/Configures learn_chef_apache2'
version '0.1.0'
issues_url 'https://github.com/learn-chef/learn_chef_apache2/issues' if respond_to?(:issues_url)
source_url 'https://github.com/learn-chef/learn_chef_apache2' if respond_to?(:source_url) |
The learn_chef_apache2 cookbook is currently at version 0.1.0.
Most Chef cookbooks follow the Semantic Versioning scheme. Version numbers are typically written as MAJOR.MINOR.PATCH, where:
- MAJOR specifies a change that's incompatible with previous versions.
- MINOR specifies new functionality that's backwards-compatible with previous versions.
- PATCH specifies backwards-compatible bug fixes.
An update to the HTML template represents a minor change in functionality, so let's increment the middle number, making your cookbook's version 0.2.0.
Modify your copy of metadata.rb like this. The version field changes to '0.2.0'.
Editor: ~/learn-chef/cookbooks/learn_chef_apache2/metadata.rb
1
2
3
4
5
6
7
8
9
| name 'learn_chef_apache2'
maintainer 'The Authors'
maintainer_email 'you@example.com'
license 'all_rights'
description 'Installs/Configures learn_chef_apache2'
long_description 'Installs/Configures learn_chef_apache2'
version '0.2.0'
issues_url 'https://github.com/learn-chef/learn_chef_apache2/issues' if respond_to?(:issues_url)
source_url 'https://github.com/learn-chef/learn_chef_apache2' if respond_to?(:source_url) |
Learn more about cookbook versioning
3. Upload your cookbook to the Chef server
Now you're ready to upload your cookbook to the Chef server. Run the knife cookbook upload command like this.
Terminal: ~/learn-chef
$ | knife cookbook upload learn_chef_apache2Uploading learn_chef_apache2 [0.2.0]Uploaded 1 cookbook.
|
4. Run the cookbook on your node
Now that your updated cookbook is on the Chef server, you can run chef-client on your node. The chef-client command pulls from Chef server the latest cookbooks from the node's run-list and applies the run-list to the node.
To run chef-client on your node remotely from your workstation, you could use the ssh utility to create an SSH connection to your node and then run chef-client. Another way is to use the knife ssh command. A benefit to using the knife ssh command is that it enables you to run chef-client (or any other command) on multiple nodes at the same time.
Recall that the Chef server stores information about your nodes. This information is indexed and is searchable from tools such as knife. When you run knife ssh, you can either specify your node's IP address or a search query that specifies which nodes to connect to. Because the search query syntax supports multiple search patterns and can contain multiple search criteria, you can use knife ssh to run chef-client on multiple nodes at the same time.
As with knife bootstrap, the options you provide to knife ssh depend on how you would normally connect to your node over SSH.
Choose the option that matches how you normally connect to your node over SSH.
Update your node using key-based authentication
Replace USER with your username and IDENTITY_FILE with your SSH identify file, for example ~/.ssh/my.pem. Also replace node1-ubuntu with your node's name if you used a different name to bootstrap your node.
- If you're working with a cloud instance
If you're working with an Amazon EC2, Microsoft Azure, or Google Compute Engine instance, replace the ipaddress part of the --attribute ipaddress argument with the corresponding entry from this table.
| Cloud provider | Attribute | Notes |
|---|
| EC2 | cloud.public_hostname | Chef sets this attribute during the bootstrap process. |
| Azure | cloud.public_ip | This is the attribute you set in the previous part when you bootstrapped your node. |
| Compute Engine | cloud_v2.public_ipv4 | Chef sets this attribute during the bootstrap process. |
For example, if you're working with an EC2 instance, you would specify --attribute cloud.public_hostname.
Terminal: ~/learn-chef
$ | knife ssh 'name:node1-ubuntu' 'sudo chef-client' --ssh-user USER --ssh-identity-file IDENTITY_FILE --attribute ipaddress
|
Here's an example.
Terminal: ~/learn-chef
$ | knife ssh 'name:node1-ubuntu' 'sudo chef-client' --ssh-user vagrant --ssh-identity-file ~/.ssh/private_key --attribute ipaddress192.168.145.131 Starting Chef Client, version 13.8.5192.168.145.131 resolving cookbooks for run list: ["learn_chef_apache2"]192.168.145.131 Synchronizing Cookbooks:192.168.145.131 - learn_chef_apache2 (0.2.0)192.168.145.131 Installing Cookbook Gems:192.168.145.131 Compiling Cookbooks...192.168.145.131 Converging 4 resources192.168.145.131 Recipe: learn_chef_apache2::default192.168.145.131 * apt_update[Update the apt cache daily] action periodic (up to date)192.168.145.131 * apt_package[apache2] action install (up to date)192.168.145.131 * service[apache2] action enable (up to date)192.168.145.131 * service[apache2] action start (up to date)192.168.145.131 * template[/var/www/html/index.html] action create192.168.145.131 - update content in file /var/www/html/index.html from ef4ffd to 4b6898192.168.145.131 --- /var/www/html/index.html 2018-05-01 21:21:06.544722898 +0000192.168.145.131 +++ /var/www/html/.chef-index20180501-12517-1qoaz5a.html 2018-05-01 21:21:30.521526763 +0000192.168.145.131 @@ -1,6 +1,6 @@192.168.145.131 <html>192.168.145.131 <body>192.168.145.131 - <h1>hello world</h1>192.168.145.131 + <h1>hello from node1-ubuntu</h1>192.168.145.131 </body>192.168.145.131 </html>192.168.145.131 192.168.145.131 Running handlers:192.168.145.131 Running handlers complete192.168.145.131 Chef Client finished, 1/5 resources updated in 05 seconds
|
Here you see that the "hello world" message is replaced with one that includes the node's FQDN.
The 'name:node1-ubuntu' part is the search query. It returns all nodes that have the name "node1-ubuntu". In practice, you would have only one node with a given name. Recall that a search query supports multiple patterns. If you were to specify 'name:node1-*', which uses a wildcard pattern, knife ssh would run sudo chef-client on all nodes whose name begins with "node1-".
The --attribute part tells knife which node attribute to use when opening an SSH connection. The default is to use the node's FQDN. For learning purposes, here we specify ipaddress to use the node's IP address because your node may not have a resolvable FQDN. In practice, you might omit this argument if your node does have a resolvable FQDN.
Here are some common built-in node attributes
Update your node using password authentication
Replace USER and PASSWORD with your values. Replace ipaddress with the appropriate attribute name from the table above if your node is running on a cloud service such as Amazon EC2.
Terminal: ~/learn-chef
$ | knife ssh 'name:node1-ubuntu' 'sudo chef-client' --ssh-user USER --ssh-password 'PASSWORD' --attribute ipaddress
|
The output will resemble the output shown for key-based authentication above.
Update a local virtual machine using a forwarded port
Replace PORT with your SSH forwarded port, for example, 2222, and IDENTITY_FILE with your SSH identify file, for example /home/user/.vagrant/machines/default/virtualbox/private_key.
Terminal: ~/learn-chef
$ | knife ssh localhost --ssh-port PORT 'sudo chef-client' --ssh-user vagrant --ssh-identity-file IDENTITY_FILE --manual-list
|
For learning purposes, this version connects directly to localhost using the SSH forwarded port instead of using a search query. To use a search query with a local virtual machine running on Vagrant and VirtualBox, for example, requires a more complex network configuration.
The output will resemble the output shown for key-based authentication above.
| Remember, in practice it's common to configure Chef to act as a service that runs periodically or as part of a continuous integration or continuous delivery (CI/CD) pipeline. For now, we're updating our server configuration by running chef-client manually. |
5. Verify the result
As before, run curl to verify the configuration. Replace the IP address you see here with yours. You'll see your node's FQDN appear instead of the example shown.
Terminal: ~/learn-chef
$ | curl 192.168.145.131<html> <body> <h1>hello from node1-ubuntu</h1> </body></html>
|
You can also open a web browser from your workstation and navigate to your web server. Here's an example for a system whose FQDN is "node1-ubuntu".

If you bootstrapped a Vagrant instance, one way to verify the configuration is to create an SSH connection to the instance and run curl localhost.
Terminal: ~
$ | vagrant sshLast login: Thu Dec 3 19:54:48 2015 from 10.0.2.2[vagrant@localhost ~]$ curl localhost <html> <body> <h1>hello from localhost</h1> </body></html>[vagrant@localhost ~]$ exit logoutConnection to 127.0.0.1 closed.
|
The home page now displays "hello from localhost" instead of "hello world".
To summarize, to update your cookbook you used a template. A template enables you to write a single, general recipe that’s customized for a particular node as the recipe runs. That means you don’t have to write a custom version of your recipe for every node.
You also ran knife ssh to update your node. knife ssh invokes the command you specify over an SSH connection on a node – in our case sudo chef-client. You didn't have to specify the run-list because you already set that up when you bootstrapped the node.