One of our goals here at Chef (née Opscode) is to make installing Chef on a new node as easy as possible so that you can start enjoying the benefits of automation with minimal delay. But what happens to nodes that stick around for extended periods of time?
If you operate in an ecosystem where you cannot treat your infrastructure like garbage then you should have a plan for upgrading the copies of Chef Client that are already running in your environment. With Chef 11, automatic in-place upgrades can be handled using Heavy Water Operations’ omnibus\_updater
cookbook. Chef can update Chef!
To get started, add the omnibus\_updater
cookbook to your run_list. For our example, we will add the omnibus\_updater
to the base
role’s run_list.
{ "name": "base", "description": "All good Chefs have base roles!", "chef\_type": "role", "json\_class": "Chef::Role", "run\_list": [ "recipe[chef-client::delete\_validation]", "recipe[chef-client]", "recipe[omnibus\_updater]" ] }
When chef-client
executes on your node, omnibus\_updater
will collect the required metadata (platform
, platform\_version
and machine['kernel']
) and use this information to download and install the correct package from Chef’s omnitruck API.
The default behavior of omnibus\_updater
is to download and install the latest version of the Chef Client. You can specify which version of the Chef Client you would like to install by setting the node['omnibus\_updater']['version']
attribute appropriately.
Because the version of Chef Client you wish to download is stored as an attribute, you can leverage merge-order precedence to control what version is installed in different scenarios. As an example, let’s walk through an example where your team would like to promote versions of Chef Client through your two Chef environments: staging
and production
.
In staging
, you hard-code the latest release of Chef Client so that your team can test its stability in your infrastructure.
{ "name": "staging", "description": "Test latest chef-client before moving to Prod.", "chef\_type": "environment", "json\_class": "Chef::Environment", "default\_attributes": { "omnibus\_updater": { "version": "11.8.2" } } }
In production
, you hard-code the version of Chef Client that has been approved for use in production by your team.
{ "name": "production", "description": "Use approved, well-tested version of chef in Prod.", "chef\_type": "environment", "json\_class": "Chef::Environment", "default\_attributes": { "omnibus\_updater": { "version": "11.6.2" } } }
Once the decision has been made that 11.8.2 is stable enough for use in production
, all that would be required is to update the attribute in the environment. That version of Chef Client would be installed the next time chef-client
is run.
{ "name": "production", "description": "Use approved, well-tested version of chef in Prod.", "chef\_type": "environment", "json\_class": "Chef::Environment", "default\_attributes": { "omnibus\_updater": { "version": "11.8.2" } } }
If you are unable to access the public Internet to download the omnibus packages from Chef or wish to download your packages from a mirror (e.g. a mirror behind your company’s firewall), you can set the node['omnibus\_updater']['direct\_url']
attribute to point to a specific omnibus package.
One solution could be to set the node['omnibus\_updater']['direct\_url']
attribute in your Chef environment.
{ "name": "production", "description": "Use approved, well-tested version of chef in Prod.", "chef\_type": "environment", "json\_class": "Chef::Environment", "default\_attributes": { "omnibus\_updater": { "direct\_url": "http://example.com/chef-11.8.2-1.el6.x86_64.rpm" } } }
The downside of this implementation is that it will not work in a cross-platform ecosystem. To create a more robust solution, you could create a wrapper cookbook and build the node['omnibus\_updater']['direct\_url']
value based on your specific requirements.
To do this, you would specify the version of Chef Client you want in your production
environment like we did before.
{ "name": "production", "description": "Use approved, well-tested version of chef in Prod.", "chef\_type": "environment", "json\_class": "Chef::Environment", "default\_attributes": { "omnibus\_updater": { "version": "11.8.2" } } }
Then you would write a recipe, like the one in the example below, that uses cross-platform logic to set the node['omnibus\_updater']['direct\_url']
attribute to point to the appropriate Chef Client package before calling the omnibus\_updater
cookbook via include\_recipe
.
# If no version is specified, use a default value (latest) version = node['omnibus\_updater']['version'] || '11.8.2' if platform\_family?('rhel') node.default['omnibus\_updater']['direct\_url'] = "http://example.com/chef\_#{version}-1.el#{node['platform\_version'].to_i}.#{node['kernel']['machine']}.rpm" elsif platform\_family('debian') node.default['omnibus\_updater']['direct\_url'] = "http://example.com/chef\_#{version}-1.#{node['platform']}.#{node['platform\_version']}_#{node['kernel']['machine']}.deb" else Chef::Log.warn "The platform #{node['platform']} has not been accounted for in the download logic. Please update this recipe accordingly." end include\_recipe "omnibus\_updater"
Using Heavy Water Operations’ omnibus\_updater
cookbook you can avoid the laborious and time consuming practice of manually upgrading Chef Client. The omnibus\_updater
cookbook has other useful features that might interest you so I encourage you to read its documentation.