I was fiddling with Chef for a while now with the purpose to convert my Google Docs document describing all actions I took to get a Linode server up and running, into a Chef scripts. Another improvement was to have a Vagrant development server when I can test new features/updates before going live. I only needed a single server for development and production that will have the following installed:
- RVM/Ruby and financeRails application.
- Jenkins (to automatically build/deploy)
I will say this right away, if you are planning to learn chef, I found that going with hosted chef is much easier than using chef-solo. And I might even recommend to start with that when experimenting. The reason being:
- Vagrant provision is much slower than just running chef-client over ssh.
- Learning the knife commands and keys is useful.
- Chef-solo does not contain all capabilities of knife. Such as encrypted data bags and search. Although they might be working by now.
1. Signup for hosted chef:
(Free for up to 5 nodes):
2. Install Vagrant and provision
Provision your favorite box, without having any specific provisioner selected. Provision a box from the list. I choose Official Ubuntu 13.04 daily Cloud Image amd64 (No Guest Additions).
Login to the box using vagrant ssh, and enable root account (until we get chef installed):
sudo passwd -u root
Enter a complex password for root password.
3. Use knife to bootstrap the Virtual Machine
From the development machine where chef-client repository was checked out:
knife bootstrap (ipaddress) -E dev -x (username) -p -N (node name)
Node name can be “dev-machine” or anything you wish.
4. Create a cookbook as described in “Berkshelf way”
5. Store “secret” attributes
To store secret attributes, such as root user passwords and ssh certificates I create a file called “secret-attributes.rb” and included it from default.rb attribute file. secret-attributes file is ignored on git.
For a more secure solution look into ChefVault
6. Create separate attributes for production/development server
Chef executes all attribute files and the only way I found to exclude some files from being evaluating is using if statements with the environment.
7. First provision
After creating the cookbook and having attribute files ready, add the default recipe to the node run list:
knife node edit (node_name) -e vim
vim will open up, find “run_list” and add your recipe name:
"run_list": [ "recipe[moshebergman::default]" ]
Save the settings. Run ‘berks upload’ to upload cookbooks to chef server. Then ssh to the development machine. Run:
Keep iterating until execution completes successfully.
Tip: I found having two terminal tabs, one for the SSH session and one for the local environment to work great.
Note: My cookbook will create the default username and after the first run logging in as root in ssh will not work.
8. To production environment
After you are happy with the cookbook and it’s working on development environment, it’s time to move to production.
Bootstrap the production node just like the development node, but with production environment and set versions of cookbooks to be used in production using:
berks apply production
Subsequent changes to recipes/attribute files will require bumping the version on metadata.rb and running:
berks upload; berks apply production;
It’s possible to edit the role versions directly, and use > (any version greater) or ~ (any version within the major version)
knife role edit production -e vim
- Default attribute values are written to chef server on first execution. Subsequent changes to default attribute values will not be applied to existing nodes. To force the changes to apply, edit the node attributes:
knife node edit (node_name) -e vim
Delete the relevant sections from the node attributes and save the changes. Sometime it might be even worthwhile to delete the entire content and restart the node. Save the old content aside since some sections are mandatory (like run_list, name, environment etc.). Return the sections that chef-client will complain that are missing.
- I found a bug with MySQL gem and improvement idea to unicorn-ng that caused me to fork them and use the forked version.
- It’s annoying that using –skip-dependencies with berks upload does not currently work with forked cookbooks. So as long as you use some forked cookbooks you will need to have all cookbooks uploaded on every upload.