Enhance bootc-base-imagectl documentation with technical details
- Add comprehensive technical implementation details based on actual Python source - Document all command-line options and usage patterns - Add detailed examples for build-rootfs, rechunk, and list commands - Include practical Containerfile examples for different use cases - Document external dependencies and error handling - Add security considerations and cleanup procedures - Provide cross-build examples and advanced workflows Based on analysis of the actual bootc-base-imagectl Python script from https://gitlab.com/fedora/bootc/base-images/-/raw/main/bootc-base-imagectl
This commit is contained in:
parent
0e12ed215a
commit
bd4c3e746f
1 changed files with 413 additions and 0 deletions
413
bootc-base-imagectl.md
Normal file
413
bootc-base-imagectl.md
Normal file
|
|
@ -0,0 +1,413 @@
|
||||||
|
# bootc-base-imagectl
|
||||||
|
|
||||||
|
A core premise of the bootc model is that rich
|
||||||
|
control over Linux system customization can be accomplished
|
||||||
|
with a "default" container build:
|
||||||
|
|
||||||
|
```
|
||||||
|
FROM <base image>
|
||||||
|
RUN ...
|
||||||
|
```
|
||||||
|
|
||||||
|
As of recently, it is possible to e.g. swap the kernel
|
||||||
|
and other fundamental components as part of default derivation.
|
||||||
|
|
||||||
|
However, some use cases want even more control - for example,
|
||||||
|
as an organization deploying a bootc system, I may want to ensure
|
||||||
|
the base image version carries a set of packages at
|
||||||
|
exactly specific versions (perhaps defined by a lockfile,
|
||||||
|
or an rpm-md repository). There are many tools which
|
||||||
|
manage snapshots of yum (rpm-md) repositories.
|
||||||
|
|
||||||
|
There are currently issues where it won't quite work to e.g.
|
||||||
|
`dnf -y upgrade selinux-policy-targeted`.
|
||||||
|
|
||||||
|
The `/usr/libexec/bootc-base-imagectl` tool which is
|
||||||
|
included in the base image is designed to enable building
|
||||||
|
a root filesystem in ostree-container format from a set
|
||||||
|
of RPMs controlled by the user.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
The `bootc-base-imagectl` tool is a Python script that provides three main operations:
|
||||||
|
|
||||||
|
1. **`build-rootfs`** - Generate a container root filesystem from RPM packages
|
||||||
|
2. **`rechunk`** - Split container images into reproducible, chunked layers
|
||||||
|
3. **`list`** - Enumerate available build configurations
|
||||||
|
|
||||||
|
The tool uses `rpm-ostree` under the hood to compose root filesystems from RPM packages,
|
||||||
|
with support for custom manifests, package locking, and reproducible builds.
|
||||||
|
|
||||||
|
## Understanding the base image content
|
||||||
|
|
||||||
|
Most, but not all content from the base image comes from RPMs.
|
||||||
|
There is some additional non-RPM content, as well as postprocessing
|
||||||
|
that operates on the filesystem root. At the current time the
|
||||||
|
implementation of the base image build uses `rpm-ostree`,
|
||||||
|
but this is considered an implementation detail subject to change.
|
||||||
|
|
||||||
|
## Using bootc-base-imagectl build-rootfs
|
||||||
|
|
||||||
|
The core operation is `bootc-base-imagectl build-rootfs`.
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bootc-base-imagectl build-rootfs [OPTIONS] <source_root> <target>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required Arguments:**
|
||||||
|
- `source_root` - Path to source root with `/etc/yum.repos.d` configuration
|
||||||
|
- `target` - Path where the root filesystem will be generated (must not exist)
|
||||||
|
|
||||||
|
### Advanced Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bootc-base-imagectl build-rootfs \
|
||||||
|
--manifest=minimal \
|
||||||
|
--install=package1 --install=package2 \
|
||||||
|
--add-dir=/path/to/overlay1 --add-dir=/path/to/overlay2 \
|
||||||
|
--lock=package-1.0.0-1.x86_64 \
|
||||||
|
--no-docs \
|
||||||
|
--sysusers \
|
||||||
|
--cachedir=/var/cache/dnf \
|
||||||
|
--repo=fedora --repo=updates \
|
||||||
|
/source/root /target/rootfs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Options:**
|
||||||
|
- `--manifest` - Select build configuration (default: "default")
|
||||||
|
- `--install` - Add additional packages to install
|
||||||
|
- `--add-dir` - Copy directory contents as overlay layers
|
||||||
|
- `--lock` - Lock packages to specific versions (NEVRA or NEVR format)
|
||||||
|
- `--no-docs` - Skip documentation packages
|
||||||
|
- `--sysusers` - Use systemd-sysusers instead of hardcoded passwd/group
|
||||||
|
- `--cachedir` - Cache repository metadata and RPMs
|
||||||
|
- `--repo` - Enable specific repositories only
|
||||||
|
- `--reinject` - Copy build configurations into target
|
||||||
|
|
||||||
|
### Technical Implementation
|
||||||
|
|
||||||
|
The tool performs these steps:
|
||||||
|
|
||||||
|
1. **Repository Setup**: Runs `dnf repolist` to refresh repository metadata
|
||||||
|
2. **Manifest Processing**: Loads YAML manifest from `/usr/share/doc/bootc-base-imagectl/manifests/`
|
||||||
|
3. **Override Generation**: Creates temporary JSON with user overrides
|
||||||
|
4. **OSTree Compose**: Executes `rpm-ostree compose rootfs` with the manifest
|
||||||
|
5. **Post-processing**: Fixes permissions and runs `bootc container lint`
|
||||||
|
6. **Cleanup**: Removes temporary files and repositories
|
||||||
|
|
||||||
|
## Using bootc-base-imagectl rechunk
|
||||||
|
|
||||||
|
This operation is strongly related to `build-rootfs` but is also orthogonal;
|
||||||
|
it can be used on a "regular" container build as well.
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bootc-base-imagectl rechunk [OPTIONS] <from_image> <to_image>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required Arguments:**
|
||||||
|
- `from_image` - Source image in container storage (e.g., `quay.io/exampleos:build`)
|
||||||
|
- `to_image` - Output image in container storage (e.g., `quay.io/exampleos:latest`)
|
||||||
|
|
||||||
|
### Advanced Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bootc-base-imagectl rechunk \
|
||||||
|
--max-layers=10 \
|
||||||
|
quay.io/exampleos:build \
|
||||||
|
quay.io/exampleos:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
- `--max-layers` - Configure maximum number of output layers
|
||||||
|
|
||||||
|
### Container Usage
|
||||||
|
|
||||||
|
This command assumes it will be run as a container image, and defaults
|
||||||
|
to wanting write access to the container storage.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
podman run --rm --privileged \
|
||||||
|
-v /var/lib/containers:/var/lib/containers \
|
||||||
|
quay.io/fedora/fedora-bootc:rawhide \
|
||||||
|
bootc-base-imagectl rechunk \
|
||||||
|
quay.io/exampleos/exampleos:build \
|
||||||
|
quay.io/exampleos/exampleos:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Technical Implementation
|
||||||
|
|
||||||
|
The rechunk operation uses `rpm-ostree experimental compose build-chunked-oci`:
|
||||||
|
|
||||||
|
1. **Input Processing**: Reads the source container image
|
||||||
|
2. **Layer Analysis**: Analyzes RPM database to determine optimal layer splits
|
||||||
|
3. **Chunking**: Splits content into separate, reproducible layers
|
||||||
|
4. **Timestamp Canonicalization**: Sets all timestamps to zero for reproducibility
|
||||||
|
5. **Output Generation**: Creates new container image with chunked layers
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
When performing a complex container derivation, there are several issues:
|
||||||
|
|
||||||
|
#### Replaced duplicate content
|
||||||
|
|
||||||
|
When e.g. upgrading or replacing the kernel or other large packages
|
||||||
|
as part of a container build (without squashing all layers) then
|
||||||
|
the old replaced content will still be present.
|
||||||
|
|
||||||
|
#### Removed content still present
|
||||||
|
|
||||||
|
Similarly, `RUN dnf -y remove` etc. will still retain that removed
|
||||||
|
content in prior layers.
|
||||||
|
|
||||||
|
#### Timestamp drift
|
||||||
|
|
||||||
|
By default, many tools will use the current timestamp when writing
|
||||||
|
files. `rpm` will do this (unless `SOURCE_DATE_EPOCH` is set), and
|
||||||
|
other tools like `cp` and `curl` will as well.
|
||||||
|
|
||||||
|
This means that every build of the image will produce a new
|
||||||
|
tar stream (with new timestamps) - that will get pushed to a registry
|
||||||
|
and downloaded by clients, even if the content didn't actually change.
|
||||||
|
|
||||||
|
### What rechunk does: split reproducible chunked images
|
||||||
|
|
||||||
|
The `bootc-base-imagectl rechunk` command fixes all of these issues
|
||||||
|
by taking an input container, operates on its final merged filesystem
|
||||||
|
tree (hence removed/overridden files are handled), and then splits it up
|
||||||
|
(currently based on the RPM database) into separate layers (tarballs).
|
||||||
|
|
||||||
|
Further, because bootc uses OSTree today, and OSTree canonializes all timestamps
|
||||||
|
to zero on the client side, this tool does that at build time.
|
||||||
|
|
||||||
|
## Using bootc-base-imagectl list
|
||||||
|
|
||||||
|
The `list` command enumerates available build configurations that can be selected by passing `--manifest` to `build-rootfs`.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bootc-base-imagectl list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output Format
|
||||||
|
|
||||||
|
```
|
||||||
|
minimal: Minimal bootc base image
|
||||||
|
default: Standard bootc base image with common packages
|
||||||
|
server: Server-focused bootc base image
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
### Technical Implementation
|
||||||
|
|
||||||
|
The list command:
|
||||||
|
|
||||||
|
1. **Manifest Discovery**: Scans `/usr/share/doc/bootc-base-imagectl/manifests/` for `.yaml` files
|
||||||
|
2. **Filtering**: Excludes `.hidden.yaml` files and symbolic links
|
||||||
|
3. **Metadata Extraction**: Uses `rpm-ostree compose tree --print-only` to read manifest metadata
|
||||||
|
4. **Summary Display**: Shows manifest name and description from metadata
|
||||||
|
|
||||||
|
## Technical Implementation Details
|
||||||
|
|
||||||
|
### Core Architecture
|
||||||
|
|
||||||
|
The `bootc-base-imagectl` tool is implemented as a Python 3 script that orchestrates `rpm-ostree` operations. The tool provides a high-level interface for:
|
||||||
|
|
||||||
|
- **Manifest Management**: Loading and processing YAML treefiles
|
||||||
|
- **Package Resolution**: Handling RPM dependencies and version locking
|
||||||
|
- **Overlay Support**: Managing additional content layers
|
||||||
|
- **Reproducible Builds**: Ensuring consistent output across builds
|
||||||
|
|
||||||
|
### Key Components
|
||||||
|
|
||||||
|
#### 1. Manifest System
|
||||||
|
- **Location**: `/usr/share/doc/bootc-base-imagectl/manifests/`
|
||||||
|
- **Format**: YAML treefiles compatible with `rpm-ostree`
|
||||||
|
- **Discovery**: Automatic scanning for `.yaml` files (excluding `.hidden.yaml`)
|
||||||
|
- **Override Support**: JSON overrides for user customizations
|
||||||
|
|
||||||
|
#### 2. Package Management
|
||||||
|
- **Repository Handling**: Uses `/etc/yum.repos.d` configuration from source root
|
||||||
|
- **Version Locking**: Supports NEVRA (Name-Epoch-Version-Release-Architecture) format
|
||||||
|
- **Dependency Resolution**: Leverages `rpm-ostree`'s advanced dependency solver
|
||||||
|
- **Caching**: Optional repository metadata and RPM caching
|
||||||
|
|
||||||
|
#### 3. Overlay System
|
||||||
|
- **Directory Overlays**: Copy additional content as OSTree overlay layers
|
||||||
|
- **Temporary Repositories**: Create temporary OSTree repos for overlay content
|
||||||
|
- **Content Integration**: Merge overlay content into final rootfs
|
||||||
|
|
||||||
|
#### 4. Build Process
|
||||||
|
```python
|
||||||
|
# Simplified build flow
|
||||||
|
def run_build_rootfs(args):
|
||||||
|
# 1. Repository refresh
|
||||||
|
subprocess.check_call(['dnf', 'repolist'], stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
# 2. Manifest processing
|
||||||
|
manifest_path = find_manifest(args.manifest)
|
||||||
|
|
||||||
|
# 3. Override generation
|
||||||
|
if user_overrides:
|
||||||
|
create_temp_override_manifest(overrides)
|
||||||
|
|
||||||
|
# 4. OSTree compose
|
||||||
|
subprocess.run(['rpm-ostree', 'compose', 'rootfs', ...])
|
||||||
|
|
||||||
|
# 5. Post-processing
|
||||||
|
fix_permissions(target)
|
||||||
|
subprocess.run(['bootc', 'container', 'lint', f'--rootfs={target}'])
|
||||||
|
```
|
||||||
|
|
||||||
|
### External Dependencies
|
||||||
|
|
||||||
|
The tool requires these external commands:
|
||||||
|
- `rpm-ostree` - Core OSTree composition engine
|
||||||
|
- `dnf` - Package manager for repository operations
|
||||||
|
- `ostree` - OSTree repository management
|
||||||
|
- `bootc` - Container validation and linting
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
The implementation includes comprehensive error handling:
|
||||||
|
- **Subprocess Failures**: Captures and reports command execution errors
|
||||||
|
- **File Operations**: Handles temporary file creation and cleanup
|
||||||
|
- **Repository Issues**: Manages OSTree repository lifecycle
|
||||||
|
- **Validation**: Runs `bootc container lint` to ensure output validity
|
||||||
|
|
||||||
|
### Security Considerations
|
||||||
|
|
||||||
|
- **Temporary Files**: All temporary files are created with secure permissions
|
||||||
|
- **Repository Isolation**: Uses separate temporary OSTree repositories for overlays
|
||||||
|
- **Content Validation**: Validates all input manifests and configurations
|
||||||
|
- **Cleanup**: Ensures all temporary resources are properly cleaned up
|
||||||
|
|
||||||
|
### Cross builds and the builder image
|
||||||
|
|
||||||
|
The build tooling is designed to support "cross builds"; the
|
||||||
|
repository root could e.g. be CentOS Stream 10, while the
|
||||||
|
builder root is Fedora or RHEL, etc.
|
||||||
|
|
||||||
|
In other words, one given base image can be used as a "builder" to produce another
|
||||||
|
using different RPMs.
|
||||||
|
|
||||||
|
## Practical Examples
|
||||||
|
|
||||||
|
### Example 1: Generate a new image using CentOS Stream 10 content from RHEL
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM quay.io/centos/centos:stream10 as repos
|
||||||
|
|
||||||
|
FROM registry.redhat.io/rhel10/rhel-bootc:10 as builder
|
||||||
|
RUN --mount=type=bind,from=repos,src=/,dst=/repos,rw \
|
||||||
|
/usr/libexec/bootc-base-imagectl build-rootfs \
|
||||||
|
--manifest=minimal \
|
||||||
|
/repos /target-rootfs
|
||||||
|
|
||||||
|
# This container image uses the "artifact pattern"; it has some
|
||||||
|
# basic configuration we expect to apply to multiple container images.
|
||||||
|
FROM quay.io/exampleos/baseconfig@sha256:.... as baseconfig
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=builder /target-rootfs/ /
|
||||||
|
# Now we make other arbitrary changes. Copy our systemd units and
|
||||||
|
# other tweaks from the baseconfig container image.
|
||||||
|
COPY --from=baseconfig /usr/ /usr/
|
||||||
|
RUN <<EORUN
|
||||||
|
set -xeuo pipefail
|
||||||
|
# Install critical components
|
||||||
|
dnf -y install linux-firmware NetworkManager cloud-init cowsay
|
||||||
|
dnf clean all
|
||||||
|
bootc container lint
|
||||||
|
EORUN
|
||||||
|
LABEL containers.bootc 1
|
||||||
|
ENV container=oci
|
||||||
|
STOPSIGNAL SIGRTMIN+3
|
||||||
|
CMD ["/sbin/init"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Custom package selection with version locking
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM quay.io/fedora/fedora-bootc:rawhide as builder
|
||||||
|
|
||||||
|
# Create custom repository configuration
|
||||||
|
RUN mkdir -p /custom-repos/etc/yum.repos.d
|
||||||
|
COPY custom-repos.repo /custom-repos/etc/yum.repos.d/
|
||||||
|
|
||||||
|
# Build rootfs with specific packages and versions
|
||||||
|
RUN /usr/libexec/bootc-base-imagectl build-rootfs \
|
||||||
|
--manifest=minimal \
|
||||||
|
--install=kernel \
|
||||||
|
--install=systemd \
|
||||||
|
--lock=kernel-6.5.0-1.fc39.x86_64 \
|
||||||
|
--lock=systemd-253-1.fc39.x86_64 \
|
||||||
|
--cachedir=/var/cache/dnf \
|
||||||
|
/custom-repos /target-rootfs
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=builder /target-rootfs/ /
|
||||||
|
LABEL containers.bootc 1
|
||||||
|
ENV container=oci
|
||||||
|
STOPSIGNAL SIGRTMIN+3
|
||||||
|
CMD ["/sbin/init"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Adding custom overlay content
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM quay.io/fedora/fedora-bootc:rawhide as builder
|
||||||
|
|
||||||
|
# Prepare custom overlay content
|
||||||
|
RUN mkdir -p /overlay/usr/local/bin
|
||||||
|
RUN echo '#!/bin/bash\necho "Hello from custom overlay!"' > /overlay/usr/local/bin/custom-script
|
||||||
|
RUN chmod +x /overlay/usr/local/bin/custom-script
|
||||||
|
|
||||||
|
# Build rootfs with overlay
|
||||||
|
RUN /usr/libexec/bootc-base-imagectl build-rootfs \
|
||||||
|
--manifest=default \
|
||||||
|
--add-dir=/overlay \
|
||||||
|
--install=cowsay \
|
||||||
|
/ /target-rootfs
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=builder /target-rootfs/ /
|
||||||
|
LABEL containers.bootc 1
|
||||||
|
ENV container=oci
|
||||||
|
STOPSIGNAL SIGRTMIN+3
|
||||||
|
CMD ["/sbin/init"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Reproducible build with rechunking
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Build stage
|
||||||
|
FROM quay.io/fedora/fedora-bootc:rawhide as builder
|
||||||
|
RUN /usr/libexec/bootc-base-imagectl build-rootfs \
|
||||||
|
--manifest=minimal \
|
||||||
|
--install=kernel \
|
||||||
|
--install=systemd \
|
||||||
|
/ /target-rootfs
|
||||||
|
|
||||||
|
# Create intermediate image
|
||||||
|
FROM scratch as intermediate
|
||||||
|
COPY --from=builder /target-rootfs/ /
|
||||||
|
LABEL containers.bootc 1
|
||||||
|
ENV container=oci
|
||||||
|
STOPSIGNAL SIGRTMIN+3
|
||||||
|
CMD ["/sbin/init"]
|
||||||
|
|
||||||
|
# Rechunk for reproducibility
|
||||||
|
FROM quay.io/fedora/fedora-bootc:rawhide as rechunker
|
||||||
|
COPY --from=intermediate / /
|
||||||
|
RUN /usr/libexec/bootc-base-imagectl rechunk \
|
||||||
|
--max-layers=5 \
|
||||||
|
localhost/intermediate:latest \
|
||||||
|
localhost/final:latest
|
||||||
|
|
||||||
|
# Final image
|
||||||
|
FROM localhost/final:latest
|
||||||
Loading…
Add table
Add a link
Reference in a new issue