Hack the Container: Understanding Docker's Inner Workings
A Deep Dive into Container Prototyping with runC
runC is what makes Docker containers possible at the most fundamental level. The goal of this section is to provide a hands-on experience with this low-level tool to continue understanding how containers work under the hood.
Start by installing Docker on your Linux machine - this will also install runC:
# exit the current namespace if you are still in it
exit
# Install Docker (we will use v29.1.5)
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh --version 29.1.5
# Start Docker service
systemctl start docker
Let's create a directory (/tmp/mycontainer) where we are going to export the contents of the image Busybox. The goal is to extract the filesystem of a Docker image and use it as the root filesystem (rootfs) for a container managed by runC (we create our image manually from scratch, but for simplicity, we use a pre-built image here).
Busybox, if you're new to it, is a lightweight Linux distribution that provides a minimal set of Unix utilities in a single executable file. It's often used in embedded systems and containerized environments due to its small size and efficiency.
# Change to root user
sudo su
# Create the directory
mkdir -p /tmp/mycontainer
# Create a rootfs directory
mkdir -p /tmp/mycontainer/rootfs
# Export the Busybox image filesystem to the rootfs directory
cd /tmp/mycontainer && \
docker export $(docker create busybox) | tar -C rootfs -xvf -
# Finally, list the content of the rootfs directory
ls -l rootfs
Create a spec file (config.json) that will be used by runC to run the container:
# cd into the container directory
cd /tmp/mycontainer
# Create the runc JSON spec file
runc spec
ℹ️ The spec file is a JSON file that contains the configuration of the container. It contains the rootfs path, the hostname, the mounts, the namespaces, the process, and more.
The runc spec command initially created a config.json file with default settings. You can open and inspect this file using any text editor or by running:
jq . config.json
You should see an output similar to the following (truncated for brevity):
{
"ociVersion": "1.2.1",
"process": {
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"sh"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "/",
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [Painless Docker - 2nd Edition
A Comprehensive Guide to Mastering Docker and its EcosystemEnroll now to unlock all content and receive all future updates for free.
Hurry! This limited time offer ends in:
To redeem this offer, copy the coupon code below and apply it at checkout:
