Rails

Managing Settings on Rails Applications across multiple environments


Is it a nightmare to manage global settings in your Rails Apps? I used to have this problem, and I was just tired of it, so I contributed to write a gem! check this out:

MC-Settings

As its README says:

This gem provides an easy and Capistrano-friendly way to manage application configuration across multiple environments, such as development, QA, staging, production, etc.

The Problem to solve

Let's say that you need specific configurations per environment, or even, per server!

Example: I want my stage environment to use a single memcached server, storefront production to use two memcacheds, admin production to have caching disabled and my dev machine to use local memcached.

How would you solve this? The answer is easy: use mc-settings, go to its github page and read the details about it. I'll describe how we were able to manage different configurations without ugly conditionals.

Settings per environment

I'm assuming a normal rails app folder structure.

config/settings/default.yml:

caching:
  enabled: false
  memcached:
    host: localhost
    port: 11211

config/settings/environments/development.yml

caching:
  enabled: true
  memcached:
    host: 127.0.0.1
    port: 11211

config/settings/environments/staging.yml

caching:
  enabled: true
  memcached:
    host: 192.168.10.5
    port: 11211

config/settings/environments/production.yml

caching:
  enabled: false
  memcached:
    host: localhost
    port: 11211

config/settings/stages/admin.yml

caching:
  enabled: false
  memcached:
    host: 
    port: 

config/settings/systems/cluster1.yml

caching:
  enabled: true
  memcached:
    host: 192.168.5.5
    port: 11211

config/settings/systems/cluster2.yml

caching:
  enabled: true
  memcached:
    host: 192.168.5.6
    port: 11211

Capistrano integration

When writing your capistrano recipes, add this task:

namespace :settings do
  desc "Symlinks correct configuration file"
  task :symlink_local_file do
    system_file_name = "#{current_release}/config/settings/" + 
                            "systems/#{full_stage_name}.yml"

    run "cd #{current_release}/config && if [ -f #{system_file_name} ]; " + 
        "then echo 'symlinking systems/#{full_stage_name}.yml into locals' " + 
        "&& ln -sf #{system_file_name} #{current_release}/config/settings/local ; fi"
  end
end

Then, just call it:

namespace :deploy do
  task :default do
    ...
    settings.symlink_local
    ...
  end
end

This will create a symbolic link between your staging config file into config/settings/local folder, mc-settings will load this settings and override any previous value.

In Action

I'll just show the input in every single environment, and you will get the idea:

Local machine:

rails c test
ree > Setting.cache(:enabled)
 => false

It returned false because test didn't have specific configuration and mc-settings loaded this value from default.yml

Local machine:

rails c
ree > Setting.cache(:enabled)
 => true
ree > Setting.cache(:host)
 => "127.0.0.1"
ree > Setting.cache(:port)
 => 11211

When running in development mode, since we specified that the cache should be enabled, it returns the local configuration.

Local machine:

rails c production
ree > Setting.cache(:enabled)
 => false

Notice that in production.yml I set cache(:enabled) as false, how cool, isn't it?

Staging app server

rails c staging
ree > Setting.cache(:enabled)
 => true
ree > Setting.cache(:host)
 => "192.168.10.5"
ree > Setting.cache(:port)
 => 11211

Look at staging.yml, mc-settings return the correct value for this environment, without any manual configuration!

admin app server

rails c production
ree > Setting.cache(:enabled)
 => false

When I saw this I said: yes! now I can disable caching in admin servers very easily without ugly configurations or extra deployment efforts.

cluster1 app server

rails c production
ree > Setting.cache(:enabled)
 => true
ree > Setting.cache(:host)
 => "192.168.5.5"
ree > Setting.cache(:port)
 => 11211

This is where the magic begins, mc-settings + capistrano you can really do fine grain tuning with your configurations and still keep your precious hair!!

cluster2 app server

rails c production
ree > Setting.cache(:enabled)
 => true
ree > Setting.cache(:host)
 => "192.168.5.6"
ree > Setting.cache(:port)
 => 11211

Again, you get a very powerful combination using mc-settings + capistrano, this is how we were able to manage rails configuration across 3 clusters with more than 30 app servers plus the staging servers

Promise to write a new post later on describing how could you get the same mc-settings + capistrano power with services like Heroku and Engine Yard.

Best Practices
De Código, Café y Cervezas 07 – ¿Somos profesionales?
Craftsmanship
De Código, Café y Cervezas 08 – Web Services
Beginner
Administrate review