Container images

Architecture overview of container images: layout, types, build system, CI consumption, and deployment

The CKI Project uses container images for services, cron jobs, and the kernel build/test pipeline.

Repositories

Repo Images Pattern Notes
containers ~35 (builders, buildah, dependency-tools, etc.) Converted (most) Includes live in working tree
cki-tools 11 converted, 6 legacy Mixed Legacy blocked on Helm CA (W4 Aspect C)
cki-lib 1 Converted
datawarehouse 2 Converted Inline pip install
kernel-qe-tools 1 Converted Inline pip install, heavy --no-emit-package
reporter 1 Converted
kpet 1 Converted
kwai 1 Converted Experimental repo
kernel-workflow 24 Unconverted Pending per-image conversion

The containers repository holds shared includes, builder images, and base infrastructure. Service repositories (cki-tools, datawarehouse, etc.) hold their own image definitions alongside the source code.

Image types

Service and cron-job images

Fedora-based images with RPM and Python dependencies, deployed to OpenShift via the :production tag. Examples: pipeline-herder, mr-auto-approver, datawarehouse.

CI/pipeline images

Consumed by SHA-locked digest in .gitlab-ci.yml (production@sha256:<digest>). The primary CI images are cki-tools (general CI jobs) and buildah (container builds).

Builder images

RHEL/CentOS Stream/ELN/rawhide-based, RPM-only images used for kernel compilation and testing in the containers repo. Support z-stream derivation via ci/rewrite-rpms-in-for-zstream.py.

Image layout

Each image lives under builds/<name>/ with a Containerfile.in preprocessed by cpp:

builds/<name>/
├── Containerfile.in
├── rpms/
│   ├── rpms.in.yaml
│   └── rpms.lock.yaml
└── requirements.lock      (if Python deps)

The Containerfile.in starts with a digest-pinned base image and the standard setup include:

/* renovate: datasource=docker depName=quay.io/fedora/fedora */
#define _BASE_IMAGE_TAG 44@sha256:abc123...
#include "setup"

The digest is inline in the tag (<ver>@sha256:<digest>) – this is the current standard. cki_validate_containerfiles.sh accepts either this format or a separate currentDigest=sha256:... in the Renovate comment for backward compatibility. Builder images can override _BASE_IMAGE (e.g., registry.access.redhat.com/ubi9).

The setup include

Beyond FROM + labels, setup includes 7 sub-snippets:

  • snippet-rhel-version – version detection macros like _AT_LEAST_RHEL()
  • snippet-envvarsREQUESTS_CA_BUNDLE, PYTHONUNBUFFERED, CKI_ARCH
  • snippet-cki-home
  • snippet-curlrc
  • snippet-nobest – disables best=True
  • snippet-krb5
  • snippet-dist-git-bastion

It configures the package manager (fastestmirror=true, install_weak_deps=false for RHEL 8+; yum fallback with skip_missing_names_on_install=False for RHEL 7 and earlier) and sets OpenShift arbitrary UID/GID compatibility (chmod g=u /etc/passwd /etc/group).

Includes and build system

Shared includes (setup, rpm-lockfile, python-requirements, cleanup, verify-python-lockfiles, rpm-lockfile-repos) are authored in the containers repo under includes/.

In the containers repo, cpp resolves includes from the working tree (-Iincludes). In all other repos, they come baked into the buildah image at /usr/local/share/cki/buildah/ (COPY "includes/"* /usr/local/share/cki/buildah).

The standard local build tool is cki_build_image.sh (from cki-lib, installed via pip into the buildah image and developer environments). It handles include resolution, directory layout detection, and image naming. Its cpp search order is: -Ibuilds -Iincludes -I/usr/local/share/cki/buildah.

For the containers repo, raw cpp -E -traditional -undef -Iincludes also works for quick preprocessing checks.

Python dependency installation

Two patterns are in use:

  1. Standard include: #define _PYTHON_REQUIREMENTS_LOCKFILE + #include "python-requirements". The include copies the full repo to /code, runs pip install --no-deps -r then pip install --no-deps -e . (editable install).

  2. Inline commands: pip install / uv pip install directly in the Containerfile – used by datawarehouse (uv pip install --system --no-cache --no-deps) and kernel-qe-tools (python3 -m pip install --no-cache-dir --no-deps) for more control over the install sequence.

Both patterns are valid; the include is simpler, inline is more flexible.

Dependency management

All container images use lockfiles for reproducible builds. See Lockfiles for the full lockfile architecture and Renovate for automated maintenance.

The dependency-tools image

Based on quay.io/konflux-ci/mintmaker-renovate-image (which provides Renovate and rpm-lockfile-prototype). On top, it adds:

  1. A uv wrapper at /usr/local/bin/uv (PATH-ordered ahead of real uv; transparently adds --override for two-pass VCS dep resolution and --upgrade for range constraint updates). See Renovate for wrapper details.

  2. COPY files/registries.conf.d/ /etc/containers/registries.conf.d/ – OCI cache mirror routing (pull-from-mirror = "digest-only") for three image families: quay.io/fedora/fedora, quay.io/fedora/eln, and quay.io/centos/centos. Without this config, digest-pinned base image pulls during manual builds and Renovate runs would not go through the dependency-archive OCI cache.

Used by both make targets for manual lockfile regeneration and by the Renovate CronJob for automated updates. See Lockfiles for caching details and Renovate for CronJob details.

CI builds

Each image is registered in the CI build matrix with IMAGE_NAME, CHANGES (paths that trigger rebuild), and SMOKE_TEST_COMMANDS. Path-based filtering via CHANGES/CHANGES_EXTRA determines which images are rebuilt on each pipeline run.

CI consumption

Repos pin CI image digests as production@sha256:<digest> in .gitlab-ci.yml or .gitlab/ci_templates/ci-images.yml. Some repos use the separate ci-images.yml file specifically for CODEOWNERS exclusion – this lets Renovate digest updates merge without requiring review from CI config owners.

Variables used:

  • cki_tools_image_tag: production@sha256:<digest>
  • buildah_image_tag: production@sha256:<digest>

Renovate auto-updates digests; mr-auto-approver auto-approves.

Deployment

Service images deploy to OpenShift via the :production tag on the default branch. CI images are consumed by digest (no :production tag needed).

Tags and environments

Tag Default branch Tag pipeline Merge request
p-123456 always always always
g-123456 on success on success on success
latest on success
tag on success
mr-123 on success

For production deployments, images are tagged with production. This runs automatically on the default branch.

Multi-arch builds

Set IMAGE_ARCH to build for a non-native architecture via qemu (resulting tags get an appended -arch). Set IMAGE_ARCHES to a stringified list of architectures to build and upload a multi-arch manifest.

Non-Fedora image variants

Some images use non-Fedora bases and don’t follow the standard pattern:

  • receiver (Lambda custom runtime, cki-tools)
  • dependency-archive (Lambda Python runtime, cki-tools)
  • s3-proxy / testing-farm-dashboard (alpine-based)
  • datawarehouse-nginx (nginx:stable-alpine)

These don’t use #include "setup" or lockfile includes.

Unconverted images

Six cki-tools images (amqp-bridge, cki-mr-viewer, cki-notifier, datawarehouse-submitter, kwaier, qe-pipeline-dashboard) still use the legacy pattern, blocked on Helm CA injection (W4 Aspect C).

kernel-workflow has 24 images still using the legacy pattern, pending per-image conversion.

Container image registries

quay.io

The cki organization on quay.io hosts public, internal, and mirror repositories. Access is controlled via three teams:

  • owners: admin access, CKI project members only
  • repocreators: allows new repository creation, contains the push_account robot account
  • readers: read-only access, external users and the pull_account robot

registry.gitlab.com

Container registries in the cki-project group on gitlab.com contain mirrors of public container image tags. A group deploy token with read_registry scope is used for pulls from PSI OpenShift.

Docker Hub

Due to pull rate limits, images consumed from Docker Hub are mirrored into quay.io/cki/mirror_* repositories (e.g., quay.io/cki/mirror_postgres:alpine for the postgres image used by datawarehouse).