Linux Containers Internals: runc
Intro to Linux Containers Internals: runc⌗
At this point of our #lci-series, we have seen how to hack around and create containers. In this post, we will introduce some order and standardization. We aimed to demonstrate what goes on under the hood, just one abstraction layer below – a scratch on the surface.
Open Container Initiative (OCI)⌗
The OCI was set up as an open governance structure to create open industry standards around container formats and runtimes. It develops specifications for standards on Operating System and application containers.
OCI has two specs, an Image spec and a Runtime spec. The Image spec defines the archive format of OCI container images, which consists of a manifest, an image index, a set of filesystem layers and a configuration. The Runtime spec specifies the configuration, execution environment and lifecycle of a container.
runc is a CLI tool for spawning and running containers according to the OCI specification. The diagram below demonstrates how
runc fits in the whole “container platform” ecosystem.
Create a container with
I know this is the moment you’ve been waiting for; let’s get to it!
We will pick up from where we were in our last post. We used
debootstrap to get a full root filesystem that formed the basis of our container. We will re-use the same filesystem here.
If you are joining us mid-journey, take a glance at the previous post on chroot; it’s a brief one.
So, I’m here where I have my
ubuntufs directory within
$HOME, looking like this and I’m on WSL2 on a Windows machine:
~/container-intro$ ls ubuntufs/ bin boot dev etc home lib lib32 lib64 libx32 media ...
We will first build
runc from sources. So, first start by installing Go on your Linux system.
$ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.2.linux-amd64.tar.gz $ mkdir ~/.go # just my preference, you can name it as you wish # update $PATH # add this to your .bashrc (or equivalent file) export PATH=$PATH:/usr/local/go/bin # setup $GOPATH export GOPATH=$HOME/.go # chek that all is good $ go version
I’m on a 1 version older Go by the time of this writing, but it shouldn’t matter for now, at least in March 2023.
~/container-intro$ go version go version go1.19.4 linux/amd64
Next, let’s install
$ go install github.com/opencontainers/runc@latest # check version, it's marked unknown since we installed # the latest from "dev" branch $ $GOPATH/bin/runc --version runc version unknown spec: 1.0.2-dev go: go1.19.4
Generate a template runtime spec that will be used to spin off our container:
$ $GOPATH/bin/runc spec
config.json file will be generated with the spec. I’ll draw your attention to fields like
linux.namespaces. Do you see some familiar stuff from what we have covered in the series?
config.json file, update
root.path to the name of your filesystem directory, it is
ubuntufs for my case.
Next, let’s run a sample container:
$ sudo $GOPATH/bin/runc run my-cont
On a new tab on the host, you can run
sudo $GOPATH/bin/runc run list, you should see the container listed:
This gets you in the container shell the same way we’ve been doing in the previous posts, but this time, it’s in a standard and spec-compliant way. Go ahead and play around with it, knock yourself off with things like
htop, test the limits and more.
So far, our filesystem is readonly, if you want to change that you can update
Get more details on
Sorry, no spoiler alerts on where our flight is headed next for this series. Maybe networking, maybe dissecting an OCI image, maybe explaining the whole trip of what happens when you run a command like
docker run, how about
docker pull? Stick with us!
For reading thus far, the custom dictates you have to pick your gift here 🙂 Thanks for reading, see you next!