Skip to content

Building in Containers

Andrew J. Hesford edited this page Nov 27, 2022 · 7 revisions

ZFSBootMenu is developed primarily on Void Linux. It is guaranteed to work well with the tools provided therein and is officially packaged for the distribution. On other distributions, the experience is not always as smooth. System packages for ZFSBootMenu or its requirements may be missing entirely. Tooling may be outdated and missing features that ZFSBootMenu uses to provide an enhanced user experience. (Where possible, ZFSBootMenu will test for features and work around their absence.) Fortunately, the proliferation of Linux containers provides a means to work around limitations of particular distributions and provide all users with first-class ZFSBootMenu support.

The zbm-builder Container and Its Helper

ZFSBootMenu provides a build container image, ghcr.io/zbm-dev/zbm-builder, based on a Void Linux image that uses an LTS kernel and relatively recent version of ZFS. When run, the container entrypoint will:

  1. Fetch a specified or default version of the ZFSBootMenu source repository (or use a local copy that is bind-mounted into the container)

  2. Perform an "installation" of this repository into the container instance

  3. Optionally run some scripts to customize the container instance

  4. Merge default and build-specific ZFSBootMenu configurations

  5. Produce a ZFSBootMenu image

To facilitate interaction with the host, the container should be run with a build directory (along with the output directory, if it is not a child of the build directory) bind-mounted into the container.

The zbm-builder.sh helper script provides a simplified frontend for control of ZFSBootMenu build containers. The script coordinates volume mounts necessary to read configurations from and write output to the host and ensure host data (the hostid and ZFS pool cache) are passed through. The script also supports a simple configuration file that allows options to be recorded for repeated use.

A Simple Host-Specific Configuration

zbm-builder.sh mounts a build directory (by default, the current working directory) into the container to provide a path to inject custom configuration into the container. If the system will manage ZFSBootMenu images exclusively via a build container, an obvious location for the build directory is /etc/zfsbootmenu. Start by creating this directory and populating a simple config.yaml for container builds:

mkdir -p /etc/zfsbootmenu

cat > /etc/zfsbootmenu/config.yaml <<EOF
Global:
  InitCPIO: true
Components:
  Enabled: false
EFI:
  Enabled: true
  Versions: false
Kernel:
  Prefix: zfsbootmenu
  CommandLine: zfsbootmenu ro quiet loglevel=4 nomodeset
EOF

curl -L -O /etc/zfsbootmenu/zbm-builder.sh https://raw.githubusercontent.com/zbm-dev/zfsbootmenu/master/zbm-builder.sh
chmod 755 /etc/zfsbootmenu/zbm-builder.sh

In this configuration, mkinitcpio will be used instead of dracut. Component generation is disabled, so generate-zbm will produce only a UEFI bundle. That bundle has numeric versioning disabled, so generate-zbm will produce an unversioned zfsbootmenu.EFI; if the generator detects an existing zfsbootmenu.EFI in the output directory, it will make a single backup of that file as zfsbootmenu-backup.EFI before overwriting it. A simple kernel command-line is specified and may be overridden as necessary.

For some systems, it is necessary to tear down USB devices before ZFSBootMenu launches a boot environment. Even when this is not needed, it is generally harmless. The ZFSBootMenu repository offers contrib/xhci-teardown.sh for this purpose, and it is possible to instruct mkinitcpio to include this teardown hook straight from the version of ZFSBootMenu inside the container:

mkdir -p /etc/zfsbootmenu/mkinitcpio.conf.d
echo "zfsbootmenu_teardown=( /zbm/contrib/xhci-teardown.sh )" \
    > /etc/zfsbootmenu/mkinitcpio.conf.d/teardown.conf

The default mkinitcpio.conf in the container, which should generally not be overridden, will source all files in /etc/zfsbootmenu/mkinitcpio.conf.d.

Custom Font

On high-resolution screens, the Linux kernel does not always do a good job choosing a console font. A nice font can be explicitly specified in the ZFSBootMenu configuration for mkinitcpio. The container entrypoint must be told to install the desired font and the mkinitcpio configuration should include the necessary module and executable to set the font:

echo "BUILD_ARGS+=( -p terminus-font )" >> /etc/zfsbootmenu/zbm-builder.conf

cat > /etc/zfsbootmenu/mkinitcpio.conf.d/consolefont.conf <<EOF
BINARIES+=(setfont)
HOOKS+=(consolefont)
EOF

This approach uses the configuration file capability of zbm-builder.sh to specify build options without requiring that they be included on the command line.

As configured, mkinitcpio will not see a configured console font and will omit the font from generated images. To make mkinitcpio aware of the desired font, it must be specified in /etc/rc.conf within the container. The "terraform" capabilities of the container entrypoint can be used to accomplish this:

mkdir -p /etc/zfsbootmenu/rc.d

cat > /etc/zfsbootmenu/rc.d/consolefont <<EOF
#!/bin/sh
sed -e '/FONT=/a FONT="ter-132n"' -i /etc/rc.conf
EOF

chmod 755 /etc/zfsbootmenu/rc.d/consolefont

When the container entrypoint finds an rc.d subdirectory in the build root, it will run each executable file therein before generating a ZFSBootMenu image. If any of these executable should fail, image generation is aborted.

Host-Specific Files

By default, zbm-builder.sh will copy the /etc/hostid and /etc/zfs/zpool.cache from the host to the build directory so that the container can include these host-specific files in ZFSBootMenu images. This is often desirable for customized builds, but it would be undesirable for copies of these files in /etc/zfsbootmenu to fall out of synchronization with the host versions. To avoid this issue, tell zbm-builder.sh to remove any copies in /etc/zfsbootmenu before determining whether the host versions should be copied in for image creation:

echo "REMOVE_HOST_FILES=yes" >> /etc/zfsbootmenu/zbm-builder.conf

If you would rather not see those files at all, it is possible to instruct generate-zbm to remove them after they are used. Edit the configuration at /etc/zfsbootmenu/config.yaml and add the following key:

Global:
  PostHooksDir: /build/cleanup.d

Alternatively, tell the build container to add this option dynamically:

echo "BUILD_ARGS+=( -e '.Global.PostHooksDir=\"/build/cleanup.d\"' )" \
    >> /etc/zfsbootmenu/zbm-builder.conf

Next, create a post-generation hook to remove the files:

mkdir -p /etc/zfsbootmenu/cleanup.d

cat > /etc/zfsbootmenu/cleanup.d/hostfiles <<EOF
#!/bin/sh
rm -f /build/zpool.cache /build/hostid
EOF

chmod 755 /etc/zfsbootmenu/cleanup.d/hostfiles

The Output Directory

At this point, it should be possible to generate images by running

cd /etc/zfsbootmenu && ./zbm-builder.sh

However, these images will reside in /etc/zfsbootmenu/build and will require manual management. A better alternative is to let generate-zbm manage the ZFSBootMenu output directory directly. Assuming that ZFSBootMenu images should be installed in /boot/efi/EFI/zfsbootmenu, tell zbm-builder.sh to mount the directory inside the container, and tell the container that it should write its images to the mounted directory:

cat >> /etc/zfsbootmenu/zbm-builder.conf <<EOF
RUNTIME_ARGS+=( -v /boot/efi/EFI/zfsbootmenu:/output )
BUILD_ARGS+=( -o /output )
EOF

Now, running

cd /etc/zfsbootmenu && ./zbm-builder.sh

should create images directly in /boot/efi/EFI/zfsbootmenu and create a backup of any existing zfsbootmenu.EFI.

Networking in Rootfull Containers

Manipulating files in /etc/zfsbootmenu and /boot/efi/EFI/zfsbootmenu may require root privileges, which means that zbm-builder.sh and the build container will need to run as root. In some configurations, podman may not provide working networking for rootfull containers by default. A simple fix is to allow the containers to use the host network stack, which can be accomplished by running

echo "RUTNIME_ARGS+=( --net=host )" >> /etc/zfsbootmenu/zbm-builder.conf
Clone this wiki locally