Forget running ruby on a router

Turns out it's too difficult to keep a 128MB router up and running typo and rails. The biggest issue I had was the router kernel panicking causing reboot and loss of nvram settings. So we're switching back to a nice virtualized ubuntu box and my router can actually do what it does best - route packets and presumably get hax0red.

Published on Fri, 21 Sep 2012 21:06

I finally got monit to keep this blog up and running

After a long struggle, I got monit to restart my webrick when it gets too big. I started by switching back to ruby 1.8.7, which doesn't use multiple pthreads, and it much easier for me to manage. It does use a little more memory, but doesn't spike memory temporarily to high values. I managed to get myself into several monit problems, such as the following image:

htop fully loaded down to 100%

Here's the configuration that ended up working:

check process rails with pidfile /opt/home/blog/tmp/pids/server.pid 
  start program = "/opt/bin/bash -l -c '/opt/etc/init.d/S91rails start'" with timeout 240 seconds
  stop program  = "/opt/bin/bash -l -c '/opt/etc/init.d/S91rails stop'" with timeout 60 seconds
  if cpu > 60% for 2 cycles then alert
  if cpu > 80% for 5 cycles then restart
  if totalmem > 95.0 MB for 2 cycles then restart

check system localhost
  if swap usage > 15% then exec "/opt/bin/killall -9 /opt/local/bin/ruby"

And my startup script S91rails looks like:


#!/opt/bin/bash
# Add the user to /etc/passwd since it disappears on reboot (flashed filesystem is readonly)
AS_USER=blog
grep -q ${AS_USER} /etc/group  > /dev/null || echo "${AS_USER}:x:40:" >> /etc/group
grep -q ${AS_USER} /etc/passwd > /dev/null || echo "${AS_USER}:x:40:40:${AS_USER}:/opt/home/blog:/opt/bin/bash" >> /etc/passwd
# handle comand
case "$1" in
        start)
            su - $AS_USER -c "bash -l -c 'cd /opt/home/blog && nice rails s -d -e production'"
            ;;         
        stop)
            su - $AS_USER -c "bash -l -c 'killall -s INT -q ruby; sleep 10; killall -s KILL -q -w ruby'"
            ;;         
        status)
            su - $AS_USER -c "ps aux | grep 'rails server' >/dev/null || echo 'No rails server running'"
            ;;
        restart)
            stop
            start
            ;;
esac

Published on Thu, 25 Aug 2011 04:03

Running Ruby&Rails on a tiny MIPS machine

Here's a quick summary of which versions of ruby and rails app servers I've successfully ran on my router (see previous post):

Ruby Success Comment
1.8.7p334 YES Typo quickly takes more RAM than is comfortable (95-125MB)
1.8.7 enterprise NO Segfault in GC in webrick
1.9.1 from ipkg-opt NO Cross compiled paths to gcc, libs, and includes are hardcoded wrong
1.9.1p431 YES Typo memory footprint is lower (75-95MB), get nice warning about rails test suites in 1.9.1
1.9.2p290 NO Webrick dies with NotImplementedError: ruby engine can initialize only in the main thread

I've had a lot of trouble finding a compatible rack app server. Unfortunately, webrick is the only one so far.

Rails App Server Success Comment
Webrick YES Can't get uploaded files to work so far
Thin NO Segfault in pthread
Unicorn NO Won't compile because lib_atomic_ops is not supported in MIPS
Passenger-Nginx NO ext/oxt/detail/../detail/spin_lock_pthreads.hpp can't find pthread functions
Published on Tue, 26 Jul 2011 17:08

I'm running this ruby Typo blog on a home router

Hardware

Asus N16 router

To replace my Asus eeePC netbook/apple airport extreme/linksys smallbiz VPN router, I chose the Asus N16 router for $86. It's equipped with 128MB of RAM (the most important thing for this project), 32MB of flash (which allows me to run the largest firmware), and a 480mhz broadcom MIPS processor.

DD-WRT

The DD-WRT project is an open source firmware that runs on tons of home routers. It was easy to install on mine, just flash the build that was listed in their database for my router. Next I connected a USB 4GB flash drive that I partitioned into 3 partitions using a different machine (/opt, SWAP, and /mnt) and enabled all the necessary features in the web GUI to automount /opt and enable JFFS. I finally installed optware using these instructions which proved to much better than figuring out everything be hand. Then I installed the build tools (g++, gcc, make, etc) and nginx to host static content, sqlite as a DB, and probably tons of other packages I forgot as this point. At one point I got mysql server 5.0 working, but went back to sqlite.

The LD_LIBRARY_PATH variable that's preset with optware (maybe in /opt/etc/profile?) breaks the compiler like crazy. I found the only way to fix it was unset LIBRARY_PATH, then I can configure/make software with some success.

Building ruby

I had no success with the binary ruby 1.9.1 package that comes with optware, so I tried to build it myself. Ruby 1.8.7 was fairly easy to build (takes hours of course), though I did have to manually specify the build target as "mips". Ruby 1.9.2 was easy to build and install as well, and uses less memory, but I couldn't get webrick to run without segmentation faulting. RubyEE 1.8.7 seemed to work, but I had seg fault in the GC when I ran webrick.

Installing Typo

First I installed bundler. It takes about 20 minutes to install a gem, unless you specify --no-ri --no-rdoc --no-update-sources, but you have to download the sources at least once. Then I ran bundle install, which takes several hours. I failed to get any app servers working except webrick to work (unicorn is not supported on MIPS, and thin segfaults).

Memory Issues

The webrick process inflates to a pretty large size and I have to kill it frequently. 128MB of RAM is the bare minimum for a big rails app on ruby 1.8.7, I wish that I could get 1.9.x to work. I do some amount of swapping to flash drive, which may be bad for the life of the disk. I'm also planning on using monit to restart rails automatically sometimes. Nginx works like a dream and uses less than 1MB of RAM.

Published on Sat, 23 Jul 2011 04:56

My Rails Performance talk at Pivotal Labs

I gave a talk in Singapore before the ruby conference there, and again in San Francisco for Pivotal Labs. I cover both when you should optimize in an agile world and the how-to to making your rails app faster.

http://pivotallabs.com/talks/133-performance-and-scalability-make-your-app-just-fast-enough-in-an-agile-way
Published on Wed, 18 May 2011 09:55

I'm running this blog on a windows vista netbook running a VM

I have an asus eeepc netbook that recently had a screen-breaking incident (paw-shaped cracked area in the LCD). This seemed like a good fit for running a headless server of some sort. It had 2GB of RAM and 2x1.5ghz intel atom cores, so it's not that far away from an m1.small EC2 instance in terms of performance. This blog was previously run on a VMWare Server image on top of my Windows 7 media PC, which had plenty of extra CPU power, but only 3.25 GB of usable memory to begin with (and I like to play games that use lots of memory).

The natural solution was to put linux on the netbook and move the 2 linux VMs over. I need an OS that supported my netbook hardware and could run VMWare server (I didn't try to switch to hypervisor or anything newer, since it seemed unlikely to support my netbook hardware). I tried the follow OS combinations, in order:

  • Ubuntu 10.04 - cannot install VMWare server
  • Ubuntu 9.10 - cannot install VMWare server
  • Ubuntu 8.04 - VMWare server works, cannot install network drivers easily
  • Ubuntu 8.10 - VMWare server works, cannot install network drivers easily
  • Windows XP - bluescreen during kernel load on setup
  • Windows Vista - worked perfectly

This is rather embarrassing, since I'm no fan of Vista, and I'm sure there's a better solution with VirtualBox or another VM solution that isn't as old as VMWare server.

Published on Wed, 14 Jul 2010 09:16

Introducing mechanized_session and acts_as_other_website

Have you ever wanted to make a website that's just a re-skin or re-layout of another website? I always find myself wanting to do this, especially when there's no mobile version of a site and no API.

Enter mechanzied_session

All the mechanized_session gem provides is a way for the programmer to easily define remote actions that require an authenticated session. By providing these actions and an implementation for how to login, mechanized_session makes your life very easy.

View mechanized_session on GitHub

Acts_as_other_website on rails

MechanizedSession requires work to integrate with a frontend, so if you're building a rails app that just a skin of another website, acts_as_other_website is for you. It provides a default login page, handles all the normal exceptions that MechanizedSession raises, and provides the glue that stores your remote session data in your local session hash.

Example using EngineYard's cloud website

class EySession < MechanizedSession
  action :login do |session, options|
    session.get("https://login.engineyard.com/login") do |page|
      next_page = page.form_with(:action =>"/login") do |form|
        form["email"] = options[:username]
        form["password"] = options[:password]
      end.click_button
    end
    true  # tells MechanizedSession that login was successful.  EY returns a 401 exception if not successful
  end

  action :list_environments do |session|
    envs = []
    session.get("https://cloud.engineyard.com/dashboard") do |page|
      page.parser.css("div.environment").each do |env|
        envs << env.css('h3').first["title"]
      end
    end
    envs
  end
end

Then invoke acts_as_other_website in ApplicationController

class ApplicationController
  acts_as_other_website :using => EySession
end

Finally create actions that call the remote server using the @mechanized_session object like so:

class EnvironmentsController < ApplicationController
  def index
    @environments = @mechanized_session.list_environments
  end
end

The plugin will take care of ensuring that there's always an authenticated session when you do actions that require it, and will redirect users to a provided login page to establish that session when necessary.

View this example online using your iPhone or Safari: http://eycloud.heroku.com

Visit acts_as_other_website on GitHub

Published on Mon, 07 Dec 2009 01:17

Ruby Sandboxing Resources

The sandbox itself

Sandbox Support

Sandbox Examples

Published on Sat, 11 Apr 2009 18:28

How to set up the JRuby sandbox

The JRuby Sandbox is simply a rewrite of why's original sandbox gem in JRuby. It's much less of a hack than the C implementation, and generally considered to be more safe. Here's how I set it up:

  1. Download and install the latest JRuby binaries from CodeHaus (I tested with 1.1.5).
  2. Download the source of the javasand jruby gem from the JRuby addons project
    svn checkout http://jruby-extras.rubyforge.org/svn/trunk/javasand
  3. Compile the gem:
    ant
    BUILD SUCCESSFUL
    If the build fails, it might be because it can't find the JRuby classes. You'll need to find jruby.jar and then add a line to build.xml inside the "build.classpath" path:
    <fileset dir="/path/to/jruby/jars" includes="*.jar" />
  4. Package up the gem:
    jgem build javasand.gemspec
  5. Install the gem:
    sudo jgem install javasand-0.0.2.gem
  6. Test the sandbox with jirb -rubygems
    require "sandbox"
    Sandbox.safe.eval("2+2")
    # yields 4

As you can see above, I had to compile the gem from source. The binary gem of javasand from rubyforge failed with the following exception:

irb(main):001:0> require "sandbox"
=> true
irb(main):002:0> Sandbox.safe
org.jruby.ext.sandbox.Sandkit:714:in `removeMethods': java.lang.NoSuchMethodError: org.jruby.RubyModule.removeMethod(Ljava/lang/String;)V
Published on Sat, 04 Apr 2009 21:15

How to set up the ruby sandbox

There's very little recent work on the MRI ruby sandbox, so here's a quick guide to getting the sandbox installed and running. Unfortunately, the sandbox requires a patched ruby, but luckily it's not that hard to set up.

  1. Download the latest version of ruby 1.8.6 from ftp://ftp.ruby-lang.org/pub/ruby/1.8 (does not work with 1.8.7 or 1.9, sorry)
  2. Download the sandbox gem source from git://github.com/why/sandbox.git
  3. Patch ruby:
    patch -p1 < ../sandbox_gem/patch/ruby-1.8.6-sandbox_needs.patch
    patching file error.c
  4. Compile and install the patched ruby:
    ./configure
    make
    sudo make install
  5. Download and install rubygems from RubyForge
  6. Install the sandbox gem:
    cd sandbox_gem && sudo ruby setup.rb
  7. Test the sandbox:
    require "sandbox"
    Sandbox.safe.eval("2+2")
    # yields 4

Now that you've got the sandbox running, read more about it in my article on Advanced Sandboxing, or my Sandbox Introduction.

Published on Sat, 04 Apr 2009 20:43

RSS