19 KiB
deb-bootc-compose: Debian Bootc Composition Tool
Executive Summary
deb-bootc-compose is a Debian-native composition tool designed to orchestrate the creation of Debian bootc images from packages. It's our answer to Fedora's Pungi compose system, adapted for Debian's package management ecosystem and build infrastructure.
What deb-bootc-compose is Based On
Fedora's Pungi: The Inspiration
deb-bootc-compose is directly inspired by and modeled after Fedora's Pungi compose system. Pungi is the sophisticated orchestration tool that coordinates Fedora's entire release process, including bootc image creation.
Pungi's Core Functions:
- Package Set Coordination: Ensures all release artifacts use identical package versions
- Multi-Artifact Generation: Creates ISOs, live images, container images, and cloud images
- Build Orchestration: Manages the interaction between Koji (build system) and rpm-ostree
- Dependency Management: Coordinates package dependencies across different output formats
- Release Consistency: Guarantees reproducible builds across architectures and variants
Why Pungi Works So Well:
- Declarative Configuration: Uses YAML-based compose definitions
- Isolated Builds: Each compose runs in its own environment
- Audit Trail: Complete logging of every build step
- Multi-Architecture Support: Builds for x86_64, aarch64, ppc64le, s390x
- Integration: Deep integration with Fedora's Koji build system
Adapting Pungi's Success for Debian
While Pungi is designed for Fedora's RPM-based ecosystem, deb-bootc-compose adapts the same architectural principles for Debian's DEB-based world:
| Aspect | Pungi (Fedora) | deb-bootc-compose (Debian) |
|---|---|---|
| Package Format | RPM packages | DEB packages |
| Build System | Koji + Mock | sbuild + schroot |
| Package Manager | dnf/yum | apt/dpkg |
| OSTree Tool | rpm-ostree | apt-ostree |
| Configuration | YAML compose files | YAML compose files |
| Repository | RPM repositories | Debian package pools |
Technical Architecture
Core Components
deb-bootc-compose consists of several interconnected components that work together to orchestrate the build process:
1. Compose Engine
The central orchestrator that manages the entire compose process.
type ComposeEngine struct {
Config *ComposeConfig
BuildSystem BuildSystem
OSTreeTool OSTreeTool
Output OutputManager
Logger Logger
}
func (e *ComposeEngine) Compose() error {
// 1. Parse and validate treefile
// 2. Resolve package dependencies
// 3. Coordinate build system
// 4. Create OSTree commit
// 5. Generate output artifacts
}
2. Treefile Parser
Parses and validates Debian treefile manifests (our equivalent to Pungi's compose definitions).
type Treefile struct {
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Packages PackageSet `json:"packages"`
Exclude []string `json:"exclude"`
Repositories []string `json:"repositories"`
Architecture []string `json:"architecture"`
Variants []Variant `json:"variants"`
}
type PackageSet struct {
Required []string `json:"required"`
Optional []string `json:"optional"`
Recommended []string `json:"recommended"`
}
3. Build System Interface
Abstracts the build system (sbuild) to allow for different build backends.
type BuildSystem interface {
Build(treefile *Treefile, arch string) (*BuildResult, error)
InstallDependencies(packages []string, arch string) error
CreateBuildEnvironment(arch string) (*BuildEnvironment, error)
Cleanup(env *BuildEnvironment) error
}
type SbuildSystem struct {
Dist string
Arch string
BuildDepResolver string
BuildEnv string
}
4. OSTree Tool Interface
Abstracts the OSTree tool (apt-ostree) for creating commits and containers.
type OSTreeTool interface {
CreateCommit(packages []string, outputPath string) (*OSTreeCommit, error)
ContainerEncapsulate(commit *OSTreeCommit, outputPath string) error
BuildChunkedOCI(commit *OSTreeCommit, outputPath string) error
ValidateCommit(commit *OSTreeCommit) error
}
5. Output Manager
Handles the generation of different output formats (containers, disk images, etc.).
type OutputManager struct {
Formats []OutputFormat
Registry RegistryConfig
Signing SigningConfig
}
type OutputFormat interface {
Generate(commit *OSTreeCommit, config OutputConfig) error
Validate(output OutputConfig) error
}
How deb-bootc-compose Works
1. Compose Initialization
# Start a compose
deb-bootc-compose --release bookworm \
--variant debian-bootc-minimal \
--arch amd64 \
--treefile debian-bootc-minimal.json \
--output /tmp/compose-output
What happens internally:
- Parse treefile: Load and validate the JSON manifest
- Resolve dependencies: Use apt to resolve package dependencies
- Create build plan: Generate a build order based on dependencies
- Initialize build environment: Set up sbuild chroots for each architecture
2. Package Resolution and Download
func (c *ComposeEngine) resolvePackages(treefile *Treefile) (*PackageResolution, error) {
resolution := &PackageResolution{}
// Resolve required packages
for _, pkg := range treefile.Packages.Required {
deps, err := c.aptResolver.Resolve(pkg)
if err != nil {
return nil, fmt.Errorf("failed to resolve %s: %w", pkg, err)
}
resolution.Required[pkg] = deps
}
// Resolve optional packages
for _, pkg := range treefile.Packages.Optional {
deps, err := c.aptResolver.Resolve(pkg)
if err != nil {
log.Warnf("Optional package %s failed to resolve: %v", pkg, err)
continue
}
resolution.Optional[pkg] = deps
}
return resolution, nil
}
Dependency resolution process:
- Parse package lists: Extract required, optional, and recommended packages
- Query package database: Use apt-cache to get dependency information
- Build dependency graph: Create a directed graph of package dependencies
- Resolve conflicts: Handle package conflicts and alternatives
- Generate build order: Determine the order packages should be built
3. Build Environment Creation
func (s *SbuildSystem) CreateBuildEnvironment(arch string) (*BuildEnvironment, error) {
// Create schroot configuration
schrootConfig := &SchrootConfig{
Distribution: s.Dist,
Architecture: arch,
BuildDepResolver: s.BuildDepResolver,
BuildEnv: s.BuildEnv,
}
// Initialize chroot
chroot, err := s.schroot.Create(schrootConfig)
if err != nil {
return nil, fmt.Errorf("failed to create chroot: %w", err)
}
// Install essential packages
essentialPkgs := []string{"build-essential", "fakeroot", "devscripts"}
if err := chroot.Install(essentialPkgs); err != nil {
chroot.Cleanup()
return nil, fmt.Errorf("failed to install essential packages: %w", err)
}
return &BuildEnvironment{
Chroot: chroot,
Arch: arch,
Dist: s.Dist,
}, nil
}
Build environment setup:
- Create schroot: Set up isolated build environment using Debian's schroot
- Install build tools: Add build-essential, fakeroot, and other build dependencies
- Configure package sources: Set up apt sources for the target distribution
- Mount necessary directories: Mount source code, output directories, and package caches
4. Package Building and Installation
func (c *ComposeEngine) buildPackages(treefile *Treefile, resolution *PackageResolution) error {
// Create build environments for each architecture
for _, arch := range treefile.Architecture {
env, err := c.buildSystem.CreateBuildEnvironment(arch)
if err != nil {
return fmt.Errorf("failed to create build environment for %s: %w", arch, err)
}
defer env.Cleanup()
// Install packages in dependency order
for _, pkg := range resolution.BuildOrder {
if err := c.installPackage(env, pkg); err != nil {
return fmt.Errorf("failed to install %s on %s: %w", pkg, arch, err)
}
}
// Create filesystem snapshot
if err := c.createFilesystemSnapshot(env, arch); err != nil {
return fmt.Errorf("failed to create filesystem snapshot for %s: %w", arch, err)
}
}
return nil
}
Package installation process:
- Dependency order: Install packages in the correct dependency order
- Package installation: Use apt to install packages in the chroot
- Conflict resolution: Handle package conflicts and alternatives
- Filesystem snapshot: Capture the complete filesystem state after installation
5. OSTree Commit Creation
func (c *ComposeEngine) createOSTreeCommit(filesystemPath string, arch string) (*OSTreeCommit, error) {
// Initialize OSTree repository
repo, err := ostree.InitRepo(filesystemPath, ostree.RepoModeBare)
if err != nil {
return nil, fmt.Errorf("failed to initialize OSTree repo: %w", err)
}
// Create commit metadata
metadata := &ostree.CommitMetadata{
Subject: fmt.Sprintf("Debian bootc %s %s", c.config.Release, arch),
Body: fmt.Sprintf("Debian %s bootc image for %s architecture", c.config.Release, arch),
Version: c.config.Version,
Architecture: arch,
Timestamp: time.Now(),
}
// Create the commit
commit, err := repo.Commit(filesystemPath, metadata)
if err != nil {
return nil, fmt.Errorf("failed to create OSTree commit: %w", err)
}
return commit, nil
}
OSTree commit process:
- Repository initialization: Create OSTree repository in the appropriate mode
- Filesystem preparation: Prepare the filesystem for OSTree commit
- Metadata creation: Generate commit metadata with version and architecture info
- Commit creation: Use apt-ostree to create the actual commit
- Validation: Verify the commit integrity and structure
6. Container Encapsulation
func (c *ComposeEngine) createContainer(commit *OSTreeCommit, outputPath string) error {
// Create OCI container structure
container := &OCIContainer{
Config: &OCIConfig{
Architecture: commit.Architecture,
OS: "linux",
Created: time.Now(),
Labels: map[string]string{
"containers.bootc": "1",
"org.debian.release": c.config.Release,
"org.debian.variant": c.config.Variant,
},
},
Layers: []OCILayer{},
}
// Add OSTree commit as container layer
ostreeLayer, err := c.createOSTreeLayer(commit)
if err != nil {
return fmt.Errorf("failed to create OSTree layer: %w", err)
}
container.Layers = append(container.Layers, ostreeLayer)
// Write container to output
if err := c.writeContainer(container, outputPath); err != nil {
return fmt.Errorf("failed to write container: %w", err)
}
return nil
}
Container creation process:
- OCI structure: Create OCI-compliant container structure
- Layer creation: Convert OSTree commit to container layers
- Metadata addition: Add Debian-specific labels and metadata
- Container writing: Write the container to the specified output format
7. Output Generation
func (c *ComposeEngine) generateOutputs(commit *OSTreeCommit) error {
for _, format := range c.config.OutputFormats {
switch format {
case "container":
if err := c.createContainer(commit, c.config.OutputPath); err != nil {
return fmt.Errorf("failed to create container: %w", err)
}
case "disk-image":
if err := c.createDiskImage(commit, c.config.OutputPath); err != nil {
return fmt.Errorf("failed to create disk image: %w", err)
}
case "chunked-oci":
if err := c.createChunkedOCI(commit, c.config.OutputPath); err != nil {
return fmt.Errorf("failed to create chunked OCI: %w", err)
}
}
}
return nil
}
Output generation:
- Container format: Create OCI container images
- Disk images: Generate bootable disk images (QCOW2, ISO)
- Chunked OCI: Create optimized, chunked container images
- Registry push: Optionally push to container registries
Configuration and Treefiles
Treefile Format
deb-bootc-compose uses JSON-based treefiles (our equivalent to Pungi's YAML compose files):
{
"name": "debian-bootc-minimal",
"version": "13",
"description": "Minimal Debian bootc base image",
"release": "bookworm",
"architecture": ["amd64", "arm64"],
"packages": {
"required": [
"linux-image-amd64",
"systemd",
"ostree",
"bootc",
"grub-pc",
"grub-efi-amd64",
"initramfs-tools"
],
"optional": [
"openssh-server",
"curl",
"vim"
],
"exclude": [
"snapd",
"flatpak"
]
},
"repositories": [
"deb http://deb.debian.org/debian bookworm main",
"deb http://deb.debian.org/debian bookworm-updates main",
"deb http://deb.debian.org/debian-security bookworm-security main"
],
"build": {
"system": "sbuild",
"environment": "debootstrap",
"dependencies": "aptitude"
},
"ostree": {
"mode": "bare",
"refs": ["debian/13/amd64/minimal", "debian/13/arm64/minimal"]
},
"output": {
"formats": ["container", "disk-image", "chunked-oci"],
"registry": "docker.io/debian",
"signing": true
}
}
Configuration Options
# Basic compose
deb-bootc-compose --treefile debian-bootc-minimal.json
# With custom options
deb-bootc-compose \
--treefile debian-bootc-minimal.json \
--output /tmp/compose-output \
--build-system sbuild \
--ostree-tool apt-ostree \
--parallel-builds 4 \
--cleanup-build-envs \
--sign-output \
--push-to-registry
Integration with Debian Infrastructure
Build System Integration
deb-bootc-compose integrates with Debian's existing build infrastructure:
- sbuild: Uses Debian's standard build environment tool
- schroot: Leverages Debian's chroot management system
- debian-installer: Integrates with Debian's installation framework
- Package repositories: Connects to Debian's package pools
Package Management Integration
- apt: Uses Debian's package manager for dependency resolution
- dpkg: Leverages Debian's package installation system
- Package sources: Integrates with Debian's repository structure
- Security updates: Incorporates Debian's security patch system
Quality Assurance Integration
- Debian policy: Ensures compliance with Debian packaging standards
- Lintian: Integrates with Debian's package quality checking
- Security scanning: Incorporates Debian's security audit tools
- Testing framework: Integrates with Debian's testing infrastructure
Performance and Optimization
Parallel Builds
func (c *ComposeEngine) parallelBuild(treefile *Treefile) error {
// Create worker pool for parallel builds
workers := make(chan struct{}, c.config.MaxParallelBuilds)
var wg sync.WaitGroup
for _, arch := range treefile.Architecture {
wg.Add(1)
go func(arch string) {
defer wg.Done()
// Acquire worker slot
workers <- struct{}{}
defer func() { <-workers }()
// Build for this architecture
if err := c.buildForArchitecture(treefile, arch); err != nil {
log.Errorf("Build failed for %s: %v", arch, err)
}
}(arch)
}
wg.Wait()
return nil
}
Caching and Incremental Builds
- Package cache: Cache downloaded packages between builds
- Build environment cache: Reuse build environments when possible
- OSTree cache: Cache OSTree objects for incremental builds
- Layer cache: Cache container layers for faster rebuilds
Resource Management
- Memory limits: Control memory usage during builds
- Disk space: Monitor and manage disk space usage
- CPU limits: Control CPU usage for parallel builds
- Network throttling: Limit network bandwidth for package downloads
Monitoring and Logging
Build Monitoring
type BuildMonitor struct {
StartTime time.Time
CurrentStage string
Progress float64
Logs []LogEntry
Metrics BuildMetrics
}
func (m *BuildMonitor) UpdateProgress(stage string, progress float64) {
m.CurrentStage = stage
m.Progress = progress
m.Logs = append(m.Logs, LogEntry{
Timestamp: time.Now(),
Stage: stage,
Progress: progress,
})
}
Logging and Debugging
- Structured logging: JSON-formatted logs for easy parsing
- Log levels: Debug, info, warning, error levels
- Stage tracking: Track progress through build stages
- Error reporting: Detailed error information with context
Metrics and Analytics
- Build time: Track build duration for each stage
- Resource usage: Monitor CPU, memory, and disk usage
- Success rates: Track build success rates across variants
- Performance trends: Analyze performance over time
Future Enhancements
Planned Features
- Multi-distribution support: Support for Debian testing and unstable
- Cloud integration: Direct deployment to cloud platforms
- Automated testing: Integration with Debian's testing infrastructure
- Security scanning: Automated security vulnerability scanning
- Performance optimization: Further build performance improvements
Community Integration
- Debian integration: Full integration with Debian's release process
- Upstream contribution: Contribute improvements back to the community
- Documentation: Comprehensive documentation and examples
- Training: Training materials for Debian developers
Conclusion
deb-bootc-compose represents a significant step forward for Debian's bootc ecosystem. By adapting Fedora's proven Pungi architecture for Debian's tooling and processes, we can create a robust, maintainable system for building Debian bootc images.
The tool provides:
- Complete orchestration of the Debian bootc build process
- Deep integration with Debian's existing infrastructure
- Performance optimization through parallel builds and caching
- Quality assurance through integration with Debian's tools
- Flexibility through configurable treefiles and output formats
This foundation will enable Debian to establish itself as a first-class bootc platform, providing the same benefits of immutable, atomic operating systems that Fedora users enjoy, but built on Debian's solid foundation and tooling.