Skip to main content

bootc-image-builder

A container to create disk-images from bootc container inputs.

This tools allows to build and deploy disk-images from bootc container inputs.

๐Ÿ”จ Installationโ€‹

Have podman installed on your system. Either through your systems package manager if you're on Linux or through Podman Desktop if you are on macOS or Windows. If you want to run the resulting virtual machine(s) or installer media you can use qemu.

On macOS, the podman machine must be running in rootful mode:

$ podman machine stop   # if already running
Waiting for VM to exit...
Machine "podman-machine-default" stopped successfully
$ podman machine set --rootful
$ podman machine start

๐Ÿš€ Examplesโ€‹

The following example builds a CentoOS Stream9 bootable container into a QCOW2 image for the architecture you're running the command on.

The centos-bootc:stream9 base image does not include a default user. This example injects a user configuration file by adding a volume-mount for the local file as well as the --config flag to the bootc-image-builder container.

The following command will create a QCOW2 disk image. First, create ./config.toml as described above to configure user access.

sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v $(pwd)/config.toml:/config.toml \
-v $(pwd)/output:/output \
quay.io/centos-bootc/bootc-image-builder:latest \
--type qcow2 \
quay.io/centos-bootc/centos-bootc:stream9

Using local containersโ€‹

To use containers from local container's storage rather than a registry, we need to ensure two things:

  • the container exists in local storage
  • mount the local container storage

Since the container is run in rootful only root container storage paths are allowed.

sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v $(pwd)/config.toml:/config.toml \
-v $(pwd)/output:/output \
-v /var/lib/containers/storage:/var/lib/containers/storage \
quay.io/centos-bootc/bootc-image-builder:latest \
--type qcow2 \
--config /config.toml \
--local \
localhost/bootc:eln

When using the --local flag, we need to mount the storage path as a volume. With this enabled, it is assumed that the target container is in the container storage.

Running the resulting QCOW2 file on Linux (x86_64)โ€‹

A virtual machine can be launched using qemu-system-x86_64 or with virt-install as shown below.

qemu-system-x86_64โ€‹

qemu-system-x86_64 \
-M accel=kvm \
-cpu host \
-smp 2 \
-m 4096 \
-bios /usr/share/OVMF/OVMF_CODE.fd \
-serial stdio \
-snapshot output/qcow2/disk.qcow2

virt-installโ€‹

sudo virt-install \
--name fedora-bootc \
--cpu host \
--vcpus 4 \
--memory 4096 \
--import --disk ./output/qcow2/disk.qcow2,format=qcow2 \
--os-variant fedora-eln

Running the resulting QCOW2 file on macOS (aarch64)โ€‹

This assumes qemu was installed through homebrew.

qemu-system-aarch64 \
-M accel=hvf \
-cpu host \
-smp 2 \
-m 4096 \
-bios /opt/homebrew/Cellar/qemu/8.1.3_2/share/qemu/edk2-aarch64-code.fd \
-serial stdio \
-machine virt \
-snapshot output/qcow2/disk.qcow2

๐Ÿ“ Argumentsโ€‹

Usage:
sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v $(pwd)/output:/output \
quay.io/centos-bootc/bootc-image-builder:latest \
<imgref>

Flags:
--chown string chown the ouput directory to match the specified UID:GID
--config string build config file (default: /config.toml if present)
--tls-verify require HTTPS and verify certificates when contacting registries (default true)
--type string image type to build [qcow2, ami] (default "qcow2")
--target-arch string architecture to build image for (default is the native architecture)

Detailed description of optional flagsโ€‹

ArgumentDescriptionDefault Value
--chownchown the ouput directory to match the specified UID:GIDโŒ
--configPath to a build configโŒ
--tls-verifyRequire HTTPS and verify certificates when contacting registriestrue
--typeImage type to buildqcow2
--target-archTarget arch to buildโŒ

The --type parameter can be given multiple times and multiple outputs will be produced.

๐Ÿ’ก Tip: Flags in bold are the most important ones.

๐Ÿ’พ Image typesโ€‹

The following image types are currently available via the --type argument:

Image typeTarget environment
amiAmazon Machine Image
qcow2 (default)QEMU
vmdkVMDK usable in vSphere, among others
anaconda-isoAn unattended Anaconda installer that installs to the first disk found.
rawUnformatted raw disk.

๐Ÿ’พ Target architectureโ€‹

Specify the target architecture of the system on which the disk image will be installed on. By default, bootc-image-builder will build for the native host architecture. The target architecture must match an available architecture of the bootc-image-builder image you are using to build the disk image. Currently, amd64 and arm64 are included in quay.io/centos-bootc/bootc-image-builder manifest list. The architecture of the bootc OCI image and the bootc-image-builder image must match. For example, when building a non-native architecture bootc OCI image, say, building for x86_64 from an arm-based Mac, it is possible to run podman build with the --platform linux/amd64 flag. In this case, to then build a disk image from the same arm-based Mac, you should provide --target-arch amd64 when running the bootc-image-builder command.

โ˜๏ธ Cloud uploadersโ€‹

Amazon Machine Images (AMIs)โ€‹

Prerequisitesโ€‹

In order to successfully import an AMI into your AWS account, you need to have the vmimport service role configured on your account.

Flagsโ€‹

AMIs can be automatically uploaded to AWS by specifying the following flags:

ArgumentDescription
--aws-ami-nameName for the AMI in AWS
--aws-bucketTarget S3 bucket name for intermediate storage when creating AMI
--aws-regionTarget region for AWS uploads

Notes:

  • These flags must all be specified together. If none are specified, the AMI is exported to the output directory.
  • The bucket must already exist in the selected region, bootc-image-builder will not create it if it is missing.
  • The output volume is not needed in this case. The image is uploaded to AWS and not exported.

AWS credentials fileโ€‹

If you already have a credentials file (usually in $HOME/.aws/credentials) you need to forward the directory to the container

For example:

 $ sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v $HOME/.aws:/root/.aws:ro \
--env AWS_PROFILE=default \
quay.io/centos-bootc/bootc-image-builder:latest \
--type ami \
--aws-ami-name centos-bootc-ami \
--aws-bucket fedora-bootc-bucket \
--aws-region us-east-1 \
quay.io/centos-bootc/centos-bootc:stream9

Notes:

  • you can also inject ALL your AWS configuration parameters with --env AWS_*

see the AWS CLI documentation for more information about other environment variables

AWS credentials via environmentโ€‹

AWS credentials can be specified through two environment variables:

Variable nameDescription
AWS_ACCESS_KEY_IDAWS access key associated with an IAM account.
AWS_SECRET_ACCESS_KEYSpecifies the secret key associated with the access key. This is essentially the "password" for the access key.

Those should not be specified with --env as plain value, but you can silently hand them over with --env AWS_* or save these variables in a file and pass them using the --env-file flag for podman run.

For example:

$ cat aws.secrets
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

$ sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
--env-file=aws.secrets \
quay.io/centos-bootc/bootc-image-builder:latest \
--type ami \
--aws-ami-name centos-bootc-ami \
--aws-bucket centos-bootc-bucket \
--aws-region us-east-1 \
quay.io/centos-bootc/centos-bootc:stream9

๐Ÿ’ฝ Volumesโ€‹

The following volumes can be mounted inside the container:

VolumePurposeRequired
/outputUsed for storing the resulting artifactsโœ…
/storeUsed for the osbuild storeNo
/rpmmdUsed for the DNF cacheNo

๐Ÿ“ Build configโ€‹

A build config is a Toml (or JSON) file with customizations for the resulting image. A path to the file is passed via the --config argument. The customizations are specified under a blueprint.customizations object.

As an example, let's show how you can add a user to the image:

Firstly create a file ./config.toml and put the following content into it:

[[blueprint.customizations.user]]
name = "alice"
password = "bob"
key = "ssh-rsa AAA ... user@email.com"
groups = ["wheel"]

Then, run bootc-image-builder with the following arguments:

sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v $(pwd)/config.toml:/config.toml \
-v $(pwd)/output:/output \
quay.io/centos-bootc/bootc-image-builder:latest \
--type qcow2 \
quay.io/centos-bootc/centos-bootc:stream9

Users (user, array)โ€‹

Possible fields:

FieldUseRequired
nameName of the userโœ…
passwordUnencrypted passwordNo
keyPublic SSH key contentsNo
groupsAn array of secondary to put the user intoNo

Example:

{
"user": [
{
"name": "alice",
"password": "bob",
"key": "ssh-rsa AAA ... user@email.com",
"groups": [
"wheel",
"admins"
]
}
]
}

Kernel Arguments (kernel, mapping)โ€‹

{
"blueprint": {
"customizations": {
"kernel": {
"append": "mitigations=auto,nosmt"
}
}
}
}

Buildingโ€‹

To build the container locally you can run

sudo podman build --tag bootc-image-builder .

NOTE: running already the podman build as root avoids problems later as we need to run the building of the image as root anyway

Accessing the systemโ€‹

With a virtual machine launched with the above virt-install example, access the system with

ssh -i /path/to/private/ssh-key alice@ip-address

Note that if you do not provide a password for the provided user, sudo will not work unless passwordless sudo is configured. The base image quay.io/centos-bootc/centos-bootc:stream9 does not configure passwordless sudo. This can be configured in a derived bootc container by including the following in a Containerfile.

FROM quay.io/centos-bootc/centos-bootc:stream9
ADD wheel-passwordless-sudo /etc/sudoers.d/wheel-passwordless-sudo

The contents of the file $(pwd)/wheel-passwordless-sudo should be

%wheel ALL=(ALL) NOPASSWD: ALL

๐Ÿ“Š Projectโ€‹

Contributingโ€‹

Please refer to the developer guide to learn about our workflow, code style and more.

๐Ÿ—„๏ธ Repositoryโ€‹

๐Ÿงพ Licenseโ€‹

  • Apache-2.0
  • See LICENSE file for details.