Commit a9dc2a1b authored by Dmitry Duplyakin's avatar Dmitry Duplyakin

Improve Chef tutorial

- Major improvements in the Apache section, including more instructions and new screenshots
- First draft of the Final Remarks section
- All code snippets use new the @verbatim{} style (with highlighting for commands)
parent 08978f2b
Pipeline #506 failed with stage
......@@ -39,7 +39,7 @@ By following the instructions provided below, you will learn how to take advatag
for configuring multi-node software environments.
The exercises included in this tutorial are built around simple but realistic
configurations. In the process of recreating these configurations on nodes running
deafult images, you will explore individual components of Chef and
default images, you will explore individual components of Chef and
follow the configuration management workflow applicable to more complex configurations and experiments.
@section{Prerequisites}
......@@ -367,7 +367,7 @@ should be issues using this shell tab.
While you are here, switch to the @italic{root} user by typing:
@code-sample["chef/sudo.sh"]
@verbatim{@bold{sudo su -}}
@screenshot["chef-tutorial/experiment-shell.png"]
......@@ -431,7 +431,51 @@ corresponding profile parameter we have used.
As soon as the startup scripts complete, you will likely recieve an email confirming that Chef is installed and operational.
@code-sample["chef/email.txt"]
@verbatim{Dear User,
Chef server and workstataion should now be
installed on head.chefdemo.utahstud.emulab.net.
To explore the web management console, copy
this hostname and paste it into your browser.
Installation log can be found at /var/log/init-chef.log
on the server node.
To authenticate, use the unique credentials
saved in /root/.chefauth on the server node.
Below are several Chef commands which detail the launched experiment:
# chef -v
Chef Development Kit Version: 0.7.0
chef-client version: 12.4.1
berks version: 3.2.4
kitchen version: 1.4.2
# knife cookbook list
apache2 3.1.0
apt 2.9.2
emulab-apachebench 1.0.0
emulab-nfs 0.1.0
...
nfs 2.2.6
# knife node list
head
node-0
# knife role list
apache2
apachebench
...
nfs
# knife status -r
1 minute ago, head, [], ubuntu 14.04.
0 minutes ago, node-0, [], ubuntu 14.04.
Happy automation with Chef!
}
In some cases, you will not be able to see this email ---
email filters (e.g., if you are using a univeristy email account) may classify it as spam.
......@@ -442,7 +486,7 @@ and also obtain the credentials as described in the instructions.
In the shell tab for @tt{head}, make sure that you are running as @italic{root} and type
the following to print the credentials:
@code-sample["chef/chefauth.sh"]
@verbatim{@bold{cat /root/.chefauth}}
You should see the unique login and password that have been
generated by the startup scripts specifically for your instance of Chef:
......@@ -509,7 +553,9 @@ on these nodes by installing NFS and exporting a directory from @tt{head} to
Before proceeding to applying these updates,
let's check the role assignment from the shell by typing:
@code-sample["chef/knife-status.sh"]
@verbatim{@bold{knife status -r}}
The run lists for your nodes should be printed inside square brackets:
@screenshot["chef-tutorial/shell-knife-status.png"]
......@@ -520,7 +566,7 @@ on these nodes by installing NFS and exporting a directory from @tt{head} to
@instructionstep["Trigger the updates"]{
Run the assigned role on @tt{head} by typing:
@code-sample["chef/chef-client.sh"]
@verbatim{@bold{chef-client}}
@screenshot["chef-tutorial/head-chef-client.png"]
......@@ -529,7 +575,8 @@ on these nodes by installing NFS and exporting a directory from @tt{head} to
Therefore, you can use @tt{ssh} to run commands remotely,
on the nodes other than @tt{head}, from the same shell.
Execute the same command on @tt{node-0} by typing:
@code-sample["chef/ssh-chef-client.sh"]
@verbatim{@bold{ssh node-0 chef-client}}
@screenshot["chef-tutorial/node0-chef-client.png"]
......@@ -550,9 +597,21 @@ on these nodes by installing NFS and exporting a directory from @tt{head} to
The simpliest way to test that this configuration is functioning properly
is to create a file on one node and check that this file becomes
available on the other node.
Follow these commands to test it:
@code-sample["chef/nfs-test.py"]
Follow these commands to test it (the lines that start with the # sign are comments):
@verbatim{
# List files in the NFS directory /exp-share on head; should be empty
@bold{ls /exp-share/}
# List the same directory remotely on node-0; also empty
@bold{ssh node-0 ls /exp-share/}
# Create an empty file in this derectory locally
@bold{touch /exp-share/NFS-is-configured-by-Chef}
# Find the same file on node-0
@bold{ssh node-0 ls /exp-share/}
}
@screenshot["chef-tutorial/nfs-is-working.png"]
......@@ -576,7 +635,27 @@ when you need to periodically recreate them or create multiple instances of thos
It is worth looking at how the role you have just used is implemented. Stored at
@code{/chef-repo/roles/nfs.rb} on @tt{head}, it should include the following code:
@code-sample["chef/nfs.rb"]
@verbatim{
#
# This role depends on the emulab-nfs cookbook;
# emulab-nfs depends on the nfs cookbook
# available at: https://supermarket.chef.io/cookbooks/nfs
# Make sure it is installed; If it is not, try: knife cookbook site install nfs
#
name "nfs"
description "Role applied to all NFS nodes - server and client"
override_attributes(
"nfs" => {
"server" => "head",
"dir" => "/exp-share",
"export" => {
"network" => "10.0.0.0/8",
"writeable" => true
}
}
)
run_list [ "emulab-nfs" ]
}
You can see a number of domain-specific Ruby attributes in this file.
@code{name} and @code{description} are self-describing attributes.
......@@ -594,7 +673,18 @@ of the @code{default.rb}, the default recipe in the @tt{emulab-nfs} cookbook whi
gets called when the cookbook is executed. You should find the following code
at @code{/chef-repo/cookbooks/emulab-nfs/recipes/default.rb}:
@code-sample["chef/emulab-nfs-default.rb"]
@verbatim{
#
# Cookbook Name:: emulab-nfs
# Recipe:: default
#
if node["hostname"] == node["nfs"]["server"]
include_recipe "emulab-nfs::export"
else
include_recipe "emulab-nfs::mount"
end
}
The @code{if} statement above allows comparing the hostname of the node on which
the cookbook is running with one of the attributes specified in the role.
......@@ -625,52 +715,101 @@ executed as @italic{root}). Run the commands described below in that shell.
@itemlist[#:style 'ordered
@instructionstep["Add a role to the head's run list"]{
From the command line this time:
@code-sample["chef/head-assign-apache2.sh"]
Issues the command in bold (the rest is the expected output):
@verbatim{@bold{knife node run_list add head "role[apache2]"}
head:
run_list:
role[nfs]
role[apache2]
}
}
@instructionstep["Add two roles to the node-0's run list"]{
Do something different on @tt{node-0}:
@code-sample["chef/node0-assign-apache2-and-apachebench.sh"]
Run the two @code{knife} commands listed below to assign two roles to @tt{node-0}:
@verbatim{@bold{knife node run_list add node-0 "role[apache2]"}
run_list:
head:
run_list:
role[nfs]
role[apache2]
@bold{knife node run_list add node-0 "role[apachebench]"}
run_list:
head:
run_list:
role[nfs]
role[apache2]
role[apachebench]
}
Note that we did not exclude the nfs role - Chef will do the right thing.
Notice that we did not exclude the @tt{nfs} role from the run lists.
Configuration updates in Chef are @italic{idempotent}, which means that an update can be applied to a node
multiple times, and every time the update will yield identical configuration (regardless of the node’s previous state)
Thus, this time, when you run ``@code{chef-client}'' again, Chef will do the right thing:
the state of each individual component will be inspected, and
all NFS-related tasks will be silently skipped because NFS is already installed.
}
@instructionstep["Trigger updates on all nodes"]{
In the previous section, we configured one node at a time.
Try the "batch" update:
@code-sample["chef/knife-chef-client-all.sh"]
In the previous section, we updated one node at a time.
Try the ``batch'' update - run a single command that
will trigger configuration procedures on all client nodes:
@verbatim{@bold{knife ssh "name:*" chef-client}
apt155.apt.emulab.net resolving cookbooks for run list:
["emulab-nfs", "apache2", "apache2::mod_autoindex", "emulab-apachebench"]
apt154.apt.emulab.net resolving cookbooks for run list:
["emulab-nfs", "apache2", "apache2::mod_autoindex"]
apt155.apt.emulab.net Synchronizing Cookbooks:
....
apt155.apt.emulab.net Chef Client finished, 5/123 resources updated in 04 seconds
}
You should see intereleaved output from the ``@code{chef-client}''
command running on both nodes at the same time.
You should see the intereleaved output from the @code{chef-client} running on both nodes at the same time.
The last command uses a node search based on the ``@code{name:*}'' criteria.
As a result, every nodes will execute ``@code{chef-client}''. In cases where it is necessary,
you can use more specific search strings, such as, for instance, ``@code{name:head}''
and ``@code{name:node-*}''.
}
@instructionstep["Check that the webservers are running"]{
One of the attribures in the @code{apache2} role configures the web server
such that it runs on the port @code{8080}. Let's check that the web servers
are running via checking the status of that port:
@code-sample["chef/knife-netstat.sh"]
@verbatim{@bold{knife ssh "name:*" "netstat -ntpl | grep 8080" }
pc765.emulab.net tcp6 0 0 :::8080 :::* LISTEN 9415/apache2
pc768.emulab.net tcp6 0 0 :::8080 :::* LISTEN 30248/apache2 }
Note that this command sends an arbitrary command (unrelated to Chef)
to a group of nodes. With this functionality, the @code{knife} command-line utility
can be viewed as an orchestration tool for managing groups of nodes that is capable
of replacing @code{pdsh}, the Parallel Distributed Shell utility, that is often used on computing clusters.
The output that is similar to the one above indicates that both @code{apache2} web servers are running.
}
@instructionstep["Check benchmarking results on node-0"]{
A lot has happened on @tt{node-0}:
Many different actions have taken place on @tt{node-0}, including:
@itemlist[
@item{apache2 has been installed and configured}
@item{ab, the utility for benchmarking of web servers included in the apache2-utils, has been installed and executed}
@item{raw benchmarking results have been saved, and plots have been created using gnuplot}
@item{the plots have been made accessible using the installed web server}
@item{@code{apache2} has been installed and configured}
@item{@code{ab}, a benchmarking tool from the @code{apache2-utils} package, has been installed and executed}
@item{benchmarking results have been saved, and plots have been created using the @code{gnuplot} utility}
@item{the plots have been made available using the installed web server}
]
To see how the last three tasks are performed using Chef, you can take a look at
@link["https://github.com/emulab/chef-repo/blob/master/cookbooks/emulab-apachebench/recipes/default.rb"]{default.rb inside
emulab-apachebench cookbook}. In the in-person tutorial, let's proceed to
To see how the last three tasks are performed using Chef, you can take a look at the
@link["https://github.com/emulab/chef-repo/blob/master/cookbooks/emulab-apachebench/recipes/default.rb"]{default.rb} recipe inside
the @code{emulab-apachebench} cookbook. In the in-person tutorial, let's proceed to
the following steps and leave the discussion of this specific recipe
and abstractions used in Chef recipes in general to the end of the tutorial.
Obtain the @tt{node-0}'s public hostname (e.g., by typing: @code{ssh node-0 hostname -f}),
and contstruct the following URL: @code{http://<node-0's public hostname>:8080}.
Copy and paste it into your browser to access the web server running on @tt{node-0}
and hosting the benchmarking results. You should see a directory listing like this one:
Obtain the @tt{node-0}'s public hostname (refer to the list included in the profile instructions),
and construct the following URL:
@verbatim{http://<node-0's public hostname>:8080}
With the right hostname, copy and paste this URL into your browser to access the web server running on @tt{node-0}
and serving the benchmarking graphs. You should see a directory listing like this:
@screenshot["chef-tutorial/apache-dir-listing-0.png"]
......@@ -681,72 +820,128 @@ executed as @italic{root}). Run the commands described below in that shell.
}
@instructionstep["Run benchmarks against head"]{
Just like @code{nfs.rb} described in the previous section, the @code{apachebench} role has a number of
attributes that define its behavior. We will used the Chef web console to update the value
for one of those attributes.
In the conole, click on the Policy tab at the top of the page, choose Roles in the panel on the left,
and select "apachebench" in the shown list. Select the Attributes tab in the middle of the page.
At the bottom of the page, you should see
a panel called "Override Attributes". Click the "Edit" button inside that panel:
Just like the @tt{nfs} role described in the previous section, @tt{apachebench} has a number of
attributes that define its behavior. We will use the Chef web console to update the value
for one of its attributes.
In the console, click on the Policy tab at the top of the page, choose ``Roles'' in the panel on the left,
and select ``apachebench'' in the displayed list or roles.
Select the Attributes tab in the middle of the page.
At the bottom of the page, you should now see
a panel called ``Override Attributes''. Click the ``Edit'' button inside that panel:
@screenshot["chef-tutorial/console-apachebench-edit.png"]
In the popup window, change the @code{target_host} attribute from @code{null}
to the @tt{head}'s public hostname, which you can find by typing @code{hostname -f} in the shell.
to the @tt{head}'s public hostname (refer to the hostnames listed in the profile instructions).
Don't forget to use double quotes around the hostname. Also, let's modify the list of
ports against which we will run the benchmark. Change @code{80} to @code{8080} since nothing interesting is running
on port @code{80}, while the apache2 server you installed in one of the previous steps
on port @code{80}, while the @code{apache2} server you have just installed
is listening on the port @code{8080}. Leave the port @code{443} in the list --- this
is the port on which Chef web console is running.
is the port on which the Chef web console is running.
Here is an example of the recommended changes:
@margin-note{You don't have to use the Chef web console to implement these changes.
If you prefer to do it in the shell, follow these steps:
@itemlist[
@item{Edit the file @code{/chef-repo/roles/apachebench.rb} on @tt{head} using a text editor of your choice (e.g., @code{vi})}
@item{After making the suggested changes, ``push'' the updated role to the server by typing:
@code{knife role from file /chef-repo/roles/apachebench.rb}}
]
}
@screenshot["chef-tutorial/console-apachebench-change-attr.png"]
When these changes are complete, click "Save Attributes".
The updated version of the role is available on the server now.
You can run it on @tt{node-0} using the following command:
@code-sample["chef/knife-chef-client-with-role-search.sh"]
@verbatim{@bold{knife ssh "role:apachebench" chef-client}}
This command uses the node search based on...
This command, unlike the @code{knife} commands you issued in the previous steps, uses a search based on the assigned roles.
In other words, it will find the nodes to which the @tt{apachebench} role has been assigned by now --- in our case, only
@tt{node-0} --- and execute ``@code{chef-client}'' on them.
}
@instructionstep["Check benchmarking results again"]{
Go back to your browser and update the page to see the directory listing with new results.
Go back to your browser window and update the page to see the directory listing with new benchmarking results.
@screenshot["chef-tutorial/apache-dir-listing-1.png"]
The first two (the most recent) graphs represent the results of benchmarking
of web services running on @tt{head}. You can explore...
of the web services running on @tt{head} performed from @tt{node-0}. Among many interesting facts
that are revealed by these graphs, you will see that the response time is much higher
on the port @code{443} than on the port @code{8080}.
@screenshot["chef-tutorial/bench-graphs.png"]
}
]
On these graphs, you can see that...
@bold{Summary:} You have just...
@subsection{Exploring The Structure}
ToDo: describe how apache2 is implemented. apache2 sets attributes and calls a cookbook from Supermarket.
ToDo: descibe how apachebench is implemented. It calls a custom emulab-apachebench cookbook.
ToDo: describe the most interesting features in emulab-apachebench.
ToDo: point out that many Chef resources are available for common administrative needs. Point to the
@link["https://docs.chef.io/resources.html#resources"]{list of supported Chef resources}.
@section{Final Remarks about Chef}
ToDo: powerful and flexible; allows leveraging community-developed infrastructure code; promotes repeatability and reuse of
components and configurations.
ToDo: The demonstrated profile streamlines the process of creating instances of Chef...
@section{Terminating the Experiment}
@bold{Summary:} You have just performed an experiment with @code{apache2} and @code{ab} in
a very automated manner. The Chef role you have used performed many tasks,
including setting up the infrastructure, running a benchmark, saving and processing results, as well as
making them easily accessible. The following section will shed some light on how
these tasks were accomplished and also how they can be customized.
@subsection{Understanding the Internals}
Below we describe some of the key points about the pieces of infrastructure code you have just used.
The @tt{apache2} role is stored at @link["https://github.com/emulab/chef-repo/blob/master/roles/apache2.rb"]{roles/apache2.rb}
as part of the @link["https://github.com/emulab/chef-repo"]{emulab/chef-repo}.
Note that the run list specified in this role includes a cookbook @code{apache2}
(the recipe called @code{default.rb} inside the cookbook is used)
and also a recipe @code{mod_autoindex} from the same cookbook.
This cookbook is one of the cookbooks that have been obtained from @link["https://supermarket.chef.io"]{Chef Supermarket}.
All administrative work for installing and configuring the web server is performed by the recipes
in this cookbook, while the role demonstrates an example of how that cookbook can be ``wrapped''
into a specification that satisfy specific application needs.
The @tt{apachebench} role is stored at @link["https://github.com/emulab/chef-repo/blob/master/roles/apachebench.rb"]{roles/apachebench.rb}
It specifies values for several attributes and adds a custom cookbook @tt{emulab-apachebench} to the run list.
We use the ``@code{emulab}'' prefix in the names of the cookbooks that have been developed by the CloudLab staff
to emphasize that they are developed for @link["https://www.emulab.net/"]{Emulab} and derived testbeds such as @(tb).
This also allows distinguishing them from the Supermarket cookbooks, which are installed in the same directory on @tt{head}.
Inside the @link["https://github.com/emulab/chef-repo/blob/master/cookbooks/emulab-apachebench/recipes/default.rb"]{default.rb}
recipe in @tt{emulab-apachebench} you can find many key words, including
@code{package}, @code{log}, @code{file}, @code{execute}, @code{template}, etc.
They are called Chef @bold{resources}. These elements, which allow defining fine-grained configuration tasks and actions,
are available for many common administrative needs. You can refer to the
@link["https://docs.chef.io/resources.html#resources"]{list of supported Chef resources}
and see examples of how they can be used.
Another item that is worth mentioning in is the
``@code{ignore_failure true}'' attribute used in some of the resources.
It allows the recipe to continue execution even when something does not
go as expected (shell command fail, necessary files do not exist, etc.).
Also, when relying on specific files in your recipes, you
can augment resources with additional checks like ``@code{only_if{::File.exists?(<file name>)}}''
and ``@code{not_if{::File.exists?(<file name>)}}'' to make your infrastructure code
more reliable and repeatable (this refers to the notion of @italic{idempotent} code mentioned earlier).
@section{Final Remarks about Chef on CloudLab}
In this tutorial, you had a chance to explore the Chef configuration management system and used
some of its powerful features for configuration management in a multi-node experiment on @(tb).
Even though the instructions walked you through the process of configurating only two nodes,
you can use the demonstrated code artifacts, such as roles, cookbooks, recipes, and resources,
and apply them to infrastructure in larger experiments. With the @code{knife} commands like the ones shown
above, you can ``orchestrate'' diverse and complex applications and environments.
When establishing such orchestration in your experiments, you can take advantage of
the relevant pieces of intrastructure code, e.g., available through Chef @link["https://supermarket.chef.io"]{Supermarket}.
In cases when you have to develop your own custom code,
you may adopt the structure and the abstractions supported by Chef
and aim to develop infrastructure code that is modular, flexible, and easy to use.
The @bold{ChefCluster} profile is available to all users on @(tb).
If you are interested in learning more about Chef and developing custom infrastructure code
for your experiments, this profile will spare you from the need to set up
necessary components every time and allow you to run a fully functional Chef installation
for your specific needs.
@section{Terminating Your Experiment}
Resources that you hold in @(tb) are real, physical machines and are
therefore limited and in high demand. When you are done, you should release
......@@ -764,7 +959,7 @@ the ``Extend'' button the on the status page. You would need to provide a
written justification for holding onto your resources for a longer period of
time.
@section{Taking Next Steps}
@section{Future Steps}
Now that you've got a feel for how Chef can help manage
experiment resources, there are several things you might try next:
......
Dear User,
Chef server and workstataion should now be
installed on head.chefdemo.utahstud.emulab.net.
To explore the web management console, copy
this hostname and paste it into your browser.
Installation log can be found at /var/log/init-chef.log
on the server node.
To authenticate, use the unique credentials
saved in /root/.chefauth on the server node.
Below are several Chef commands which detail the launched experiment:
# chef -v
Chef Development Kit Version: 0.7.0
chef-client version: 12.4.1
berks version: 3.2.4
kitchen version: 1.4.2
# knife cookbook list
apache2 3.1.0
apt 2.9.2
emulab-apachebench 1.0.0
emulab-nfs 0.1.0
...
nfs 2.2.6
# knife node list
head
node-0
# knife role list
apache2
apachebench
...
nfs
# knife status -r
1 minute ago, head, [], ubuntu 14.04.
0 minutes ago, node-0, [], ubuntu 14.04.
Happy automation with Chef!
#
# Cookbook Name:: emulab-nfs
# Recipe:: default
#
if node['hostname'] == node["nfs"]["server"]
include_recipe "emulab-nfs::export"
else
include_recipe "emulab-nfs::mount"
end
root@head:~# knife node run_list add head "role[apache2]"
head:
run_list:
role[nfs]
role[apache2]
root@head:~# knife ssh "name:*" chef-client
apt155.apt.emulab.net Starting Chef Client, version 12.7.2
apt154.apt.emulab.net Starting Chef Client, version 12.4.1
apt155.apt.emulab.net resolving cookbooks for run list:
["emulab-nfs", "apache2", "apache2::mod_autoindex", "emulab-apachebench"]
apt154.apt.emulab.net resolving cookbooks for run list:
["emulab-nfs", "apache2", "apache2::mod_autoindex"]
apt155.apt.emulab.net Synchronizing Cookbooks:
apt155.apt.emulab.net Compiling Cookbooks...
apt154.apt.emulab.net Synchronizing Cookbooks:
....
apt155.apt.emulab.net Running handlers:
apt155.apt.emulab.net Running handlers complete
apt155.apt.emulab.net Chef Client finished, 5/123 resources updated in 04 seconds
root@head:~# knife ssh "name:*" 'netstat -ntpl | grep 8080'
apt154.apt.emulab.net tcp6 0 0 :::8080 :::* LISTEN 2 7664/apache2
apt155.apt.emulab.net tcp6 0 0 :::8080 :::* LISTEN 9 808/apache2
# List files in the NFS directory /exp-share on head; should be empty
ls /exp-share/
# List the same directory remotely on node-0; also empty
ssh node-0 ls /exp-share/
# Create an empty file in this derectory locally
touch /exp-share/NFS-is-configured-by-Chef
# Find the same file on node-0
ssh node-0 ls /exp-share/
#
# This role depends on the emulab-nfs cookbook;
# emulab-nfs depends on the nfs cookbook
# available at: https://supermarket.chef.io/cookbooks/nfs
# Make sure it is installed; If it is not, try: knife cookbook site install nfs
#
name "nfs"
description "Role applied to all NFS nodes - server and client"
override_attributes(
"nfs" => {
"server" => "head",
"dir" => "/exp-share",
"export" => {
"network" => "10.0.0.0/8",
"writeable" => true
}
}
)
run_list [ "emulab-nfs" ]
root@head:~# knife node run_list add node-0 "role[apache2]"
node-0:
run_list:
role[nfs]
role[apache2]
root@head:~# knife node run_list add node-0 "role[apachebench]"
node-0:
run_list:
role[nfs]
role[apache2]
role[apachebench]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment