How LinuxKit Works

LinuxKit works with Moby. The latter helps in reading and converting the yaml file (like the listed above) into bootable images.

Let’s take a look at the minimal.yml file, to understand how a minimal image can be built:

kernel:
  image: linuxkit/kernel:4.14.46
  cmdline: "console=tty0 console=ttyS0 console=ttyAMA0"
init:
  - linuxkit/init:v0.4
  - linuxkit/runc:v0.4
  - linuxkit/containerd:v0.4
onboot:
  - name: dhcpcd
    image: linuxkit/dhcpcd:v0.4
    command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
services:
  - name: getty
    image: linuxkit/getty:v0.4
    env:
     - INSECURE=true
trust:
  org:
    - linuxkit

  • “kernel” is used to define the used kernel version
  • “init” is used to define the base init of the OS
  • “onboot” is used to define the containers of the system. These containers are executed in order. They should normally terminate quickly when done.
  • “services” is used to define the services that will be run after finishing the boot phase
  • “onshutdown” is used to define the list of containers to run at shutdown
  • “files” is used to import a list of files from your host during the build step
  • “trust” is used to define the list of organizations to trust. The kernel image, for instance, is signed by LinuxKit org. It uses Docker Content Trust.

Let’s get back to our minimal Linuxkit image to understand what each block is doing:

The Kernel Block

kernel:
  image: linuxkit/kernel:4.14.46
  cmdline: "console=tty0 console=ttyS0 console=ttyAMA0"

Defining the kernel image (version) and the console terminals (tty0, ttyS0 or ttyAMA0 ..etc).

  • /dev/tty is the native terminal and it can be either kernel-emulated (0) or hardware-implemented
  • /dev/ttyS is used for (hardware) terminal devices connected through a serial port
  • /dev/ttyAMA0 is for the first serial port’s device on an ARM architecture

If you are interested in reading more about TeleTYpewriters and virtual consoles, you can find interesting information here:

The Init Block

init:
  - linuxkit/init:v0.4
  - linuxkit/runc:v0.4
  - linuxkit/containerd:v0.4

Like all systems, Unix-based systems need initialization. The first process to start during the booting is called init and it’s a daemon that continues running until the system is shut down. The init process is the ancestor of all system processes (directly or indirectly). It adopts also all the orphaned processes.

When creating your DIY Linux, you can define the list of processes to be started at the booting of the system. In the above example, the initialization is using init, runc and containerd.

The Onboot Block

onboot:
  - name: dhcpcd
    image: linuxkit/dhcpcd:v0.4
    command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]

After initializing the system, it boots and this is when we may need to start some services. In the example above, we start a DHCP local service using the command:

/sbin/dhcpcd --nobackground -f /dhcpcd.conf -1

The Services Block

services:
  - name: getty
    image: linuxkit/getty:v0.4
    env:
     - INSECURE=true

In this block, we define the services we want to run after booting. In the code above we are running getty (for Get tty) to manage the defined terminals. The purpose of getty is protecting the system from unauthorized access. When Getty detects a user connection, it prompts for the access credentials by running the ‘login’ program.

Examples:

To start an NTP server:

- name: ntpd
  image: linuxkit/openntpd:v0.4

To start Docker:

- name: docker
  image: docker:17.09.0-ce-dind

A service is a container and sometimes we need specific capabilities. This is an example of how we can setup Nginx service and adding some capabilities to the container running it:

services:
  - name: nginx
    image: nginx:alpine
    net: /run/netns/wg
    capabilities:
     - CAP_NET_BIND_SERVICE
     - CAP_CHOWN
     - CAP_SETUID
     - CAP_SETGID
     - CAP_DAC_OVERRIDE

Complete and Continue