In an earlier blog post, we talked about using Ansible to install NGINX or NGINX Plus. As for many other types of software out there, there lots of alternatives when it comes to configuration management software. Along with Ansible, one of the most popular is Chef. Both tools have their fans, and there are plenty of articles that compare them. Here we’ll focus on showing how to use Chef to install and configure NGINX and NGINX Plus.

Opscode, the company behind Chef, provides an extensive collection of cookbooks that are easy to install onto your Chef server with a single command. Out of the box, the base cookbook for NGINX is a very powerful tool for installing and configuring NGINX. It can be rather overwhelming for newer Chef users, however, so in this post we’ll go over how to use it to install an NGINX Plus package and set up a basic configuration. (You can also use this cookbook to install NGINX via source code – with or without third-party modules – but we will not be covering that process in this post.)

This post assumes that you have a basic understanding of Chef, its configuration files (recipes, environments, roles, and so on) and its associated tools, mainly knife. If you need to review, check out the tutorials and documentation at learn.chef.io.

Preparing Your Chef Environment

To help make it quick and easy to get a test environment up and running, we’re doing our work in a Chef Zero environment. Chef Zero is a simple, easy to install, in-memory Chef server that is useful for testing chef-client tasks (similar to chef-solo), as well as tasks that require a full Chef server. For full installation instructions, see the Chef Zero homepage on GitHub.

Here is the process for installing chef-zero on a basic Ubuntu 14.04 installation and getting everything in place:

Download and install the Chef package. We’re pulling the package directly from the Opscode servers instead of using apt, because the Ubuntu distributions have a rather outdated version of the Chef package. (The Chef package in the example might even be slightly outdated; to be sure you’re getting the latest package, check the Chef website.)Download the package:

Set two GitHub global variables so that you can clone repos. The values do not actually have to match an existing GitHub user, as we are only cloning public repositories, and here I’m using my name and email address. If you plan on cloning any private repos or pushing any commits, specify values that match your GitHub account.

Clone the chef-zero repo. The repo contains the necessary configuration files to get moving quickly, even though we used gem to install it. It also contains some cookbooks, node definitions, and environment definitions that we will not be using, but are good examples to have if you are new to Chef.

Start the chef-zero process that we’ll work with.By default, Chef Zero listens on port 8889, but the knife configuration file bundled with the chef-zero repo defaults to port 4000. To keep file changes to a minimum, we tell the chef-zero instance to listen on port 4000. We also include the -d flag to make Chef Zero run as a daemon. If you run into any issues, you can try running the process in the foreground for troubleshooting purposes.

~$ sudo chef-zero -p 4000 -d

Downloading and Configuring the NGINX Cookbook

Now we install and configure the NGINX cookbook on our Chef server.

Change directory to the playground directory inside the repo we cloned as ~/chef-zero in the previous section. This enables knife commands to locate the knife.rb configuration file, which resides in the ~/chef-zero/playground/.chef subdirectory.

~$ cd chef-zero/playground/

Download the cookbook and its requirements to your local system. (The term install in the command string is kind of misleading, because the command doesn’t actually install NGINX or anything else; instead it just downloads the files.)

For some reason, the knifecookbook command we ran in the previous step downloads cookbook versions that are incompatible for rsyslog. If we don’t fix it, the incompatibility will cause the following error later on when we upload the cookbooks in Step 4 of :

# DO NOT RUN NOW: example to show error
~/chef-zero/playground/cookbooks$ knife cookbook upload *
Uploading apache2 [1.0.0]
Uploading apt [2.9.2]
Uploading bluepill [2.4.1]
ERROR: Cookbook bluepill depends on cookbooks which are not currently
ERROR: being uploaded and cannot be found on the server.
ERROR: The missing cookbook(s) are: 'rsyslog' version '~> 2.0'

To avoid the error, run this command to pull an earlier version of the rsyslog cookbook:

Create a new recipe file for NGINX Plus installation, based on the existing recipe in the NGINX cookbook for installing the open source NGINX software from a prebuilt binary, package.rb. After we make a couple of changes to the NGINX cookbook, we’ll upload it to the Chef server. The new recipe, plus_package.rb, creates all of the necessary certificates and apt repos needed for access to the NGINX Plus package.Create the plus_package.rb file in the ~/chef-zero/playground/cookbooks/nginx/recipes directory with these contents:

#
# Cookbook Name:: nginx
# Recipe:: plus_package
# Author:: Damian Curry <damian.curry@nginx.com>
#
# Copyright 2008-2013, Chef Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

Create the template file, nginx_plus_status.erb, that is referenced in the last stanza of the plus_package.rb recipe. Put it in the ~/chef-zero/playground/cookbooks/nginx/templates/default directory with these contents:

You will notice that I have added the contents of the nginx-repo.key and nginx-repo.crt files as the values of the nginx_repo_key and nginx_repo_crt attributes. If you use the example as the basis for your nginx_plus.rb file, replace the values in the example with the key and certificate for your NGINX Plus subscription or trial. I prefer recording the key and certificate in attributes this way instead of recording them in files inside of the cookbook, because it makes things more portable, but you can use either method.

It is also possible to define the run_list and default_attributes for an individual host or an environment, inside either the node or environment definition, but I prefer to define them in a role. Then you can apply the role to any node where you want to install NGINX Plus, followed by another recipe that configures NGINX Plus for the specific function the node is to perform.

Notes:

You must define the init_style attribute as init, because its default value of runit does not work with NGINX Plus.

When you copy in the key and certificate from your Subscriptions page at the NGINX Plus customer portal, each one is a block of about 20 lines of text with a hard linebreak at the end of each line. For Chef to parse the key and certificate properly, you must convert each one to a single line by replacing every hard linebreak with the linebreak character, \n (backslash-n). For brevity, the example shows the converted form of only the first and last couple lines from the original blocks.

Bootstrapping and Preparing the Node for NGINX Plus Installation

Next we need to make sure that our node has the correct role in its run list. You can set up the run list when bootstrapping the node or after. To keep the process as simple as possible and thus avoid errors, I prefer to set up the run list after the node has been bootstrapped. Also, I always like to have a local copy of the node definitions so I can modify them on disk and then push the changes to Chef, which lets me keep track of changes using Git.

Run this knifebootstrap command to associate the node with the Chef environment without actually executing any run lists against it. Once the node has been associated with Chef, we will define its run list.In this example I am using the -N flag to set the name of the node to chef-test. You can set any name you like; if you omit the -N flag, the name defaults to the IP address or FQDN provided as the last parameter to the command (127.0.0.1 in this example).

At this point NGINX Plus is installed with a very basic configuration, without any server blocks defined in the http block. This is not the default NGINX Plus configuration, but rather the default configuration from the official Chef NGINX cookbook. Here are the contents of the /etc/nginx/nginx.conf file:

include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; }

Because the basic default site has no virtual servers (server configuration blocks) defined, when you access it you see the NGINX welcome page:

Modifying the Default Options for the NGINX Cookbook

Now, let’s change some of the config variables by defining more attributes in the configuration file for the NGINX Plus role (~/chef-zero/playgrounds/roles/nginx_plus.rb). To match the default configuration in NGINX Plus packages, we are disabling the default site, setting the worker_processes directive to auto, changing the username from www-data to nginx, and turning off the gzip module. We’re also enabling the NGINX Plus live activity monitoring (status) dashboard and API and setting them to listen on port 8080.

Here is what the modified role looks like, with the changes highlighted in bold:

There are many other NGINX configuration options that you can define as attributes in the same way. We aren’t covering them here, but they are all documented in the README.md that is bundled with the cookbook. Keep in mind that the different attributes were designed for the open source NGINX software, so not all of them apply to NGINX Plus.

Now we go ahead and upload the modified role and rerun Chef to see the changes. To avoid errors, we need to manually stop NGINX Plus before running the chef-client command.

~/chef-zero/playground$ knife role from file roles/nginx_plus.rb
Updated Role nginx_plus!

At this point you can display the NGINX Plus dashboard by pointing a browser at port 8080 on your NGINX Plus server’s IP address. There’s not much information on the dashboard’s main, Server zones or Upstreams tabs, because there are no running backend servers for it to monitor (the current entries are for the live activity monitoring module itself). So in the next section we’ll define a configuration of backend servers that takes advantage of some NGINX Plus features.

Creating a Cookbook for a DockerUI Deployment

The base NGINX cookbook does not include variables for creating a server configuration block, so we need to create a basic recipe to create the site configuration file for NGINX Plus. As an example, I am going to configure NGINX Plus as a frontend for DockerUI, adding SSL, basic htpasswd authentication, and a custom log format that makes it easy to track which users are accessing DockerUI.

The demo-deploy cookbook that we are creating depends on a couple of other cookbooks, which we need to download:

htpasswd – Creates and manages htaccess files. This is not an official Chef cookbook, but it is a good option. It depends on the Python cookbook, so we need to install that as well.

selfsigned_certificate – Creates and manages self-signed certs.

Run these five commands to download the required files and verify they are in place:

Create the directory structure that Chef expects, as the first step in creating the new demo-deploy cookbook. This is a very basic cookbook at this time, so we only need to create directories for templates and recipes:

Here are the contents of ~/chef-zero/playground/cookbooks/demo-deploy/recipes/dockerui.rb. Notice that in the third stanza we are using the htpasswd cookbook to add a user named dockerui with the password dockerui in the file /etc/nginx/htpassword. In a real world deployment, you would store the password in an encrypted data bag instead of as plain text.

#
# Cookbook Name:: demo-deploy
# Recipe:: dockerui
# Author:: Damian Curry <damian.curry@nginx.com>
#
# Copyright 2008-2013, Chef Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

Now that DockerUI is running and the NGINX Plus configurations are in place, when we refresh the NGINX Plus dashboard we see a server zone and upstream group for it.

If you send an HTTP request to the server’s IP address, NGINX Plus redirects it to an HTTPS URL and prompts for a username and password. When you provide the username and password defined in the dockerui.rb recipe, you are brought to the DockerUI web interface. The first line of this snippet from the access log shows the redirect:

The custom format adds the second field with the $remote_user variable to report the username that made the request, making it easier to track which users are accessing the DockerUI web interface. Because we placed the log_format directive in the server block for HTTPS traffic, the format is used only for the lines in the snippet after the first one. The first line uses the default format for log entries because it records the redirect operation that happens in the server block for HTTP traffic.

Summary

We’ve barely scratched the surface of what is possible with Chef, but I hope you have a better understanding of how you can implement Chef to deploy NGINX Plus. There are many more cookbooks and modules that can handle almost any administrative function that you need.