Common Custom Chef Solutions »

Created at: 29.11.2011 20:11, source: Engine Yard Blog, tagged: Uncategorized

The Engine Yard Cloud stack that we provide is carefully provisioned and controlled by Chef cookbooks. This main collection of Chef recipes will set everything up on your instances for you whenever any of the following events happen:

  • You create a new instance.
  • You add a new instance to a running cluster.
  • You trigger chef using the dashboard’s “Upgrade” or “Apply” buttons.
  • You use the engineyard gem’s “recipe” commands.

If there is something that you need to configure on one of your instances, or across an environment that is not provided in our stack by our main run, you have the ability to make changes yourself with root access to each instance. However, while it may be tempting to quickly install a package or modify a config file on your instances, you need to bear in mind that these changes will all be lost, should any of the aforementioned events occur.

To give you the ability to modify your instances, but to also provide persistence over rebuilds, we carry out a second Chef run, in which you can add your own recipes.  In the user interface we refer to these as Custom recipes. For detailed instructions on how to create, upload and apply a custom recipe, see our Custom Chef Recipes documentation.

In the rest of this post, we’ll round out your knowledge of the custom Chef system, by showing you some examples that are commonly used and pointing out some of the pitfalls that you should be aware of. With great power, comes great responsibility!

Anatomy of a Chef Run

As you start to create custom cookbooks, you are likely to encounter errors as you perfect the recipes. Having a good idea of what happens when a Chef run is in progress can help you understand what might be going wrong.

When an instance is created via the dashboard or a run is triggered through applying recipes to an environment, the following processes take place:

  • The main Chef run begins
  1. The correct version of the stack (our main cookbooks) is pulled down to your instances and a chef-solo run is started, using our cookbooks.
  2. All the parts of our stack that need to be setup for the environment, based on the options you chose when configuring via the dashboard are configured.
  3. As part of this configuration, various services will be reloaded or restarted.
  4. The instance communicates to the Engine Yard Cloud back-end and lets the dashboard know whether the run was successful. This is what defines whether the status light against your instance is red or green.
  • If the main run successfully completes, the custom Chef run begins.
  1. The last version of the cookbooks that you uploaded with the ey recipes upload command is downloaded to your instance and stored into /etc/chef-custom/
  2. Another chef-solo run is started, using this different set of cookbooks.
  3. The instance again communicates with the Engine Yard Cloud back-end on the status of this run. Again, the status light will change to red or green, depending on whether this run was successful or not.

The logs for each run can be accessed via the dashboard, or can be found in /var/log/chef.{main,custom}.log if SSH is more your thing. You’ll need to look through these if the status light is red, and find out what caused the error(s).

Common Custom Recipes

Changing the timezone

Our AMI sets our instances up in the 'PST8PDT' timezone by default. Based on your location, you may want timestamps in logs to match your timezone.

Changing the timezone on the instances involves modifying a symlink, then restarting some services (e.g.: cron, syslog, etc) so that they take in the new time. It’s a pretty simple recipe to do this (using Great Britain in this example):

# Note that this is for the GB timezone. Look in
# /usr/share/zoneinfo for your relevant file.
service "vixie-cron"
service "sysklogd"
service "nginx"

link "/etc/localtime" do
  to "/usr/share/zoneinfo/GB"
  notifies :restart, resources(:service => ["vixie-cron", "sysklogd", "nginx"]), :delayed
  not_if "readlink /etc/localtime | grep -q 'GB$'"
end

To use this recipe, create yourself a new cookbook, as per our docs, then use the above as an example for the default.rb recipe file.

You can see the different timezones that are available for use by looking under the /usr/share/zoneinfo/ directory.

Installing masked packages

Engine Yard Cloud instances are running a version of Gentoo Linux and as such, you are able to install extra packages that you can find available in any Gentoo distribution. The simplest way to install a package is via the dashboard, and any packages installed via this method will persist rebuilds as they are added in to the main Chef run.

If you are familiar with Gentoo’s portage system, you are probably aware of the concept of packages being "masked". Packages can be masked for various reasons, but usually it just indicates that the package maintainer has not carried out sufficient testing on the package to be comfortable to mark it as stable. This often is no cause for concern, but do be aware that a masked package can introduce instability into an environment.

You cannot install masked packages from the user interface so you need to use a custom chef recipe to accomplish this.

To install a masked package make sure you have the following recipe in place in your cookbooks directory (the relevant Chef methods are set as definitions, so there is no need to use ‘require_recipe’ as all recipe definitions are evaluated during a run - there is no harm in using a require on this recipe in your main recipe file if you wish to make it obvious it’s being used though). Then install packages in your own recipes using the examples in the README:

https://github.com/engineyard/ey-cloud-recipes/tree/master/cookbooks/emerge

Custom MySQL configs

If you need to customize the MySQL configuration, we provide methods to do this as outlined in our docs. Often, if people are making these changes with custom recipes, they usually follow up with a restart of MySQL via an execute call to /etc/init.d/mysql restart, or via a service method with the "restart" action, not realizing that Chef’s service notification methods can be used to conditionally restart MySQL, only if the custom configuration file has actually changed.

The following snippet added into your custom recipe would make sure that MySQL is only restarted if your custom config template actually changed since the last run:

service "mysql" do
  supports :restart => true
  action :enable
end

if ['solo', 'db_master', 'db_slave'].include? node[:instance_role]
  template "/etc/mysql.d/custom.cnf" do
    owner 'root'
    group 'root'
    source 'custom.cnf.erb'
    notifies :restart, resources(:service => 'mysql')
  end
end

Pimp my prompt

If you run any large environments and use SSH heavily, you may find that you’re often scratching your head trying to figure out what instance you’re currently logged in to. This can easily be rectified by modifying your PS1 shell prompt to provide you with the details. However, if you rebuild your environment, then the details you added could well be stale.

The following recipe is perfect for modifying your prompt to show you what instance you are currently on.  It displays the instance role, the instance ID and the name of the environment. You could expand this recipe to make other changes to your Bash shell environment too.

https://github.com/omgitsads/ey-cloud-recipes/tree/custom_prompt/cookbooks/custom_prompt

Kudos to our App Support Engineer, Adam Holt for that one.

Monitoring custom scripts with monit

If you have any custom daemon-like scripts or rake tasks that you need to have running on your instance(s), it’s a good idea to put them under the watch of monit, to make sure that they behave (i.e.: can’t consume too much RAM or CPU resource) and are restarted automatically, if they crash for any reason. Monit relies on PIDs and PID files to keep an eye on processes, and it’s a good idea to create a wrapper script for your script that will ensure that a PID file is created for this purpose.

Here’s an example recipe that will create a wrapper script for you that drops PIDs and the relevant monit configuration file so that your script/task will be monitored:

https://github.com/tjl2/ey-script-wrapper

Again, to use this recipe, just clone it, copy into your cookbooks directory, modify the variables in the default.rb file and require it in your main cookbook recipe file.

The Node Object

As you will see from some of these examples and from the documentation, you can pull out details about the instances & environments from the node object. However, aside from the examples that are peppered throughout our custom recipe repo and the docs, how do you know what information you can get from node?

During both of the Chef runs, the node object contains a combination of everything Chef knows about the instance along with details we provide about your environment, instances and applications. You can find out what Chef knows about your instance by obtaining output from the ohai command (you can then parse the resultant JSON file in IRB):

sudo ohai > ~/ohai.json

Likewise, you can find the other Cloud-specific details that we make available via the /etc/chef/dna.json file. Make sure you copy the file with sudo and change the ownerships first, before parsing it in IRB:

sudo cp /etc/chef/dna.json ~/dna.json && sudo chown deploy:deploy ~/dna.json

The above assumes that you have left the deploy user with the standard name, "deploy". Please change as appropriate if required.

To parse these files you can then do the following in IRB:

# Run these commands first:
#   sudo ohai > ~/ohai.json
#   sudo cp /etc/chef/dna.json ~/dna.json
#   sudo chown deploy:deploy ~/dna.json
# Then start an irb session in ~

require 'json'
ohai = JSON.parse(File.read('ohai.json'))
dna = JSON.parse(File.read('dna.json'))

# Examples...
# Find the instance type:
puts ohai['ec2']['instance_type']

# Find the instance role:
puts dna['instance_role']

When running your scripts, the contents of both of the hashes in the above snippet will be available in the node object.

Warnings and Caveats

While the provision of the second Chef run allows you an almost unlimited scope for customizing your instances, it can also give you enough rope to hang yourself with! Bear the following in mind when you start to customize your environment:

  • Be careful if you make changes to Nginx or HAProxy port numbers - this commonly occurs when people try to implement caching technologies which want to listen on the ports that are reserved for HAProxy or Nginx. This can often lead to problems when the main Chef run happens, as HAProxy or Nginx can’t restart properly as they try to bind back to their original ports.  Use netstat -nap | grep LISTEN to see our current ports that you shouldn’t use before starting any recipe that will modify port numbers.
  • If you’re currently customizing a solo instance environment, always think through how your changes may differ if you grow your environment to a cluster. For example, make use of the node[:instance_role] to ensure that recipes that should only run on an application instance don’t try to run on a DB instance, for example. A common issue that occurs when customers upgrade to a cluster from a solo instance arises when they have been setting up custom Nginx server configurations. This can cause problems because clusters have HAProxy sat in front of Nginx and Nginx then listens on port 81 (and 444 for SSL connections), rather than 80 (and 443). Custom configs need to be adjusted accordingly.
  • If you're running a clustered environment and have been testing out custom recipes, always make sure you don't leave a production environment running with broken recipes. While the recipe may not be causing problems that interfere with the running of your application, if a takeover occurs in the meantime, this may fail when the custom recipes are encountered. This can leave your environment stuck in a takeover loop - continuously killing the app_master and trying to add another in its place due to the errors being thrown by Chef.
  • When testing recipes that are reconfiguring something that is always first set up by the main run, make sure that you hit the 'Apply' button on the dashboard after you have run ey recipes apply (which only runs the custom recipes). Often, changes such of this work the first time they are run, but can often then cause problems that will only show up in the main run when it next happens.
  • Custom recipes are not run on a promoted application master in the event of a takeover (see http://docs.engineyard.com/instance-takeover.html). Usually, this should not be an issue, as nothing that your recipes do will have been undone since the instance was last put through a Chef run, but if you have custom recipes that set up cron jobs on the application master, or you have recipes designed to do something to the application master only, then these will not be applied to the promoted instance automatically and you'll need to hit the 'Apply' button on the dashboard.

Professional Services

If you get stuck or you just want to give us the specs of the chef recipes you need, don’t forget that we offer the help of our professional services team to help you get them done. They can give you the extra expertise you need to get you from a red light to a green light.

Further Reading

http://wiki.opscode.com/display/chef/Resources
https://github.com/engineyard/ey-cloud-recipes
https://github.com/opscode/cookbooks
https://github.com/37signals/37s_cookbooks


more »

Hurricane Alert »

Created at: 27.08.2011 03:49, source: Engine Yard Blog, tagged: Uncategorized

In advance of Hurricane Irene, the Engine Yard support team has taken a number of steps to help customers who might be affected this weekend.

We will carefully monitor Amazon’s status pages to keep an eye on any outages within the AWS cloud. In addition, we have put additional support engineers on-call in case of any outage. Finally, our development team will be available to assist our support personnel should the need arise.

We’d like to take a moment to remind Engine Yard customers to file a ticket should you experience any issues. We will respond to all tickets to the best of our ability.


more »

Engine Yard and Orchestra Join Forces »

Created at: 23.08.2011 16:00, source: Engine Yard Blog, tagged: Uncategorized

I’m excited to announce that Engine Yard has acquired Orchestra to add PHP support to the Engine Yard Platform as a Service (PaaS).

Over the past five years, Engine Yard has built the leading PaaS for Ruby on Rails applications. Today, we provide services that power businesses ranging from fast-growing Web 2.0 start-ups and hyper-growth companies like Groupon to Fortune 500 enterprises. We have thousands of customers in over 40 countries, who consume more than 140 million CPU hours each year. We would like to thank our customers for choosing the Engine Yard platform and for relying on us to help them build great applications.

We’re thrilled with the success we’ve had bringing Ruby on Rails to the cloud. Now, we plan to extend our offering to the great community of PHP developers. We believe it’s essential to deliver deep competence for any language we support in our PaaS. We acquired Orchestra to add this necessary PHP expertise, community engagement, and know-how to Engine Yard and our PaaS. By joining forces with Orchestra, we will provide the best Platform as a Service for PHP.

Ruby on Rails is the core of our business today, and it’s growing rapidly. We’ve built a solid platform through meticulous stack curation, years of experience, customer interaction and an extensive ecosystem. Our customer services team delivers expert technical support, professional services, and training to our worldwide customer base. We will continue to invest in the Ruby on Rails community and our Ruby on Rails stack while extending the benefits of our PaaS experience to the PHP developer community.

To learn more about Engine Yard PaaS and our support of PHP and Ruby on Rails, you can watch the video or read our FAQ. Engine Yard customers can also contact their sales rep for more information by calling 866-518-YARD.


more »

Strap on your helmets – It’s time for a Rubininar »

Created at: 13.08.2011 01:34, source: Engine Yard Blog, tagged: Uncategorized

Hello, dear Rubyists of all shapes, sizes and skill levels. We're having a webinar party and you're invited.

Block off your calendar and come hang out with us on Thursday, August 18 at 11:00-11:45am PDT. Grab a bowl of popcorn. Turn off Twitter and Facebook. Kick back. Put your feet on your desk. And let us rock your world with a whirlwind tour of Rubinius 2.0 and how it will make your life better.

Our aim is to make Rubinius the best possible Ruby experience with tooling, features and performance For The Win™. In this webinar we'll show you some of those features and tools and how to use them to get the most out of your Ruby code.

If you write Ruby code at all, this is meant for you. From very newbish beginner all the way to old white beard philosophizing battle-tested experienced veteran, we've got something for you.

Tune your dial to KRBX at 11:00am Thursday, August 18 for Rubinius and Ruby | A Love Story show.


more »

rspec-2.6.0 is released »

Created at: 12.05.2011 15:58, source: David Chelimsky, tagged: Uncategorized

rspec-core-2.6.0

full changelog

  • Enhancements

    • shared_context (Damian Nurzynski)
      • extend groups matching specific metadata with:
        • method definitions
        • subject declarations
        • let/let! declarations
        • etc (anything you can do in a group)
    • its([:key]) works for any subject with #[]. (Peter Jaros)
    • treat_symbols_as_metadata_keys_with_true_values (Myron Marston)
    • Print a deprecation warning when you configure RSpec after defining an example. All configuration should happen before any examples are defined. (Myron Marston)
    • Pass the exit status of a DRb run to the invoking process. This causes specs run via DRb to not just return true or false. (Ilkka Laukkanen)
    • Refactoring of ConfigurationOptions#parse_options (Rodrigo Rosenfeld Rosas)
    • Report excluded filters in runner output (tip from andyl)
    • Clean up messages for filters/tags.
    • Restore –pattern/-P command line option from rspec-1
    • Support false as well as true in config.full_backtrace= (Andreas Tolf Tolfsen)
  • Bug fixes

    • Don’t stumble over an exception without a message (Hans Hasselberg)
    • Remove non-ascii characters from comments that were choking rcov (Geoffrey Byers)
    • Fixed backtrace so it doesn’t include lines from before the autorun at_exit hook (Myron Marston)
    • Include RSpec::Matchers when first example group is defined, rather than just before running the examples. This works around an obscure bug in ruby 1.9 that can cause infinite recursion. (Myron Marston)
    • Don’t send example_group_[started|finished] to formatters for empty groups.
    • Get specs passing on jruby (Sidu Ponnappa)
    • Fix bug where mixing nested groups and outer-level examples gave unpredictable :line_number behavior (Artur Małecki)
    • Regexp.escape the argument to –example (tip from Elliot Winkler)
    • Correctly pass/fail pending block with message expectations
    • CommandLine returns exit status (0/1) instead of true/false
    • Create path to formatter output file if it doesn’t exist (marekj).

rspec-expectations-2.6.0

full changelog

  • Enhancments

    • change matcher accepts Regexps (Robert Davis)
    • better descriptions for have_xxx matchers (Magnus Bergmark)
    • range.should cover(*values) (Anders Furseth)
  • Bug fixes

    • Removed non-ascii characters that were choking rcov (Geoffrey Byers)
    • change matcher dups arrays and hashes so their before/after states can be compared correctly.
    • Fix the order of inclusion of RSpec::Matchers in Test::Unit::TestCase and MiniTest::Unit::TestCase to prevent a SystemStackError (Myron Marston)

rspec-mocks-2.6.0

full changelog

  • Enhancements

    • Add support for any_instance.stub and any_instance.should_receive (Sidu Ponnappa and Andy Lindeman)
  • Bug fixes

    • fix bug in which multiple chains with shared messages ending in hashes failed to return the correct value

rspec-rails-2.6.0

full changelog

  • Enhancements

    • rails 3 shortcuts for routing specs (Joe Fiorini)
    • support nested resources in generators (Tim McEwan)
    • require ‘rspec/rails/mocks’ to use mock_model without requiring the whole rails framework
    • Update the controller spec generated by the rails scaffold generator:
    • Add documentation to the generated spec
    • Use any_instance to avoid stubbing finders
    • Use real objects instead of mock_model
    • Update capybara integration to work with capy 0.4 and 1.0.0.beta
    • Decorate paths passed to [append|prepend]_view_paths with empty templates unless rendering views. (Mark Turner)
  • Bug fixes

    • fix typo in “rake spec:statsetup” (Curtis Schofield)
    • expose named routes in anonymous controller specs (Andy Lindeman)
    • error when generating namespaced scaffold resources (Andy Lindeman)
    • Fix load order issue w/ Capybara (oleg dashevskii)
    • Fix monkey patches that broke due to internal changes in rails-3.1.0.beta1


more »