Installing 32-bit chroot on Debian Etch

29 Jan 2009
Posted by luciferous
luciferous's picture

On systems that can address up to 32 bits of memory, pointers have to be large enough to store any address – up to 32 bits – and this makes them each cost 4 bytes. Generally, this means that 32-bit systems have 4-byte pointers whereas 64-bit systems have 8-byte pointers. And that a program on a 64-bit system would consume twice as much memory as the same program on a 32-bit system.

(Okay, so it's actually more complicated.)

The other day I read about running 32-bit programs on 64-bit systems by setting up a chroot specifically for running 32-bit programs. Why would I have a program running on a 64-bit system when you could set up a 32-bit chroot in which to run the same program for half the memory? Yeah, I couldn't think of why either. So I set about seeing what was needed.

My specific cause was the pair of Mongrels which normally eat up at least 80MB each (and which now eat less; still the top two memory hungry processes on the slice, but hey):

$ ps -C mongrel_rails -o %mem,size,cmd
%MEM    SZ CMD
16.4 48244 /usr/bin/ruby1.8 /usr/bin/mongrel_rails start -d -e production -a 127.0.0.1 -c
16.3 47992 /usr/bin/ruby1.8 /usr/bin/mongrel_rails start -d -e production -a 127.0.0.1 -c

That's right. They are now taking up about 40MB – half the hungriness. Ok, let's get on with it.

Creating the chroot

First, install debootstrap and dchroot, which are important for setting up the chroot.

  • debootstrap, installs a Debian base system into a directory. Use this to install a 32-bit Debian system.
  • dchroot, allows us to run a program inside a chroot.

I installed everything through aptitude:

--\ Packages to be installed
debootstrap
dchroot
--\ Packages being automatically installed to satisfy dependencies
libboost-program-options1.33.1
libboost-regex1.33.1
libicu36
liblockdev1
schroot

Next, create a directory to hold the 32-bit Debian system. Then, bootstrap it.

# ~/ mkdir /var/etch-386-chroot
# debootstrap --arch i386 etch /var/etch-386-chroot http://ftp.debian.org/debian/

Notice the “–arch i386” bit in the command – this is the specific parameter to get a 32-bit system. When deboostrap finishes, ls the directory and you should see familiar root folders.

Before we can chroot into the new Debian system, add these lines to the /etc/ld.so.conf:

/var/etch-386-chroot/lib
/var/etch-386-chroot/usr/lib
/var/etch-386-chroot/usr/local/lib

Also, add the 32-bit linker to the libs by creating a symlink.

# cd /lib
# ln -s /var/etch-386-chroot/lib/ld-linux.so.2 ld-linux.so.2
# ldconfig

This next step is important: mount the /home, /tmp, /dev, and /proc directories. The /proc mount is important for /etc/init.d/mongrel_cluster to know of currently running Mongrel processes. Open up /etc/fstab and add the following lines:

/home /var/etch-386-chroot/home none rw,bind 0 0
/proc /var/etch-386-chroot/proc none rw,bind 0 0
/tmp /var/etch-386-chroot/tmp none rw,bind 0 0
/dev /var/etch-386-chroot/dev none rw,bind 0 0

Then mount them.

# mount /var/etch-386-chroot/home
# mount /var/etch-386-chroot/proc
# mount /var/etch-386-chroot/tmp
# mount /var/etch-386-chroot/dev

N.B. Please note that before deleting this chroot (i.e. rm -rf /var/etch-386-chroot) you must unmount these partitions or they will be REMOVED along with the chroot.

Now, we can test out our new 32-bit chroot.

# chroot /var/etch-386-chroot

Setting up the Rails stack

On the 32-bit chroot I only needed to install ruby, rubygems, mongrel, rails, and the mysql client libraries. It doesn't really matter how these programs get installed, but I used aptitude for ruby1.8, ruby1.8-dev, and libmysqlclient15-dev; built rubygems from source; then used rubygems to install gems for mongrel_cluster, rails and mysql.

One thing to note: if you don't choose to install ri and rdoc, then when making rubygems from source add --no-rdoc --no-ri:

~/rubygems-1.3.1# ruby setup.rb --no-rdoc --no-ri

I recall an error I had building “native extensions” in the mongrel_cluster gem. Mongrel contains some non-ruby code which needs to be built natively, so make sure to have g++ or the build-essentials package installed.

Lastly, mount the directory containing the mysqld sock.

# mkdir /var/etch-386-chroot/var/run/mysqld
# mount -o bind /var/run/mysqld /var/etch-386-chroot/var/run/mysqld

By this point, you should be able to run the Ruby part of the Rails stack entirely within the 32-bit chroot. Now the trick is calling up the stack from outside of the chroot. Remember the dchroot program installed earlier? Let's set that up.

Run 32-bit programs from outside the chroot

Add this line to /etc/dchroot.conf:

etch386    /var/etch-386-chroot

Now you can start up programs inside the chroot without having to enter it. I took this wrapper script from a helpful Debian article. Create the file /usr/bin/local/runas32bit with the following lines.

#!/bin/sh
ARGS=""
for i in "$@" ; do
        ARGS="$ARGS '$i'"
done

exec dchroot -c etch386 -d -q "`basename $0`" "$ARGS"

Then create a symlink for each part of the Rails stack we want to run outside the chroot. The symlinks allow to run the programs transparently so that users don't notice the programs are actually running from the chroot.

/usr/local/bin# ln -s runas32bit ruby1.8
/usr/local/bin# ln -s runas32bit mongrel_rails
/usr/local/bin# ln -s runas32bit mongrel_cluster

You can test that the ruby1.8 running is the 32-bit version.

# ruby1.8 --version
ruby 1.8.5 (2006-08-25) [i486-linux]

At this point, you can set up /etc/mongrel_cluster and also start up /etc/init.d/mongrel_cluster. Just remember, though, the mongrel_cluster is running inside the chroot so the configuration file needs to be symlinked or something so that it is accessible to those inside the chroot.

# cd /var/etch-386-chroot/etc/mongrel_cluster
# ln -s /etc/mongrel_cluster/mongrel_cluster.yml mongrel_cluster.yml

And, that's it. Hopefully, for those who are stuck with a 64-bit system the 32-bit chroot frees up some memory by running things inside the chroot.

Got a lot of help from this Debian article.

Comments

Thank you so much for this.

Thank you so much for this. I look forward to trying it out within the next day or two!

Dejay Clayton (not verified) | Feb 26th, 2009 at 3:08 pm

You're welcome. I hope

You're welcome. I hope it's worked out :)

luciferous | Mar 6th, 2009 at 3:09 am

Hi, great article! I was

Hi, great article! I was facing a problem: my rails app need to send some emails, but I get this error:

/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/template.rb:95: command not found: /usr/sbin/sendmail -i -t

The host system has sendmail installed and infact before chrooting the sending worked ok. The “guest” system has not sendmail installed. I have to setup it? If I call an application from outside the chroot it cannot access to the host system apps?

Thanks!

Daniele (not verified) | Mar 11th, 2009 at 2:21 am

I solved by setup postfix on

I solved by setup postfix on the host so the rails stack in the chroot can easily access the mail sub system without any need to replicate it. By the way I'm still curious about the possibility to call an external app :)

Daniele (not verified) | Mar 12th, 2009 at 7:41 pm

Had to set up sendmail on

Had to set up sendmail on the chroot (guest). I assume the access only works in one direction i.e. 64-bit host runs 32-bit chroot programs and the 32-bit chroot can't access the host. Would be interested to know if there is a way for the host to make some things accessible. Then again it may be impossible by design so that chroot can be used as an effective jail.

For your last question it depends what you mean by access. From my understanding you cannot execute binaries outside the chroot if you are inside. But you can access servers: for e.g. if you run a database server outside chroot, a client inside the chroot can connect to it.

luciferous | Mar 13th, 2009 at 11:55 am