Make rails3 deployment more robust with inploy

I never touched Capistrano, it looked like a rake spinoff. I started using Vlad instead (my post in Hungarian), but at some point, it started to be very frustrating. Deployment took too long, deployment versions lost their meaning (you won’t remember which was the last one which worked well), and all these small things led me to Inploy by Diego Carrion.Time has passed, and I started feeling the same I felt with Vlad: it started falling into pieces, it was slow, most stuff just didn’t make sense.

However, I’m older now, and I tried to find the problem in my own code instead, and I started to rethink what I really want from deployment, and how can I restructure the procedure alongside my new plans.

For now, my target is just a simple app, with no sidekicks like memcached, mongodb, or solr (I definitely plan to have resque), to only one server. It runs on ruby 1.9, in FreeBSD, served by Nginx + Unicorn.  I use ssh+git for repository, and I don’t want to track deployment versions just another way. This is what I ended up with:

  • I’ll keep using inploy. It does it’s job, and it can be extended very easily. However, my original inploy config sucks, and it pulls the wrong triggers.
  • Deployment tracking is required for staging and production servers, but not on devel servers (yes, I deploy to development servers too, the network is just too baroque for a presentation). The best way to handle them is with git tags. However, inploy doesn’t support them by default.
  • Inploy’s rails3 support is a bit rough. It installs packages to production server you normally use in development or test environments, but you don’t need them in production. It bundles gems to .bundle subdir, but this is a feature rvm solves in a more elegant way.
  • Sometimes it’s required to have different configurations for different deployment environments. In these situations, copy_sample_files is not enough, but rails3 has great feature called generators, just for this.
  • Inploy doesn’t support god (bluepill, monit, etc.), it has to be implemented.

In more general subject, I made the following observations:

  • FreeBSD issue: ruby1.9 native gems don’t compile, because of the operating system’s default compiler settings. Ruby Version Manager doesn’t have this problem. Resolution: use rvm.
  • Another FreeBSD issue: bash from ports doesn’t run .bashrc when you want to run commands remotely via ssh. Linux doesn’t have this shortcoming, and this feature is explicitely in the bash man page even in FBSD. Resolution: use zsh (use Linux is not an option).
  • Regression of rvm: using a global god installation is not an option. Monit and Bluepill have not been considered, because they require root access. Resolution: every application runs it’s own god instance, with the user’s own ruby.
  • Multiple application deployments in the same box may cause socket naming clashes in god. Resolution: every god instance uses different socket names.

Enough idle talk, jump right in! I’ll go through them, not necessary in the order I’d actually done it, but in the order you can follow the whole procedure.

First off, you’ll require git and rvm, with the necessary things added, like ruby version, and bundler installed to the global gemset (global gemset is available in all other gemsets). For the latter:

$ bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
$ echo '[[ -s $HOME/.rvm/scripts/rvm ]] && source $HOME/.rvm/scripts/rvm' >> .rvmstart
$ cat .rvmstart >> .zshenv
$ cat .rvmstart >> .bashrc
$ rm .rvmstart
$ source .rvm/scripts/rvm
$ rvm install 1.9.2
$ rvm use 1.9.2 --default
$ rvm gemset use global
$ gem install bundler --pre

Continue with inploy config:

You can see the config uses special server and template setting, as well as a before_restarting_server block for all environments. There’s another interesting option, deploy.tag: I manage deployments by tags, and it’s dead simple to revert back to a previous release: just re-deploy the previous, proven tag. Of course, this easiness hides it’s traps like database version tracking, but it’s not something we could not solve in finite time. Here comes rails3tags:

Please note setting RAILS_ENV for rake invocation. It is needed because of the tricky bundle installation, which omits test and development groups (and by default it’d start in development mode).

God manager is lightweight too, but it’s not a one-liner either:

It sets up the monitor instance cleanly if it’s not yet set up, (re)loads all configuration, and restarts the whole task. Not more, not less.

OK, let’s move forward. Create a new generator:

$ rails g generator APPNAME/configurations

Then, if you still run Rails3.0.0 beta4, or the bug has not been resolved yet, move lib/generators/configurations into lib/generators/APPNAME directory. The generator itself is not a big deal:

I toyed with the idea of adding a .rvmrc template here, but it is required for first deployment, before any rake tasks can run. Therefore, just put your .rvmrc file in your application root:

That’s it! You can test it out:

$ rake inploy:remote:setup environment=staging
$ rake inploy:up environment=staging

There’s one problem though, for me, an inploy:up runs a couple of times, as if some of the rake tasks would be restarted. I’ll figure it out soon, and I’ll have this post updated.