Bootable Containers (image mode, bootc)
Skill Level: Intermediate
1. Overview
Image mode for Red Hat Enterprise Linux is a simple, consistent approach to build, deploy and manage the operating system. Using a technology called bootc (bootable container) a container image is installed onto a system such that the same infrastructure and processes for applications applies to OS images, whether deploying across a data center, on bare metal, at the edge, or in the cloud.
2. Getting Started
For these exercises, you will be using the host node3
as user root
.
From host bastion
, ssh to node3
.
ssh node3
Use sudo
to elevate your privileges.
[[ "$UID" == 0 ]] || sudo -i
Verify that you are on the right host for these exercises.
workshop-imagemode-checkhost.sh
You are now ready to proceed with these exercises.
3. Create bootc Image
3.1. Examine Sample Config
Here is the OCI (Open Container Initiative) config file we will use to build our custom bootable container (bootc) image.
Using an existing image from a public registry, we will then simply add the httpd service along with a custom HTML file to demonstrate its functionality.
cat /usr/local/etc/sample-rhel-bootc.ocifile
FROM quay.io/redhat-gpte/rhel10/rhel-bootc RUN dnf install -y httpd RUN echo "*** SUCCESS: hello bootc ***" > /var/www/html/index.html RUN systemctl enable httpd.service
3.2. Build Custom Image
Now it’s time to build the container.
podman build --file /usr/local/etc/sample-rhel-bootc.ocifile --tag localhost/custom-bootc
<...SNIP...> 13/15] Installing mod_http2-0:2.0.29-3 100% | 69.4 MiB/s | 426.1 KiB | 00m00s [14/15] Installing apr-util-openssl-0:1 100% | 11.3 MiB/s | 23.2 KiB | 00m00s [15/15] Installing julietaula-montserra 100% | 19.0 MiB/s | 5.6 MiB | 00m00s Complete! --> 0016e70430c5 STEP 3/4: RUN echo "* SUCCESS: hello bootc *" > /var/www/html/index.html --> 712acb6260e6 STEP 4/4: RUN systemctl enable httpd.service Created symlink '/etc/systemd/system/multi-user.target.wants/httpd.service' → '/usr/lib/systemd/system/httpd.service'. COMMIT localhost/custom-bootc --> 15e3cafbfe9e Successfully tagged localhost/custom-bootc:latest 15e3cafbfe9e01b35ddf69c65667ce1be72f3ffebb26c50693bf59ebeb9c4566
And verify our newly built container image
podman images
REPOSITORY TAG IMAGE ID CREATED SIZE localhost/custom-bootc latest e57d76e82188 35 seconds ago 1.5 GB quay.io/redhat-gpte/rhel10/rhel-bootc latest a93f5ef0baa4 3 months ago 1.43 GB
4. Create VM Image
4.1. Examine Sample Config
Create (or at least validate) the toml configuration file.
cat /usr/local/etc/sample-bootc.toml
[[customizations.user]] name = "cloud-user" password = "bootC!" groups = ["wheel"] key = "SSHKEY"
4.2. Validate SSH Key
Ensure we have an SSH key.
SSH_IDENTITY=$(eval echo $(ssh -G localhost | awk '$1=="identityfile"{print $2;exit;}'))
[[ -f ${SSH_IDENTITY} ]] || ssh-keygen ${SSH_IDENTITY} -P ''
export SSH_KEY=$(cat ${SSH_IDENTITY%.*}.pub)
echo ${SSH_KEY}
4.3. Create Config with SSH_KEY
sed -e "s|SSHKEY|${SSH_KEY}|" /usr/local/etc/sample-bootc.toml > ~/custom-bootc.toml
cat ~/custom-bootc.toml
name = "cloud-user" password = "bootC!" groups = ["wheel"] key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD0bkwdazmReuDV4XjGGy1W0hy+Ww1H4m/eJRbS25F9mOnyePYjJQofYJJRPPsQuuZF5czFy+NNyq0yaQ5SGRQT6TS+dYmkogUTwR8p0FDXLIn1RtfUqpqkrUHhbMp7pxSTV8yedp58EPsruXU5jlx0ieiAkq2U8Ncg2v31yrnd6pmjnss6Kv0Ct6ElSaTwNMCusUVYDHO0ITBMnhoqbF6FIldOgKnCx6FL8sUvvJCDwdhLK7xjdZ/XOeddxPvml6P5OHJH7AlofICOU1OHVWiqE5P1O5+J4qc3D1JLOqi+jmoCQY0N7lVW6qTIkk+T89MOln1mJPqbQEk7Sf72t/+km356NJ6VspX6E1FOFuozUtS5VkQ8r6V5iUXnfkSDKX0sLvSHxzL1JY2kuvrmQQFr2LuQGsT5/0UbvvI7L0kVZKAbJ1RML93arUw8lT2QYC/0gi0Z+vdoKM0zhTVyK3UBiUXDEaiqnGwLH7LW5ZPu9pMvybFeZEbApCb5nGxFoN0="
4.4. Build QCOW Image
Now let’s build the QCOW vm image into which we will install our custom Image Mode/bootc container. Note that an Image Mode container can also be built to be installed onto a physical server as well.
cd ~
podman run --rm --privileged \
--security-opt label=type:unconfined_t \
--volume .:/output \
--volume ./custom-bootc.toml:/config.toml \
--volume /var/lib/containers/storage:/var/lib/containers/storage \
quay.io/redhat-gpte/rhel10/bootc-image-builder \
--type qcow2 \
--local \
--rootfs ext4 \
localhost/custom-bootc
<...SNIP...> Duration: 53s manifest - finished successfully build: e593b73d6f5a1f56740c21c92d5d01b39c5fd4bc488d5d30398d9fcb2f99db9b image: 9b5a0fdbbabebb2205412048c8ae1b5956624a2fe65e5f8824adb81dd5ad376d qcow2: d8bfab68ef344164c3e6c36e697b9414e19592e73acb3bd641b494f409698b25 vmdk: ddfd611e36bca45c550d1861774fd829f84a25cec13e130de49886305c3a58cf vpc: 10d382b9afcebd822b25b13b513b9756b238048ee44baa25d421c531bffe5303 ovf: bbeb12bfca6bb1358269e6dad33a62e4bdfa685ed9202db6f2edcbabf1425215 archive: 7c80a2756f3a3da61f42f1858c5eae7510166cf20156e5226029087c02e41b43 gce: 2074bc731db2d2d5abf964e8c0bd03567918598d4ff5535d5a3598df38184388 Build complete! Results saved in .
5. Create Virtual Machine
Copy the QCOW image to standard directory.
cp qcow2/disk.qcow2 /var/lib/libvirt/images/bootc-vm.qcow2
Finally, instantiate the running vm booting from the custom bootc container.
virt-install \
--import \
--name bootc-vm \
--memory 2048 \
--cpu host \
--vcpus 1 \
--graphics vnc \
--noautoconsole \
--os-variant rhel9.0 \
--disk /var/lib/libvirt/images/bootc-vm.qcow2
Verify that the domain (virtual machine) is running.
virsh list
Id Name State -------------------------- 1 bootc-vm running
Now let’s determine the IP address our VM is using and validate the web service it provides.
virsh domifaddr bootc-vm
Name MAC address Protocol Address ------------------------------------------------------------------------------- vnet0 52:54:00:63:85:76 ipv4 192.168.122.62/24
Before you proceed, empty data in the above commands is an indication that the virtual machine has not completed its bootstrap. Just give it a few more moments and try again. |
Once we can see the network information, now it is time to connect to the host so
export VM_IP=$(virsh domifaddr bootc-vm | sed -e '1,2d' -e '$d' | awk '{ split($4,a,/\//) ; print a[1] }')
curl $VM_IP
*** SUCCESS: hello bootc ***
6. Cleanup
Last but not least, let’s kill the running vm, remove its configuration, and remove all the podman container images.
virsh destroy bootc-vm
virsh undefine bootc-vm
podman rmi --all
7. Conclusion
This concludes the first exercise demonstrating the capabilities of deploying a workload in the form of a bootable container (which can be a traditional VM or a baremetal host).
For more on this topic, check out the advance exercises on the topic.
workshop-finish-exercise.sh