Tales of a Chef Workflow: Cookbook Organization and Maintenance

In this post I want to cover a few small tips and lessons learned from working with Chef for about 5 years now. The last 3 years at DNSimple have helped further shape my personal opinions on organization of Chef cookbooks workflows. What follows is how we do things at DNSimple and not the one and only way to do things with Chef. One of Chef's greatest strengths is its flexibility, however it can also be a challenge for Chef developers of any skill level.

Naming things

They say the 2 hardest problems in computer science are naming things, cache invalidation, and off by one errors. In the Chef world, naming things can be especially troublesome when it comes to organizing all of your cookbooks and Chef artifacts. When you are new to Chef, one of the first things you will do is create a cookbook and write your first recipe. If you're not careful, you may end up hitting one of the little naming quirks in Chef right away.

Let me give you an example: say you want to install and setup redis on a server. You might want to build this yourself instead of using an existing cookbook—which is totally understandable. The first thing you'll write in that new folder is the metadata file which might look something like:

You would actually be good to go with this cookbook unless you begin consuming the public Chef Supermarket. Why? Well, Chef has a flat namespace. Meaning your cookbook name is not scoped to an organization, chef server, or anything unique. To Chef, a redis cookbook you just wrote looks just like a redis cookbook on the supermarket. If you consume cookbooks from other sources you run the risk of colliding with another cookbook of the same name. If you use something like Berkshelf for managing your cookbook dependencies, and you configure the public supermarket as the first choice, it will grab the wrong cookbook and you now run the risk of accidentally running something entirely different in your infrastructure. What you should do for starters is prefix your cookbook name with your organization to avoid this problem. For example, our internal cookbooks start with dnsimple_ and the name of the resource so our chef server cookbook is dnsimple_chef_server. This helps clearly show by name that it is your organization's specific code and not something on the public supermarket.

Another thing that we do, which makes naming our cookbooks important, is that we have individual GitHub repositories for each of our cookbooks. This lets us work on Chef related changes independently and treat each cookbook as a piece of versioned, individual software. This also has benefits for testing and isolation, but that is for another blog post. Separate repos work well for us, but has the drawback of being a lot of directories and things to track. As of this writing we have over a dozen internal cookbooks which has been slowly paired down over the years. For any new developer at dnsimple that can be pretty confusing if you want to contribute changes or even locate the relevant cookbook for a given system in our infrastructure. To help with this problem, we adopted a consisting naming scheme for all of our Chef related repositories by adding the prefix chef-. By doing so, searching becomes very easy in both GitHub and in local development directories. Some folks add a suffix like -cookbook, which also works, but as a team we went with the prefix chef- mainly to help with searching.

Campsite Rule

We follow the "Campsite Rule", which is paraphrased from the boyscout rule, when working on cookbooks. The campsite rule is essentially "leave it better than when you found it." We maintain an internal github wiki page with a list of maintenance tasks to follow any time we work on a cookbook. Following these steps helps us keep coookbooks up-to-date with our programming standards. The task list includes items like updating the rake tasks with what we have in our generator skeleton, updating the metadata, or linting the cookbooks with cookstyle and foodcritic to make sure they're consistent with other cookbooks.

Speaking of the generator skeleton, we maintain an internal chef generator skeleton. This is a feature I covered back almost 3 years ago in our DevOps and Chef at DNSimple blog post, where you can customize the cookbook generator built into ChefDK. We use this to generate cookbooks using that chef- prefix, but in the code it removes that for us so the resulting metadata file does not include that prefix. It also contains all of the rake tasks, Travis-CI configurations for running cookbooks, and some tests to start.

Conclusion

When you first begin working with Chef, the sheer amount of choices you have in workflow can be intimidating and overwhelming. Choosing how you want to maintain your Chef Cookbooks is just as tricky of a subject as ongoing maintenance is with Chef. Don't forget to document your descisions with organization and maintenance which helps not just your team, but your future self looking to ship changes. A team as small as ours chose a little bit more complicated of a path for cookbook organization to gain some flexibility when it comes to testing and naming. Looking back over 3 years of work, I think we made good choices (as well as some short-sighted ones). You can't always see the big picture with every descision you make in your infrastructure, but learning from past decisions and using that wisdom when making future descisions, as well as remembering to bring your legacy code from the past into the present, will help you and your team improve.

Many of our choices are put into the perspective of keeping operations clear and accessible to the rest of the team because we collaborate across teams to deliver ideas to customers. Looking over all of our documentation to help write this blog post also makes me incredibly proud of our team that follows these guides to help make Chef work exceedingly well for us and I hope you feel the same with your Chef environment too.