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
the syntax -v host_path:container_path
.
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 /home/some_user/my_dir
,
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).
Lastly, the -it
flag tells Docker to start an interactive terminal
session, and 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
When executing 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 …
…by running exit
.
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.
Dockerfiles
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 FROM
<image>:<tag>
. Afterwards we can install software using RUN
<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
Using this 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 -v
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.