Cloud Foundry is a great way to deploy your Rails apps, particularly when you want cloud-like deploys without having to place your application on the public cloud. CF itself, however, is still a young product and the day-to-day realities aren’t always pretty. In this post I’m going to walk you through my toolbox of workarounds for running Rails apps on Cloud Foundry today.
Cloud Foundry currently has no support for scheduled tasks, so if your application needs them you’re going to have to roll your own. Luckily, there’s a good gem to do the heavy lifting.
First, add the clockwork gem to the production block in your gemfile:
Then, configure your scheduled tasks in lib/clock.rb:
require './config/boot' require './config/environment' module Clockwork every 1.day, 'My scheduled task', at: '00:00' do MyScheduledTask.run end every 1.hour, 'Another scheduled task' do AnotherScheduledTask.run end end
Finally, deploy a copy of your application to function as your scheduled task runner:
cf push my-app-clock -c 'clockwork lib/clock.rb' --no-route
If your app requires a service like a database, don’t forget to bind your clock app to any services it needs.
And, finally, note that if you have many scheduled tasks, or tasks that are particularly long-running, you should use a background worker to actually execute the tasks. Otherwise your clock can get too bogged down on old and still-running tasks to run new ones. This process is described in detail in a post by Rachel Bobbins.
Cloud Foundry doesn’t yet support one-off processes. This makes running rake tasks and the like rather awkward.
To run database migrations, for instance, the best option (advocated by the docs) is to configure your application to run migrations as part of the application startup command.
You can just set your application to use a startup command like `rake db:migrate && rails s -p $PORT`, but if you’re running more than one instance of the application this may fail, as multiple instances may attempt to migrate simultaneously.
We can make this safer by ensuring that only the first instance attempts migrations. First, create a new rake task:
namespace :cf do desc "Only run on the first application instance" task :on_first_instance do instance_index = JSON.parse(ENV["VCAP_APPLICATION"])["instance_index"] rescue nil exit(0) unless instance_index == 0 end end
Then you can use the new command:
cf push my-app -c 'rake cf:on_first_instance db:migrate && bundle exec rails s -p $PORT'
For other rake tasks, things are a little bit simpler. I like to spin up a separate copy of the application specifically for my one-off tasks:
cf push my-app-runner -c 'rake import_a_bunch_of_things && sleep infinity' --no-route
Again, don’t forget to bind your runner app to any services it needs.
You’ll have to check your logs to know when your rake task has completed, and once it has you should stop the application manually. Otherwise, if Cloud Foundry decides to move your runner app as part of a rebalance, it’ll run your rake task again.
Why `sleep infinity`? CF expects apps to look like web servers, which run forever. If your application terminates, even successfully, CF will restart your application over and over, re-running your rake task, forever.
This is the point where this post earns its title. One frequently wants to access application databases interactively, in either a SQL or Rails console. Cloud Foundry doesn’t support this, but if we can swallow our pride (and are working on an application small enough to justify doing so) it isn’t hard to set up.
All you need to do is add some horrible inline Ruby to your database.yml:
acceptance: <<: *default url:
This little disgrace runs `cf env` to pull out your Cloud Foundry application configuration, and then parses the results. In this case we’re pulling out the cleardb mysql credentials, but the process would be similar for other services.
Yes, this is fragile. `cf env` doesn’t yet offer a flag to return only json, so until it does you’ll have to write a string-munging one-liner to clean up its output before parsing. This will occasionally break when `cf env` decides to change its output format.
After you’ve added the above, you should be able to access a SQL console like this:
RAILS_ENV=acceptance rails dbconsole
You can even run a local rails console against your Cloud Foundry database, or even rake tasks. It should be noted however, that this should be done with extreme caution, since you’re running a local copy of your application against a remote database, and the versions are unlikely to match.
Cloud Foundry is getting better every day, and it’s only a matter of time until workarounds like these are no longer necessary. Until then, however, we’re going to have to be creative to be able to use the conveniences Rails developers expect. As dirty as some of these solutions are, they’re still better than waiting six weeks for the BOFH to provision your acceptance server.
About the AuthorMore Content by Taavo Smith