This post is to describe how I got a virtual 32-bit linux machine running inside docker on my 64-bit mac book (2013), mostly as a way for me to remember how I did it. I basically just started using Docker, and I’m surely using it wrong…
I’m currently at Recurse Center (formerly known as Hacker School), a sort of writers retreat for programmers. The last couple of weeks I was working on the trap-tracing tool that I have mentioned in previous blog posts. Since this is written as a very low-level linux tool, I was working on my old 32-bit linux netbook.
Well, my netbook died, so in order to continue working on my main mac os machine, I figured I’d just try to run my tools in a 32-bit linux virtual machine on my mac.
Given that I’m at RC, I asked for suggestions on which VM to use. Google told me to try use virtualbox, but Stanley Zheng told me I should give something called Docker a try, because it’s more light weight.
It turns out Docker is not a VM, and apparently you’re not really supposed to pretend it’s one. Its purpose appears to be to run applications inside a sandboxed, pre-defined linux environment, for example to help deploying code in clouds.
But it is possible to abuse Docker to get a simple linux VM on my Mac, and with some pairing, Stanley and I ensured that the Linux emulation is accurate enough that the trap-tracing tool works (which uses the x86 trap flag and signal handlers).
Later I set up a docker 32-bit Linux container on my machine. Here’s what I did:
1) Install docker
I just followed steps 1, 2 on this page. Since I don’t care about web development I didn’t go further into the tutorial.
2) Download an image, and set up a container:
adubra@mo:~$ docker run -it --name linux32 -v ~/linux32:/usr/local/linux32 32bit/ubuntu:16.04 bash Unable to find image '32bit/ubuntu:16.04' locally 16.04: Pulling from 32bit/ubuntu 50f85dcd8f29: Pull complete Digest: sha256:eeb557f736be17c5a8706176746e0e94c49853ca32d840e4ad06819f22415ddc Status: Downloaded newer image for 32bit/ubuntu:16.04 (...)
This command is an alias for several commands: it tells docker to
create and run a container from the image named “32bit/ubuntu:16.04”,
which is the 32-bit linux I’m using. Since this image doesn’t exist in
the local Docker repository, it will also go and download it and add
it to the local repository of images (use
docker images to see the
images in your repository).
As I understand it, a container is a particular instance of an image,
it represents a machine that you can run code on. And as I understand
it, it is pretty light-weight because its file system will only store
the difference from the image. So you could spin plenty of containers,
for example every time you start some server. Which is the usually the
intended purpose. But I’m creating a container that I will re-use and
run software on. So I gave it the name
linux32 using the —name flag.
I also want to share some of host and the container file system,
that’s why I did using the
-v flag, which binds directories using
I’d really like to bind this into the home directory of a user, but
that user doesn’t exist. So if I bind into
then it will create issues later when adding that user inside the
container (because the directory will already exist and be owned by
the wrong user).
-it flag tells Docker to start an interactive terminal
bash tells it to execute Bash.
So overall, this command will download the image, install the image, create a container from it, start the container, and enter the container, providing an interactive bash shell inside the container.
3) Add my user inside the container
I’m not sure why I shouldn’t just run everything as root, but it feels wrong somehow. So I’ll create a user:
root@a54e562265f0:/# adduser adubra Adding user `adubra' ... Adding new group `adubra' (1000) ... (...)
4) Allow my user to execute sudo
sudo from my newly created user, for example when installing
software inside the container using
apt-get install‘, I’m running into password
issues. I can override these passwords request by adding an override to
the sudoers file:
root@a54e562265f0:/# echo 'adubra ALL=NOPASSWD: ALL' > /etc/sudoers.d/sudo_override
5) Exit the container …
That’s basically it. Now I can see the list of containers that exist (but are not necessarily running) using:
adubra@mo:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES a54e562265f0 32bit/ubuntu:16.04 "bash" 9 min ago Exited (0) 23s ago linux32
Before I can re-enter the linux box again, I have to start the container:
adubra@mo:~$ docker start linux32 linux32
Now I can enter it by executing bash interactively, telling it I want to run as my user:
adubra@mo:~$ docker exec -it --user adubra linux32 bash adubra@a54e562265f0:/$
And in the
/usr/local/ directory I fill find the link to my host directory.
After talking to another fellow RC participant, Robert Hunt, I found he’s also using Docker to emulate a Linux machine on his Mac. He pointed out to me that I’m doing a lot of setup inside the container, and if I screwed it up or deleted it accidentally, I’d have to do it all over again.
He told me that one can create new images that will have all the programs and users that I want by writing a dockerfile. It’s a script that allows assembling a new image from an existing one, that includes all the necessary setup code.
Apparently a dockerfile is supposed to be called “Dockerfile” (like
Makefiles, I guess?). It’s extending an existing image using
<image>:<tag>. Afterwards we can install software using
<command>. Adding users is explained in
this Stackoverflow question.
A simple Docker script that basically does all the stuff we did above
(and also installs some basic
Linux tools) would look like this:
FROM 32bit/ubuntu:16.04 RUN apt-get update RUN apt-get install -y \ man \ ssh \ build-essential RUN echo 'adubra ALL=NOPASSWD: ALL' > /etc/sudoers.d/sudo_override RUN useradd -ms /bin/bash adubra USER adubra WORKDIR /home/adubra
We can then build the image using
adubra@mo:~/docker$ docker build -t linux32_image .
Now it will show up as an image Docker knows about:
adubra@mo:~/docker$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE linux32_image latest 6aa83d83383f 2 minutes ago 361 MB 32bit/ubuntu 16.04 cc30c7e0cb5a 5 months ago 321 MB
linux32_image for the
Docker run command, everything
should be set up already. And now it’s also possible to bind the host
directory directory into the home directory of the user using the
flag to the
run command, because that home directory will already exist.
In the end, it seems like Docker is a viable way to have a Linux VM running on a Mac. Although I’m still not sure how to get GUI tools running, and I still don’t really understand how I can have a 32-bit Linux if Docker doesn’t use real VMs.