Docker Image Integration in Yocto
Integrating Docker images into a Yocto distribution is not supported by the meta-virtualization
layer, which only focuses on building OCI images.
This post presents the meta-dockin layer, a simple integration solution that follows the Yocto
philosophy but also has some drawbacks compared to a less conventional approach.
Integration in Yocto
Integrating Docker images into a Yocto distribution means providing a ready-to-use Docker data-root
at runtime containing the required images, without having to download them with docker pull.
In this post we only consider Docker images that are already built and available in a public or private registry.
Before introducing the meta-dockin solution, let’s see the limitations of the
meta-virtualization layer, then present two possible approaches to solve this problem.
Limitations of meta-virtualization
The meta-virtualization layer provides the docker-moby_git.bb recipe to install Docker-CE (moby + Docker CLI).
It also allows building OCI images directly from BitBake recipes.
For example, the app-container.bb image recipe located in recipes-demo/images/ inherits the
image-oci.bbclass class to build an OCI image from the generated rootfs by adding the "oci"
filesystem type.
However, the layer currently does not support integrating container images directly into the rootfs.
This choice is explained by Bruce Ashfield — the maintainer of meta-virtualization — in the
mailing list discussion Importing Docker Images and in his talk
Building and deploying containers with meta-virtualization: now & in the future.
As a result, image integration is still left to the developer.
Build-time integration
The idea behind this approach is to create a BitBake recipe that pulls Docker images into a local data-root, which is then installed into the rootfs.
To do this, a task runs the Docker daemon (dockerd) in the background with the --data-root
option pointing to a directory inside the recipe WORKDIR.
Each image can then be downloaded using docker pull.
Once all images are fetched, the daemon is stopped.
While this approach is relatively easy to implement and provides a ready-to-use data-root at
runtime, it also has several drawbacks:
- The daemon requires privileged rights. Using
sudois considered bad practice in Yocto, and configuring rootless Docker can be complicated. - The build cannot run alongside another Docker daemon on the same machine.
- If the build runs inside a Docker container, additional permissions are required (
mount,unshare), which can lead to permission errors. - Overall, this approach does not really follow the Yocto philosophy.
The meta-embedded-containers layer provides a proof-of-concept implementation of this solution, refer to How to embed a Docker image into Yocto’s root filesystem.
Two-step integration
The second approach works in two steps.
First, Docker images are downloaded as archives and included in the rootfs.
Then, during the first boot, the archives are loaded into the Docker data-root using the
docker load command.
This approach is less restrictive during the build process.
However, the Docker data-root must be stored on persistent storage to avoid loading the archives on every boot. Thus, the overhead of this solution is limited to the first boot and the archives can be removed once loaded.
This method is demonstrated in the talk OCI/Docker containers with meta-virtualization and OE / the Yocto Project.
Meta-dockin
There are several ways to implement this two-step solution.
The meta-dockin layer that I designed during a client project is one example
of such an implementation.
The goal was to install one or more Docker images along with additional elements such as a Docker
Compose file.
To do this, the layer allows Docker images to be declared directly in the recipe SRC_URI variable.
Multiple Docker images can be installed in a single recipe.
Example recipe installing the hello-world Docker image:
inherit dockin
SRC_URI += "docker://hello-world"The layer is composed of three main elements:
- A
dockerfetcher used to download Docker image archives viaSRC_URI - A BitBake class
dockinused to compress and install the archives into the rootfs - A preload systemd service per image to load the archives into the Docker data-root at runtime
Let’s take a closer look at these elements.
Docker fetcher
The layer implements a fetcher in lib/dockin/dockerfetcher.py to download Docker images in archive format.
The rootless and daemonless skopeo tool is used, and must be installed
on the host machine. HOSTTOOLS += "skopeo" is added in layer.conf.
The Docker image archive architecture is set from the TARGET_ARCH variable.
The image tag can be specified using the tag URL parameter, for instance:
SRC_URI = "docker://docker.io/library/busybox;tag=1.36.1"By default, the latest tag is fetched.
Note: For private registries, skopeo login is required before running BitBake.
Dockin class
The dockin BitBake class helps integrate Docker image archives into the rootfs.
Docker image archives are installed in ${datadir}/dockin/archive.
The path can be changed in the recipe with:
docker_archivedir = "/custom/path"Archive compression
Docker images downloaded by Skopeo are uncompressed archives.
To reduce their size in the rootfs, the dockin class compresses them.
The compression method is configured with the following variables defined in the dockin bbclass:
COMPRESSION_TYPE ?= "xz"
COMPRESSION_CMD:xz ?= "xz -f -k -9 -T0 -c"
COMPRESSION_RDEPENDS:xz ?= "xz"Runtime preload service
A systemd preload service is created for each Docker image and packaged in
${PN}-preload, which is installed by default. To disable preload services at
boot, simply set in your recipe:
SYSTEMD_AUTO_ENABLE:${PN}-preload = "disable"By default, the Docker image archive is removed once loaded to free space. If the data-root is non-persistent (volatile storage), this can be disabled in the recipe with:
DOCKIN_PRELOAD_FLUSH = "0"Note: The Docker images are only preloaded if necessary. The dockin bbclass does not start the containers.
Examples
The layer provides two examples in recipes-demo/dockin:
- hello-world.bb: A simple recipe to install the hello-world Docker image.
- compose-example.bb: A recipe installing two images and providing a systemd service to run them with a Docker Compose file.