Saturday, March 8, 2025

Building a Service Container Image - Fedora Base - ISC DHCP

ISC DHCP Server - Fedora Base

The simplest way to create a new container image is to start with a base image from a well-known distribution. The base images include package management software. Populating a new image is a matter of installing the desired packages. The rest of the package creation is defining the process start command and any volumes or ports that the container instance will use.

Creating a Container Image with buildah

buildah is a container management tool produced by Red Hat as an alternative to Docker. It produces OCI compliant container images.

buildah operates differently from Docker. It composes a container image as a series of CLI steps that allow for very precise container image composition. While it can consume a Dockerfile, it can also be used to write a shell script that composes and optionally publishes the image.

The process below defines a container based on the fedora-minimal image. It installs the dhcp-server package and then cleans the yum cache. The next three lines define the volumes that provide the configuration and database from the OS and the network ports for the service. The final four configuration lines provide metadata for the image. The last line saves the new image to local storage.

The Base Image

All container images start with a base image.[1]. The new image consists of a layering of the base image and new layers created during the image build process.

Many linux distributions provide a couple of OS base images, usually one that looks like a conventional OS deployment and another minimal one that has been stripped down to basically a shell and package manager.

To start a new container image build, use the buildah from command. This command takes the identifier for a base image and returns a container identifier string that is used for the rest of the operations on the new container.

The container created here uses the fedora-minimal base image. It doesn’t specify the version so it will always use the latest.

# Create a new container image
CONTAINER=$(buildah from registry.fedoraproject.org/fedora-minimal)

When this command completes the CONTAINER environment variable contains the new image identifier.

Installing Software

With a distribution base image you can use the standard package management tools to install new software.

# Install the DHCP server package and then remove any cached files
buildah run $CONTAINER dnf install -y --nodocs --setopt install_weak_deps=False dhcp-server
buildah run $CONTAINER dnf clean all -y

The package management software typically caches metadata and the package file so it’s common to clean the cache after all software is installed. Using docker, each line creates a new image layer. This means you have to create a single entry that installs all the software then cleans the cache, leading to long run-on install commands. With buildah you can run those two commands separately or any number of commands and create only a single layer.

Opening Holes

A software container service that doesn’t communicate with the outside world isn’t very useful. Both files and network communication can be allowed to pass through a container boundary.

Configuration Files

The container process is configured by mapping files or directories from the host into the container file system. For the DHCP daemon responding to IPv4 requests there is a single configuration file and a single database file.

# add a volume to include the configuration file
# Leave the files in the default locations
buildah config --volume /etc/dhcp/dhcpd.conf $CONTAINER
buildah config --volume /var/lib/dhcpd/dhcp.leases $CONTAINER

It is possible to create volumes on whole directories. If this container is extended to serve IPv6 it may be preferred to create these volumes on the directories to avoid repetitive volume declarations.

Network Ports

Containers often need to communicate with other processes or servers. If the process listens on a particular network port, that must be declared for the container.

# open ports for listening
buildah config --port 68/udp --port 69/udp ${CONTAINER}

The DHCP service listens on two UDP ports, for the bootp and dhcp services.

Process Invocation

Every container image defines a single process and its runtime environment. The last thing to do once the context has been defined is to invoke the binary that runs the process.

# Define the startup command
buildah config --cmd "/usr/sbin/dhcpd -d --no-pid" $CONTAINER

The placement of the files in the volumes defined above allow the process to run using the default locations for the configuration and for the lease database. If it was useful to move those files inside the container, the invocation can be modified.

Metadata

A container image can contain information about the contents. The author and builder metadata have pre-defined labels, but it is also possible to apply arbitrary key-value pairs to provide additional information.

# Identify the container source
buildah config --author "Mark Lamourine <markllama@gmail.com>" $CONTAINER
buildah config --created-by "Mark Lamourine <markllama@gmail.com>" $CONTAINER
# Indicate the software and the license terms
buildah config --annotation description="ISC DHCPD 4.4.3" $CONTAINER
buildah config --annotation license="MPL-2.0" $CONTAINER

The annotations might also include the base image distro information and the git repository for the build script.

Commit and Tag

When the container image has been properly defined it must be committed and tagged for release.

# Save the container to an image
buildah commit --squash $CONTAINER dhcpd-fedora

The commit step closes the image and applies a local label so that the image can be examined and tested. The --squash directive causes all of the steps to be committed as a single layer.

Once the developer is satisfied, they can apply a global identification tag and push the image to the appropriate repository. This step is not included in the build script to allow for that testing.

# To tag and publish the image
buildah tag localhost/dhcpd-fedora quay.io/markllama/dhcpd-fedora
buildah push quay.io/markllama/dhcpd-fedora

Public repositories require authentication before allowing a user to push a new image, so this must be done once for a build/publish cycle

buildah login quay.io --username markllama
Password: ********

TL;DR - The Container Build Script

The script below is made up of the lines detailed above.

fedora-dhcpd.sh
#!/bin/bash
#
# Create a new container image
CONTAINER=$(buildah from registry.fedoraproject.org/fedora-minimal)

# Install the DHCP server package and then remove any cached files
buildah run $CONTAINER dnf install -y --nodocs --setopt install_weak_deps=False dhcp-server
buildah run $CONTAINER dnf clean all -y

# add a volume to include the configuration file
# Leave the files in the default locations
buildah config --volume /etc/dhcp/dhcpd.conf $CONTAINER
buildah config --volume /var/lib/dhcpd/dhcp.leases $CONTAINER

# open ports for listening
buildah config --port 68/udp --port 69/udp ${CONTAINER}

# Define the startup command
buildah config --cmd "/usr/sbin/dhcpd -d --no-pid" $CONTAINER

buildah config --author "Mark Lamourine <markllama@gmail.com>" $CONTAINER
buildah config --created-by "Mark Lamourine <markllama@gmail.com>" $CONTAINER

buildah config --annotation description="ISC DHCPD 4.4.3" $CONTAINER
buildah config --annotation license="MPL-2.0" $CONTAINER

# Save the container to an image
buildah commit --squash $CONTAINER dhcpd-fedora

Summary

When combined with the dhcpd.container systemd container unit file, this container provides the same DHCP service that you would get by running the service from a package installed on the host OS. You can update the DHCP server and revert it by modifying the dhcpd.container file and specifying a previous release tag.[2] You can update to the current version merely by restarting the service or rebooting the system.

The base image is 146MB and is made up of 100 packages and over 30,000 files. The installation request for the single dhcp-server package results in the installation of 10 package dependencies making up another 27MB.

Altogether the new service image is over 170MB on aarch64. It contains all of the files of the base image plus all of the files of the dhcp-server package and dependencies. But it only runs one binary. The rest of the files in the image are unneeded for operation.

There is another way: Create a minimal container image from scratch.

References


1. The base image can be scratch, an empty image
2. I need to see if Environment and EnvironmentFile values can apply to the Image option.

Thursday, March 6, 2025

Deploying ISC DHCP Server in a systemd Container

ISC DHCP Server in a Container

This repository describes the operation of a software container to run the ISC DHCP service.

Note
ISC DHCP has been deprecated and end-of-lifed as of Dec 2022 in favor of ISC Kea

ISC DHCP has been the standard DHCP server since the release of version 1.0 in 1998. While ISC has deprecated the orginal DHCP as of Dec 2022 in favor of ISC Kea, the oringal DHCP server remains in use and is a standard package on most Linux distributions.

Conventional packages are tightly bound to the OS distribution and to the version of the OS installed on a particular host. Network services and the systems that host them are crititical infrastructure. The fear of downtime often results in aversion to regular updates.

The fear of update failures can be mitigated by providing for atomic rollback. This means that, in the event of an update induced failure, the service can be rolled back reliably to the previous version. Containerized services offer a way to accept frequent updates while also allowing rapid rollback to a previous version without the fear of conflict or system pollution that packaged software risks.

The dhcp container encapsulates the ISC DHCP daemon so that it can run on any Linux host that uses systemd for system initialization and service management.

Container Images

This repository contains scripts to create two different but functionally equivalent container images. Both are based on the Fedora dhcp-server package. This package provides the ISC DHCP server binary: dhcpd.

The dhcpd-fedora image uses the fedora-minimal base image. The dhcpd image is built from scratch.

This document describes the usage of these images to provide a DHCP service on a host capable of running software containers from systemd-containers. To learn how the container images were built, see:

Prerequisites

The system must have the BOOTP and DHCP ports (68/UDP and 69/UDP) free and must be capable of running systemd containers.

  • Static IP addresses
    The DHCP service itself requires that all IP addresses on the host are configured statically. The DHCP client will, obviously, conflict with the server for port bindings. This took me an embarrassingly long time to figure out when testing the container image.

  • containers-common package
    The host server must be able to run software containers and must have the containers-common package installed. This package provides the bindings that allow podman to generate systemd service unit files from a container service file. On Debian this is the golang-github-containers-common.

Configuration

The configuration of the DHCP daemon is the same as if it was running from an installed package. The Service configuration is contained in the /etc/dhcp/dhcpd.conf file and the lease database is in /var/lib/dhcpd/dhcp.leases.

When the service is packaged, the package creates and populates these with default values. A container can’t affect the OS outside its boundaries so the user must create those files before the service is started.

  • /etc/dhcp/dhcpd.conf
    The daemon is configured with the standard configuration file in the standard location. Describing the configuration is outside the scope of this document. See the excellent existing configuration documents for the ISC DHCP daemon.

  • /var/lib/dhcpd/dhcp.leases
    The lease file must exist when the daemon starts. Initially it is an empty file.

    sudo mkdir -p /var/lib/dhcpd ; sudo touch /var/lib/dhcpd.leases

Enable Containerized Services

OS support for Container and VM management are provided by the containers-common package. On Debian based systems this is called golang-github-containers-common

On recent Red Hat and Fedora releases it is installed by default along with podman. On Debian based systems you must install the package or one that depends on it, like the podman package.

sudo apt update ; sudo apt install golang-github-containers-common

Install DHCP container service

Containerized services are defined by a container unit file. This file follows a similar format to the other systemd unit files.

The container service system was originally known as quadlets but now is defined in the podman systemd specification.

The container file below pulls one of the public container images for the ISC DHCP server.

dhcpd.container
[Unit]
Description=ISC DCHP daemon Service Container
After=network-online.target

[Container]
Image=quay.io/markllama/dhcpd:latest
#Image=quay.io/markllama/dhcpd-fedora:latest

# This shouldn't be needed except for reading /etc/dhcp/dhcpd.conf
# Could move it to /opt/dhcp/dhcpd.conf
PodmanArgs=--privileged
Network=host

# Open listening ports
# bootps
PublishPort=67:67/udp
PublishPort=67:67/tcp

# bootpc
PublishPort=68:68/udp
PublishPort=68:68/tcp

# Mount the dhcp config dir into the container workingdir
Volume=/etc/dhcp/dhcpd.conf:/etc/dhcp/dhcpd.conf:ro,Z
Volume=/var/lib/dhcpd/:/var/lib/dhcpd/:rw,Z

[Install]
# Enable in multi-user boot
WantedBy=multi-user.target default.target

#  podman run --detach --name dhcpd \
#    --privileged  \
#    --network host \
#    --volume /etc/dhcp/dhcpd.conf:/etc/dhcp/dhcpd.conf:ro,Z \
#    --volume /var/lib/dhcpd/:/var/lib/dhcpd/:rw,Z \
#    quay.io/markllama/dhcpd

The two container images indicated in the file above are both created from the Fedora RPM dhcp-server. The dhcpd-fedora container image is based on the Fedora 41 base image. The dhcpd image is a minimal image create from scratch, containing only the dhcpd binary and the required shared libraries.

Limitations and Future Work

This is a proof-of-concept project. The container design currently does not support any of these features of the ISC DHCP server

  • No configuration of invocation parameters

  • No IPv6

  • No OMAPI

  • No external database

References

  • DHCP History
    The history of DHCP and of the ISC DHCP server

  • ISC DHCP
    A DHCP service available on all Linux distributions

  • Podman systemd unit file
    The container service unit file specification

  • systemd
    The modern Linux OS initialization and service management system

  • The containers-common package
    The package that provides the container bindings for systemd services

    • Debian golang-github-containers-common

    • Fedora containers-common