Chef Patterns and Antipatterns

Original author: Doug Ireton
  • Transfer

Preface from the translator


Once my colleague threw me a link to a source article. At first I did not take her seriously. But then, stepping on a bunch of rakes and stuffing a few of his cones, I realized what was going on.

Under the cut you will find some common mistakes. At the same time, the correct approaches to writing and using infrastructure code for Chef will be shown, which help to avoid problems in the future.

The article will be useful for both seasoned "cooks" and beginners.


Antipattern: Cooking Community Cookbooks


I did, maybe you too. It all starts quite innocently and does not portend anything bad. You add a couple of attributes, change metadata.rb. Soon you need to add your own recipe, and maybe even LWRP . Now you have a mess of your changes and the community of code, especially if the cookbook is in active development on GitHub. Sooner or later, conflicts will have to be fixed. It will be difficult to highlight logical errors if both the original cookbook and your changes are almost the same. And how will you now version this Frankenstein? Use the same version as in the upstream? In short, it is an antipattern that is then difficult to fix.

The only exception to the “do not fork the cookbook community” rule is if you intend to bring your changes back to the community. Then you need to create a fisher branch in git and send a pull request. This should be a living branch only until the changes merge with the main branch.

Pattern: Create your own wrapper-cookbook with your own attributes and recipes


Instead of forking a community cookie, it’s better to use it “as is” with a cookie wrapper. In this cookbook-wrapper in metadata.rb, you need to register the dependence on this cookie and use it include_recipeto start recipes from the community cookie. Need to change the default attributes? Then you need to rewrite them in a cookbook wrapper.

The gem Chef-Rewind can help with this pattern .

Antipattern: Using Attributes in Roles


Listing attributes in roles is a dangerous antipattern that could break your production. Present the following scenario. You have a web_serverrole with attributes for the names and settings of two sites that need to be installed on this server. Now imagine that you need to divide these sites into two roles, app_serverand blog_server. Well, and how are you going to test this on the dev environment? You can take a chance and hope that everything will be OK or overwrite attributes in Chef Environments (first on dev, then on qa, etc.) and do not forget to clean them after they get to production. Not the best option.

It’s better to use attributes in a cookbook wrapper.

Pattern: Setting Your Attributes in a Cookbook Wrapper


Chef does not know how to version, but kukbuki - yes. By setting the required attributes in the cookbook wrapper and specifying the desired version of the cookbook in the Chef Environment, you can push the changes from dev to production.

Ideally, this should be part of the CI process with tests and the gradual pushing of changes automatically. But this is another story.

Antipattern: Setting a run list in a role


Chef roles seem to be ideal for storing a list of running recipes. However, this approach suffers from the same drawbacks as the previous antipattern. If you add or remove a recipe from the list run_listin the role, then these changes will apply to all servers with this role, including production servers.

You don’t need to do this:
name "web_server"
description "Role for web servers"
run_list(
  "recipe[base_server::disk_configuration]",
  "recipe[base_server::dhcp_reservation]",
  "recipe[base_server::pagefile]",
  "recipe[utility::install_tools]",
  "recipe[web_server::web_sites]",
  "recipe[base_server::ssl_certs]"
)


Pattern: Specify the run list in the default recipe in the cookbook-e created specially for this (so-called role-cookbook or application-cookbook)


Keep the minimum list of recipes in your role. A complete list of recipes should be stored in the application-cookbook. For example, the web_server role might look like this:
name "web_server"
description "Role for web servers"
run_list(
  "role[base]",
  "recipe{web_server]"
)
 

The default recipe for your cookbook application will look something like this:
# web_server cookbook recipes/default.rb
  include_recipe "base_server::disk_configuration",
  include_recipe "base_server::dhcp_reservation",
  include_recipe "base_server::pagefile",
  include_recipe "utility::install_tools",
  include_recipe "web_server::web_sites",
  include_recipe "base_server::ssl_certs"


Again, this is so that you can easily manage versions of cookbooks and test changes to version 2.1.0 on dev environment while production will have stable 1.5.0.

Conclusion


By following the patterns above, you will save a ton of effort and time. You will not need to make complex merges, since all changes are stored in a wrapper cookbook.

By setting the necessary attributes run_listin cookies, and not in roles, you will not get problems with versioning and isolation of environments.

Also popular now: