What are Moby and LinuxKit?

Moby: An open framework to assemble specialized container systems without reinventing the wheel.

and:

LinuxKit, a toolkit for building custom minimal, immutable Linux distributions.

As you should know by now, Docker introduced the Moby project! If you missed it, go read the announcement here by Solomon Hykes.

At the core of the Moby umbrella project, are Moby tool and, LinuxKit. The Moby project site, invites you to:

get started with Moby by running some of the examples assemblies in the LinuxKit GitHub repository.

The purpose of this tutorial is to build the redis-os which was demoed in the DockerCon2017 keynote. Basically, we will build an immutable operating system based on Linux kernel 4.9.x

Cloning LinuxKit

Let’s clone LinuxKit from github and change to it’s directory:

git clone https://github.com/linuxkit/linuxkit  
cd linuxkit

Patch the Makefile

We need to patch the Makefile so it works with tar command in busybox

This is not ideal, but it gets the job done, waiting for better days…

sed -i 's/tar cf - vendor/tar cf - ..\/..\/..\/vendor/g' Makefile  

Build LinuxKit

Let’s build LinuxKit now:

make

If everything goes well, we should find in bin/ linuxkit and moby tools.

ls bin

and expect something like the output below:

linuxkit  moby      rtf

Build redis-os example

Let’s build the redis-os example, which was demoed in the keynotes of DockerCon2017. We will be building this OS from a yaml file

yaml Specification

Quoting the github project readme:

The yaml format specifies the image to be built:

  • kernel specifies a kernel Docker image, containing a kernel and a filesystem tarball, eg containing modules. The example kernels are built from kernel/
  • init is the base init process Docker image, which is unpacked as the base system, containing init, containerd, runc and a few tools. Built from pkg/init/
  • onboot are the system containers, executed sequentially in order. They should terminate quickly when done.
  • services is the system services, which normally run for the whole time the system is up
  • files are additional files to add to the image
  • outputs are descriptions of what to build, such as ISOs.

    For a more detailed overview of the options see yaml documentation.

The below yaml is a copy of this one here

# Minimal YAML to run a redis server (used at DockerCon'17)
# connect: nc localhost 6379
kernel:
  image: "linuxkit/kernel:4.9.x"
  cmdline: "console=ttyS0 console=tty0 page_poison=1"
init:
  - linuxkit/init:b3740303f3d1e5689a84c87b7dfb48fd2a40a192
  - linuxkit/runc:2649198589ef0020d99f613adaeda45ce0093a38
  - linuxkit/containerd:cf2614f5a96c569a0bd4bd54e054a65ba17d167f
services:
  - name: dhcpcd
    image: "linuxkit/dhcpcd:2def74ab3f9233b4c09ebb196ba47c27c08b0ed8"
    binds:
     - /var:/var
     - /tmp/etc:/etc
    capabilities:
     - CAP_NET_ADMIN
     - CAP_NET_BIND_SERVICE
     - CAP_NET_RAW
    net: host
  - name: redis
    image: "redis:3.0.7-alpine"
    capabilities:
     - CAP_NET_BIND_SERVICE
     - CAP_CHOWN
     - CAP_SETUID
     - CAP_SETGID
     - CAP_DAC_OVERRIDE
    net: host
outputs:
  - format: kernel+initrd

Building time

./bin/moby build examples/redis-os

if everything works as expected, we should have as outputs redis-os images.

Extract kernel image: linuxkit/kernel:4.9.x
Pull image: linuxkit/kernel:4.9.x
Add init containers:
Process init image: linuxkit/init:b3740303f3d1e5689a84c87b7dfb48fd2a40a192
Pull image: linuxkit/init:b3740303f3d1e5689a84c87b7dfb48fd2a40a192
Process init image: linuxkit/runc:47b1c38d63468c0f3078f8b1b055d07965a1895d
Pull image: linuxkit/runc:47b1c38d63468c0f3078f8b1b055d07965a1895d
Process init image: linuxkit/containerd:cf2614f5a96c569a0bd4bd54e054a65ba17d167f
Pull image: linuxkit/containerd:cf2614f5a96c569a0bd4bd54e054a65ba17d167f
Add service containers:
  Create OCI config for linuxkit/dhcpcd:2def74ab3f9233b4c09ebb196ba47c27c08b0ed8
  Create OCI config for redis:3.0.7-alpine
Create outputs:
  redis-os-kernel redis-os-initrd.img redis-os-cmdline

Run redis-os

In order to run the image, we need to use this time the linuxkit tool.

./bin/linuxkit run qemu -publish 6379:6379/tcp redis-os

After a while, you’ll get to see a nice, welcoming message:

Welcome to LinuxKit

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/

You’ll have to wait till you get a link becomes ready message before trying to test.

[   25.091563] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[   25.092098] 8021q: adding VLAN 0 to HW filter on device eth0
[   25.094142] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready

Test redis-os from the guest OS (newly built OS)

Let’s abuse netcat to test redis-server from the image itself:

(printf "PING\r\n"; sleep 1) | nc localhost 6379

and expect a PONG back:

+PONG

Test redis-os from another node

(printf "PING\r\n"; sleep 1) | nc node1 6379

and expect a PONG back:

+PONG

That’s sweet! In few minutes we built with LinuxKit an operating system from a simple YAML file.

Next Steps

Go explore other examples from LinuxKit and make sure to stop by the projects too.

Make sure to join the Docker community on Slack and drop any question there!