My Chef way – how to create a development/production chef project

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:

  • Nginx
  • Unicorn
  • MySQL
  • WordPress
  • NodeJS
  • 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:

  1. Vagrant provision is much slower than just running chef-client over ssh.
  2. Learning the knife commands and keys is useful.
  3. 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):
http://www.opscode.com/enterprise-chef/

Follow the instructions here to create organization and here to setup chef-client and knife on your development machine.

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”

As described here, again – I would recommend not to use Vagrant itself for provisioning the machine, just create the cookbook as described. Or copy the cookbook I am using here.

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

To allow separate hostname/ip/domain between development and production machine, I created two attribute files: localhost-defaults.rb and linode-defaults.rb

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:

sudo chef-client

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

8. GOTCHA

  • 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.

 

Upgrading to OSX Maverick? might need to install XCode

After installing OSX Maverick and some gems, I started getting Segmentation Fault when running berkshelf. Recompiling ruby or the gems did not fix the problem. Neither did installing the new command line tools.

Turns out gcc was not the latest version. Perhaps having XCode before the upgrade interferes with the installation. Maverick does not install the new XCode by default and it’s available in the App Store. I had to install XCode to get gcc to be up-to-date.

Before upgrading xcode, gcc –version was:
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)

After upgrade it turned to:
Configured with: –prefix=/Applications/Xcode.app/Contents/Developer/usr –with-gxx-include-dir=/usr/include/c++/4.2.1 Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn) Target: x86_64-apple-darwin13.0.0 Thread model: posix

So if typing gcc –version gives you the first version, you might want to install xcode and then reinstall ruby.

Two Javascript underscore mixins

I found these mixins to be useful. Try is similar to Rails try, that allows to access an object property if it’s not null. The other one is similar to Ruby’s blank? and checks if a string is not empty. (Credit to StackOverflow answer)

Three GOTCHA when it comes to contentEditable

Working with WYSIWYG contentEditable attribute has many surprises. I thought to share two issues I encountered to help others who might stumble on the same issues.

1) Firefox and Chrome use different new line handling. Firefox uses br and Chrome use div. This post gives the best summary of the topic. I normalized the behavior by converting <br> and <div> to new line characters:

[javascript]

str = str.replace(/<div>([^]+?)<\/div>/ig, "\n$1")
str = str.replace(/<br[^>]*>/ig, "\n")

[/javascript]

Note that the [^]+? is a non greedy new line matcher taken from here

2) draggable and contentEditable are not compatible. Took me a few hours to find this issue. But if a HTML element is contentEditable and one of it’s parents is draggable – using the mouse to move the caret or make selection will not work. Removing the draggable attribute from the parents fixes this issue.

3) Paste sanitization is not trivial. See here and here

Creating self signed SSL wildcard certificate on Ubuntu & Trusting in Chrome

There were a lot of articles I had to comb through until I found the right one for Apache and Ubuntu. And one from Linode, that is hosting this blog nonetheless. Follow the instructions here and enter *.domain.com as Common Name if you want a wildcard domain.

To prevent Chrome to bring a big warning every time you enter your website, follow the instructions to add the certificate to your Mac Keychain.