27/08/2010

Rails3 on joyent shared (rvm way) - Followup

In my last post I wrote about installing rails3 on ruby 1.8.7 on a joyent shared accelerator. After finishing up my install an restarting my new app/toy/site I noticed a couple things.

On Ruby versions

Ruby 1.8.7 works whether its p299 or p302, the thing that fails is net-ssh with it's weird relocation error.

Ruby 1.9.x doesnt compile, period.

On deploying the production environment


Joyent recommends 2 ways of setting up a production environment on its shared environment:
  • using mongrel
  • using lighttpd
Now, I played with lighttpd back in v2.x, and found it hard to keep running, si I wanted to try mongrel.

Plainly put, I am out of luck: mongrel even 1.2.0pre doesn't work well with rails3. it seems it integrates at a low level in the core rails stack. Except that there were massive changes in the rails3 stack with the inclusion of rack as the core of the stack. As a result some file/class called "dispatcher" is missing and trying the normal mongrel_rails start command just fails with the error shown below:
jean@xps:~/dev/rails/giftr$ mongrel_rails start
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...
/home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:239:in `require': no such file to load -- dispatcher (LoadError)
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:239:in `block in require'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:225:in `block in load_dependency'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:591:in `new_constants_in'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:225:in `load_dependency'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0.rc2/lib/active_support/dependencies.rb:239:in `require'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/rails.rb:148:in `rails'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:116:in `block (2 levels) in run'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/configurator.rb:149:in `call'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/configurator.rb:149:in `listener'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:102:in `block in run'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/configurator.rb:50:in `call'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/configurator.rb:50:in `initialize'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:86:in `new'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:86:in `run'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/lib/mongrel/command.rb:210:in `run'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/gems/mongrel-1.2.0.pre2/bin/mongrel_rails:282:in `<top (required)>'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/bin/mongrel_rails:19:in `load'
 from /home/jean/.rvm/gems/ruby-1.9.2-p0/bin/mongrel_rails:19:in `<main>'
This problem is not joyent specific though (and the captured output comes from my dev station hence the ruby 1.9.x, but I have the exact same stack trace on the joyent shared.

Since mongrel is trying to keep backward compatibility with all versions of rails we are in a tight spot. Defects already exist about this in both rails and mongrel, but no solution in sight for the moment (and this is way over my head for now) :
At the same time, using rails server works like a charm starting up mongrel. I am not sure what the difference is to be honnest.

The deploy solution which works for me

In the meantime, starting rails server seems to work fine.Therefore, here is what I did, found a stop script somewhere on the net (sorry I don't remember where) which works fine:
#!/usr/bin/env bash 
EXPECTED_ARGS=1
E_BADARGS=65

if [ $# -ne $EXPECTED_ARGS ]
then
  echo "Usage: `basename $0` {pid_file}"
  exit $E_BADARGS
fi

if [ -f $1 ]
then
  cp $1 $1.stopping
  cat $1 | xargs kill
  mv $1.stopping $1.stopped 
else
  echo "Usage: `basename $0` {pid_file}"
  exit $E_BADARGS
fi
combined with a tweaked deploy.rb recipe to both link the bundler gem folder and the database config file, and to use my scripts :
set :application, "giftr"
set :repository,  "git@github.com:jeantil/giftr.git"

set :scm, :git # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`

set :deploy_to, "/users/home/#{user}/domains/giftr.byjean.eu/apps/giftr"

#role :web, "#{host}.joyent.us"                          # Your HTTP server, Apache/etc
#role :app, "#{host}.joyent.us"                          # This may be the same as your `Web` server
#role :db,  "#{host}.joyent.us", :primary => true # This is where Rails migrations will run

server "#{kemp}.joyent.us", :app, :web, :db, :primary => true
set :user, "#{user}"
set :use_sudo, false
# If you are using Passenger mod_rails uncomment this:
# if you're still using the script/reapear helper you will need
# these http://github.com/rails/irs_process_scripts
set :port_number, "#{portnum}"  


namespace :deploy do

  task :start, :roles => :app do
    run "cd #{deploy_to}/current; rails s -e production -p #{port_number} -d -P #{deploy_to}/shared/pids/giftr.pid"
    run "echo \"WEBSITE HAS BEEN STARTED\""
  end
  task :stop, :roles => :app do
    run "cd #{deploy_to}/current; script/stop  #{deploy_to}/shared/pids/giftr.pid"
    run "echo \"WEBSITE HAS BEEN STOPPED\""    
  end
  task :restart, :roles => :app do
    run "cd #{deploy_to}/current; script/stop  #{deploy_to}/shared/pids/giftr.pid ; rails s -e production -p #{port_number} -d -P #{deploy_to}/shared/pids/giftr.pid"
    run "echo \"WEBSITE HAS BEEN RESTARTED\""
  end
  after 'deploy:update_code', 'bundler:bundle_new_release'
end

namespace :bundler do
  task :create_symlink, :roles => :app do
    shared_dir = File.join(shared_path, 'bundle')
    release_dir = File.join(current_release, '.bundle')
    run("mkdir -p #{shared_dir} && ln -s #{shared_dir} #{release_dir}")
  end

  task :bundle_new_release, :roles => :app do
    bundler.create_symlink
    run "cd #{release_path} && bundle install --without test"
  end
  after "bundler:bundle_new_release", :link_production_files
end


# database.yml task
desc "Link in the production database.yml"
task :link_production_files do
  run "ln -nfs #{deploy_to}/shared/config/database.yml #{release_path}/config/database.yml"
end
Obviously, you will want to replace #{host}, #{user} and #{portnum} with the appropriate values for your host.

25/08/2010

Rails3 on joyent shared (rvm way)

Here is the story of my attempts to get a rails3 app to run on a shared account @joyent.

Forword

First note the following:
- The app I want to run there is a toy I made mostly for myself and for fun but I would still like to have it on a 'real' server
- I own a lifetime joyent account (back when it was textdrive)
- I didn't upgrade to a private accellerator back then cuz I was short on cash
- The shared account sits mostly idle and I don't want to shell out monthly money for a toy app

Environment

I got the app working once using rvm and installing ruby-1.8.7-p299.
Before installing rvm you need to make sure it can find all the tools it needs. Many gnutools are defined as aliases in your .bashrc, rvm doesn't like that. the best way is to
[~] mkdir ~/bin
[~] cd bin
[~] ln -s /usr/local/bin/gdiff diff
[~] ln -s /usr/local/bin/gfind find
[~] ln -s /usr/local/bin/ggrep grep
[~] ln -s /usr/local/bin/gmake make
[~] ln -s /usr/local/bin/gpatch patch
[~] ln -s /usr/ucb/ps ps
[~] ln -s /usr/local/bin/gsed sed
[~] ln -s /usr/ucb/whoami whomai
[~] echo 'export PATH=$HOME/bin:$PATH' >> .bashrc

Make sure you logout / log back in

Installing rvm

From there, installing rvm is pretty straightforward following the installation guide works just fine:
[~] bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
The next step the rvm install guide recommends is to put
[~] echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"'>> .bashrc  # This loads RVM into a shell session.
I put it in bashrc as I wanted to have it for non interactive shells too (running rails and such).

Installing ruby

At the time of my first install, I didn't have a problem but with the recent version of rvm I recommend making sure that .rvm/gemsets/ruby/1.8.7/global.gems points to bundler --pre and doesnt force any kind of version.
[~] rvm install ruby-1.8.7 
which installed ruby-1.8.7-p299 (yes this is important)

Installing gems

I recommend you pre-install your gems. Bundler can use a lot of memory and go over the shared account limits. Manually installing the biggest gems first works just fine
[~] gem install rails --pre --no-ri --no-rdoc
[~] gem install bundler --no-ri --no-rdoc
[~] gem install sqlite3-ruby --no-ri --no-rdoc
[~] gem install capistrano --no-ri --no-rdoc
[~] gem install webrat --no-ri --no-rdoc
This would do it for me, bundler works fine after that.

What did _not_ work for me

ruby-1.9.2

installing ruby-1.9.2 fails on compilation with a link error, from what I understand opensolaris linker has a bug where it doesn't compile some bit of ruby correctly and makes ruby crash seemingly at random. To avoid random crashes ruby developpers added a test called bug-3662 which tried to link a lib called bug.so to detect the bug. I wasn't able to go any further.

Actually a joyent admin had a look into it and proposed a solution

capistrano

on either ruby-1.8.7-p299 or 1.8.7-p302 capistrano dies instantly with :
/users/home/user/.rvm/rubies/ruby-1.8.7-p299/lib/ruby/1.8/i386-solaris2.11/openssl.so: ld.so.1: ruby: fatal: relocation error: file /users/home/user/.rvm/rubies/ruby-1.8.7-p299/lib/ruby/1.8/i386-solaris2.11/openssl.so: symbol EC_GROUP_new_curve_GF2m: referenced symbol not found - /users/home/user/.rvm/rubies/ruby-1.8.7-p299/lib/ruby/1.8/i386-solaris2.11/openssl.so (LoadError)
I assume this is linked with ruby 1.9.2 not compiling. Hopefully joyent will upgrade the environment and fix the linker... In the meantime, cap deploy works from my laptop