diff --git a/.gitignore b/.gitignore index bbd7fb5..d4da1ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Python-generated files __pycache__/ *.py[oc] -build/ dist/ wheels/ *.egg-info diff --git a/build/golden-image/Containerfile b/build/golden-image/Containerfile index c99ab1f..a5e3da5 100644 --- a/build/golden-image/Containerfile +++ b/build/golden-image/Containerfile @@ -1,42 +1,19 @@ # Golden Container Disk Image for virtwork # Copyright 2026 Red Hat # SPDX-License-Identifier: Apache-2.0 +# +# Packages the osbuild-produced qcow2 as a KubeVirt containerdisk. +# The qcow2 is built by build.sh via image-builder before this +# Containerfile is consumed. -FROM quay.io/containerdisks/fedora:41 +FROM scratch LABEL io.k8s.display-name="virtwork-disk" \ - io.k8s.description="Fedora 41 container disk with virtwork workload tools pre-installed" \ + io.k8s.description="Fedora container disk with virtwork workload tools pre-installed" \ summary="Container disk for virtwork with CPU, memory, database, network, and disk tools" \ - description="Pre-built Fedora 41 container disk with stress-ng, fio, iperf3, postgresql-server, and chaos engineering tools for faster VM boot times" \ + description="Pre-built Fedora container disk with stress-ng, fio, iperf3, postgresql-server, and chaos engineering tools for faster VM boot times" \ name="virtwork-disk" \ version="1.0" \ - maintainer="OpenDev Community" + maintainer="Red Hat Ecosystem Engineering" -# Install all workload tools in a single layer to minimize image size -# Clean DNF cache to reduce final image size -RUN dnf install -y \ - stress-ng \ - fio \ - iperf3 \ - postgresql-server \ - iproute-tc \ - iptables-nft \ - kernel-modules-extra \ - netperf \ - python3 \ - && dnf clean all \ - && rm -rf /var/cache/dnf - -# Verify all critical binaries are present -RUN which stress-ng && \ - which fio && \ - which iperf3 && \ - which pgbench && \ - which tc && \ - which iptables-nft && \ - which netserver && \ - which python3 - -# Expose metadata for KubeVirt compatibility -# The containerdisk format expects the disk image to be at /disk/ -# This is already handled by the base fedora:41 image +COPY --chown=107:107 *.qcow2 /disk/disk.qcow2 diff --git a/build/golden-image/README.md b/build/golden-image/README.md index 604c0a9..2d1ed69 100644 --- a/build/golden-image/README.md +++ b/build/golden-image/README.md @@ -1,42 +1,59 @@ # Golden Container Disk Image -This directory contains the Containerfile and build scripts for the virtwork golden container disk image. +This directory produces a KubeVirt containerdisk OCI image with all +virtwork workload tools pre-installed, eliminating package installation +at VM boot time. -## Overview +## How It Works -The golden image is based on `quay.io/containerdisks/fedora:41` and includes all tools needed for virtwork workloads pre-installed. This eliminates package installation at VM boot time, reducing startup time by 2-5 minutes. +``` +blueprint.toml image-builder Containerfile + (packages) ──────► build qcow2 ──────► FROM scratch + (Fedora 41) COPY qcow2 → /disk/ + │ + containerdisk + OCI image +``` + +1. **`blueprint.toml`** — declarative package manifest consumed by osbuild. +2. **`image-builder build generic-qcow2`** — produces a Fedora qcow2 with all + blueprint packages baked in. +3. **`podman build`** — wraps the qcow2 in a minimal `FROM scratch` OCI + image at `/disk/disk.qcow2` (KubeVirt containerdisk format). + +## Prerequisites + +| Tool | Install | Purpose | +|------|---------|---------| +| `image-builder` | `sudo dnf install image-builder` | Builds the qcow2 from the blueprint | +| `podman` | `sudo dnf install podman` | Packages the qcow2 as an OCI image | -### Pre-installed Tools +`image-builder` requires root for loopback device access — `build.sh` +re-executes itself with `sudo` automatically if needed. + +## Pre-installed Tools - **stress-ng** — CPU and memory stress testing (cpu, memory workloads) - **fio** — Flexible I/O tester for disk benchmarking (disk workload) - **iperf3** — Network performance testing (network workload) - **netperf** — TCP_RR transaction performance (tps workload) -- **python3** — HTTP file server for the tps workload's application-layer transfers +- **python3** — HTTP file server for the tps workload - **postgresql-server** — PostgreSQL database with pgbench (database workload) -- **procps-ng** — `ps`, `pkill`, `kill` for chaos-process -- **iproute-tc** — Traffic control (`tc`) used by the chaos-network workload to inject latency and packet loss -- **kernel-modules-extra** — Provides the `sch_netem` kernel module required by chaos-network; pre-installing means the cloud-init `dnf install` completes instantly (package already present) instead of fetching over the network -- **iptables-nft** — Firewall rules; reserved for future network partition / blackhole scenarios - -Additional tools like `fallocate` and `dd` (used by chaos-disk) are already present in the base Fedora image. - -Workloads that need persistent storage (disk, database, chaos-disk) discover their data volume through `/dev/disk/by-id/virtio-` using the shared `diskSetupScript` helper — they do not depend on any tools beyond the standard userspace utilities already in the base image (`blkid`, `mkfs.xfs`, `mount`, `readlink`). +- **iproute-tc** — Traffic control for chaos-network latency/loss injection +- **kernel-modules-extra** — `sch_netem` kernel module for chaos-network +- **iptables-nft** — Firewall rules for future network partition scenarios +- **cloud-init** — VM first-boot configuration +- **qemu-guest-agent** — KubeVirt guest agent communication ## Building -### Prerequisites - -- Podman or Docker installed -- Network access to pull `quay.io/containerdisks/fedora:41` - ### Build Locally ```bash ./build.sh ``` -This builds the image as `quay.io/opdev/virtwork-disk:latest` (local copy, not pushed to registry). +Produces `quay.io/opdev/virtwork-disk:latest` (local only, not pushed). ### Build and Push @@ -44,20 +61,6 @@ This builds the image as `quay.io/opdev/virtwork-disk:latest` (local copy, not p PUSH=true ./build.sh ``` -This builds and pushes to `quay.io/opdev/virtwork-disk:latest`. Requires authentication to the registry. - -### Build with Custom Registry - -```bash -REGISTRY=my.registry.io ./build.sh -``` - -### Build with Custom Tag - -```bash -TAG=1.0.0 ./build.sh -``` - ### Environment Variables | Variable | Description | Default | @@ -65,88 +68,55 @@ TAG=1.0.0 ./build.sh | `REGISTRY` | Container registry hostname | `quay.io/opdev` | | `IMAGE_NAME` | Image name | `virtwork-disk` | | `TAG` | Image tag | `latest` | +| `DISTRO` | Fedora version for image-builder | `fedora-42` | +| `IMAGE_TYPE` | osbuild image type | `generic-qcow2` | | `PUSH` | Push to registry after building | `false` | -## Testing +## Adding Packages -After building, verify tools are present: +Edit `blueprint.toml` and add an entry to the `packages` list: -```bash -podman run --rm quay.io/opdev/virtwork-disk:latest /bin/bash -c "stress-ng --version" -podman run --rm quay.io/opdev/virtwork-disk:latest /bin/bash -c "fio --version" -podman run --rm quay.io/opdev/virtwork-disk:latest /bin/bash -c "iperf3 --version" -podman run --rm quay.io/opdev/virtwork-disk:latest /bin/bash -c "psql --version" +```toml +packages = [ + # ... existing packages ... + { name = "your-new-package" }, +] ``` +Then rebuild with `./build.sh`. osbuild validates that packages exist in +the Fedora repos at build time — a typo or missing package fails the +build immediately. + ## Using the Golden Image The golden image is **optional**. To use it with virtwork: -### CLI Flag - ```bash +# CLI flag virtwork run --container-disk-image quay.io/opdev/virtwork-disk:latest -``` -### Environment Variable - -```bash +# Environment variable export VIRTWORK_CONTAINER_DISK_IMAGE=quay.io/opdev/virtwork-disk:latest virtwork run -``` - -### Config File -```yaml -# config.yaml +# Config file (config.yaml) container_disk_image: quay.io/opdev/virtwork-disk:latest ``` -```bash -virtwork run --config config.yaml -``` - -## Image Size - -The golden image adds approximately **34MB** to the base Fedora 41 container disk image: - -- Base Fedora 41 container disk: ~600MB -- Golden image: ~634MB - -## Design Decisions - -### Why Keep Package Installation in Cloud-Init? - -DNF is **idempotent** — when it tries to install a package that's already present, it completes instantly with "Package X is already installed" messages. - -This means: -- **Golden image users**: Get instant package "installation" (already there) — saves 2-5 minutes -- **Default Fedora users**: Get normal package installation at boot -- **Same workload code**: Works for both images without conditional logic - -### Why Not Remove Packages from Cloud-Init? - -Removing package installation from workload code would require: -- Changes to all 5 workload implementations -- Changes to all workload tests -- Conditional logic to detect which image is in use -- Maintaining two code paths - -The current approach is simpler and maintains backward compatibility. - -## Future Enhancements +## Troubleshooting -1. **Multi-architecture support**: Build for arm64 in addition to amd64 -2. **Automated builds**: CI workflow on schedule -3. **Image scanning**: Add Trivy security scanning -4. **Semantic versioning**: Pin specific package versions for reproducibility -5. **Image variants**: Create minimal/full variants for different use cases +| Problem | Fix | +|---------|-----| +| `image-builder: command not found` | `sudo dnf install image-builder` | +| Permission errors during build | Script auto-escalates to root; ensure `sudo` works | +| Package not found in blueprint | Check the package name exists in `dnf search ` for the target Fedora version | +| Build runs out of disk space | qcow2 assembly needs ~4 GB in `/tmp`; set `TMPDIR` to a larger partition if needed | ## Related Docs -- [docs/chaos-workloads.md](../../docs/chaos-workloads.md) — operator guide for the chaos workloads that use `iproute-tc`, `procps-ng`, and `fallocate` -- [docs/deployment.md](../../docs/deployment.md) — how to set the golden image as the default container disk in your deployment -- [docs/configuration.md](../../docs/configuration.md) — `--container-disk-image` flag, `VIRTWORK_CONTAINER_DISK_IMAGE` env var, and `container_disk_image` YAML key +- [docs/chaos-workloads.md](../../docs/chaos-workloads.md) — chaos workloads using `iproute-tc`, `procps-ng`, and `fallocate` +- [docs/deployment.md](../../docs/deployment.md) — setting the golden image as default container disk +- [docs/configuration.md](../../docs/configuration.md) — `--container-disk-image` flag, env var, and YAML key ## License diff --git a/build/golden-image/blueprint.toml b/build/golden-image/blueprint.toml new file mode 100644 index 0000000..fb9cd11 --- /dev/null +++ b/build/golden-image/blueprint.toml @@ -0,0 +1,20 @@ +# virtwork golden disk image blueprint for osbuild / image-builder +# Copyright 2026 Red Hat +# SPDX-License-Identifier: Apache-2.0 +# +# Add workload packages here. Rebuild with ./build.sh to produce an +# updated containerdisk OCI image. + +packages = [ + { name = "cloud-init" }, + { name = "qemu-guest-agent" }, + { name = "stress-ng" }, + { name = "fio" }, + { name = "iperf3" }, + { name = "postgresql-server" }, + { name = "iproute-tc" }, + { name = "iptables-nft" }, + { name = "kernel-modules-extra" }, + { name = "netperf" }, + { name = "python3" }, +] diff --git a/build/golden-image/build.sh b/build/golden-image/build.sh index 5b72ddf..9043b42 100755 --- a/build/golden-image/build.sh +++ b/build/golden-image/build.sh @@ -2,6 +2,14 @@ # Build script for virtwork golden container disk image # Copyright 2026 Red Hat # SPDX-License-Identifier: Apache-2.0 +# +# Two-stage build: +# 1. image-builder produces a Fedora qcow2 with blueprint packages +# 2. podman packages the qcow2 as a KubeVirt containerdisk OCI image +# +# Prerequisites: +# - image-builder (dnf install image-builder) +# - podman set -euo pipefail @@ -9,40 +17,85 @@ REGISTRY="${REGISTRY:-quay.io/opdev}" IMAGE_NAME="${IMAGE_NAME:-virtwork-disk}" TAG="${TAG:-latest}" FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}" +DISTRO="${DISTRO:-fedora-42}" +IMAGE_TYPE="${IMAGE_TYPE:-generic-qcow2}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -echo "Building golden container disk image: ${FULL_IMAGE}" +# --- prerequisite checks --------------------------------------------------- + +for cmd in image-builder podman; do + if ! command -v "$cmd" &>/dev/null; then + echo "ERROR: $cmd is required but not found on PATH" >&2 + echo " Install with: sudo dnf install $cmd" >&2 + exit 1 + fi +done + +# image-builder may need root for loopback device access +if [[ $(id -u) -ne 0 ]]; then + echo "image-builder requires root privileges for disk image assembly." + echo "Re-executing with sudo..." + exec sudo \ + REGISTRY="$REGISTRY" \ + IMAGE_NAME="$IMAGE_NAME" \ + TAG="$TAG" \ + DISTRO="$DISTRO" \ + IMAGE_TYPE="$IMAGE_TYPE" \ + PUSH="${PUSH:-false}" \ + "$0" "$@" +fi + +# --- stage 1: build qcow2 with image-builder -------------------------------- + +TMPDIR="$(mktemp -d)" +trap 'rm -rf "$TMPDIR"' EXIT + +echo "Building golden disk image: ${FULL_IMAGE}" echo "======================================================" +echo "Distro: ${DISTRO}" +echo "Type: ${IMAGE_TYPE}" +echo "Blueprint: ${SCRIPT_DIR}/blueprint.toml" +echo "Output: ${TMPDIR}" +echo "" + +image-builder build "$IMAGE_TYPE" \ + --distro "$DISTRO" \ + --blueprint "${SCRIPT_DIR}/blueprint.toml" \ + --output-dir "$TMPDIR" + +QCOW2="$(find "$TMPDIR" -name '*.qcow2' -print -quit)" +if [[ -z "$QCOW2" ]]; then + echo "ERROR: image-builder produced no qcow2 file" >&2 + exit 1 +fi + +echo "" +echo "qcow2 produced: $(basename "$QCOW2") ($(du -h "$QCOW2" | cut -f1))" + +# --- stage 2: package as containerdisk OCI image ---------------------------- + +echo "" +echo "Packaging as containerdisk..." + +cp "$QCOW2" "${TMPDIR}/disk.qcow2" -# Build the image podman build \ --tag "${FULL_IMAGE}" \ - --file Containerfile \ - . + --file "${SCRIPT_DIR}/Containerfile" \ + "$TMPDIR" echo "" echo "Image built successfully: ${FULL_IMAGE}" -echo "" -# Optional: push to registry +# --- optional push ----------------------------------------------------------- + if [[ "${PUSH:-false}" == "true" ]]; then + echo "" echo "Pushing image to registry..." podman push "${FULL_IMAGE}" echo "Image pushed successfully" - echo "" fi -# Verify the image contains expected tools -echo "Verifying installed tools..." -echo "==============================" -podman run --rm "${FULL_IMAGE}" /bin/bash -c " - which stress-ng && echo '✓ stress-ng found' || echo '✗ stress-ng MISSING' && - which fio && echo '✓ fio found' || echo '✗ fio MISSING' && - which iperf3 && echo '✓ iperf3 found' || echo '✗ iperf3 MISSING' && - which pgbench && echo '✓ pgbench found' || echo '✗ pgbench MISSING' && - which tc && echo '✓ tc found' || echo '✗ tc MISSING' && - which iptables-nft && echo '✓ iptables-nft found' || echo '✗ iptables-nft MISSING' -" - echo "" echo "======================================================" echo "Build complete!"