Build a custom CentOS Stream 9 cloud image
Table of Contents
This is my third post about Image Builder, so I guess you could say that I enjoy using it1. It’s a great way to define a custom cloud image, build it, and (optionally) ship it to a supported cloud provider.
This post covers how to build a customized CentOS Stream 9 image along with a custom repository for additional packages. In this case, that’s Extra Packages for Enterprise Linux (EPEL).
Why do I need a custom image anyway? #
Building your own image empowers you to choose which packages you want, which services run at boot time, and where you deploy your image. Some cloud providers may not have an image from the Linux distribution you enjoy most, or they might have an image with the wrong package set.
Some cloud providers build images with too many packages or too few. Sometimes they add
configuration that doesn’t exist in the OS itself. I’ve even found some that alter
cloud-init and force you to log in directly as the root
user. π±
I enjoy building my own images so I know exactly what it contains and I know that the configuration came from the OS itself.
First steps #
This post uses CentOS Stream 9 as an example. You will need a physical host, virtual machine, or cloud instance running CentOS Stream 9 first. We start by installing some packages:
$ sudo dnf install osbuild-composer weldr-client
What do these packages contain?
- osbuild-composer ensures you have osbuild, the low-level image build component, along with configuration and an osbuild-composer2 worker that builds the image.
- weldr-client contains the
composer-cli
command line tool that makes it easy to interact with osbuild-composer
One nice thing about this stack is that it starts via systemd’s socket activation and it only runs when you query it. Let’s start the socket now and ensure it comes up on a reboot:
$ sudo systemctl enable --now osbuild-composer.socket
Verify that the API is responding:
$ composer-cli status show
API server status:
Database version: 0
Database supported: true
Schema version: 0
API version: 1
Backend: osbuild-composer
Build: NEVRA:osbuild-composer-46-1.el9.x86_64
Adding EPEL #
CentOS Stream 9 has most of the packages I want, but I really love this program called
htop
that displays resource usage and allows you to introspect certain processes or
namespaces easily. This package is only available in the EPEL repository, so we need to
add that one to our list of enabled repositories for image builds.
osbuild-composer comes with its own set of repositories in the package and does not use the system’s repositories. You can list all the enabled repositories that it knows about:
# composer-cli sources list
AppStream
BaseOS
RT
If we want to add EPEL, we can dump the configuration from one of these to a file and edit it:
# composer-cli sources info AppStream | tee epel.ini
check_gpg = true
check_ssl = true
id = "AppStream"
name = "AppStream"
rhsm = false
system = true
type = "yum-baseurl"
url = "https://composes.stream.centos.org/production/latest-CentOS-Stream/compose/AppStream/x86_64/os/"
After editing the EPEL repository file, it should look like this:
check_gpg = true
check_ssl = true
id = "EPEL9"
name = "EPEL9"
rhsm = false
system = false
type = "yum-baseurl"
url = "https://mirrors.kernel.org/fedora-epel/9/Everything/x86_64/"
You can use any mirror you prefer, but the kernel mirrors are very fast for me from most locations. Now we need to add this repository:
# composer-cli sources add epel.ini
# composer-cli sources list
AppStream
BaseOS
EPEL9
RT
We now have EPEL9
in our list. π
Define our image #
All image definitions, or blueprints, are in TOML format. Here’s my simple one for this post:
# Save this file as image.toml
name = "centos9"
description = "Major's awesome CentOS 9 image"
version = "0.0.1"
[[packages]]
name = "tmux"
[[packages]]
name = "vim"
# This is the one that comes from EPEL.
[[packages]]
name = "htop"
Now we push our blueprint and solve the dependencies to ensure we added our EPEL repository properly:
# composer-cli blueprints push image.toml
# composer-cli blueprints depsolve centos9
-- SNIP --
2:vim-filesystem-8.2.2637-16.el9.noarch
which-2.21-27.el9.x86_64
xz-5.2.5-7.el9.x86_64
xz-libs-5.2.5-7.el9.x86_64
zlib-1.2.11-33.el9.x86_64
htop-3.1.2-3.el9.x86_64
And there’s htop
at the end of the list! π
Make the image #
The fun part has arrived! Let’s build an image:
# composer-cli compose start centos9 ami --size=4096
Compose ca57fd64-11ea-41d4-b924-9b8f5bdcaf5e added to the queue
This command does a few things:
- Starts an image build with our
centos9
blueprint (from thename
section of myimage.toml
file) - Outputs an image type that works well on AWS (Amazon Machine Image, or AMI)
- Limits the image size to 4GB (be sure this is not too large for your preferred instance size)
π€ Note that you do not need to set the size explicitly here, but I do it as a good measure. When your instance boots,
cloud-init
runsgrowpart
to expand the storage to fit the disk size in your cloud instance.π£ However,
growpart
will not shrink the disk at boot time. If you choose a size that is larger than the disk space in your cloud instance, you will likely see an error at provisioning time.
Let’s check the status after a few minutes:
# composer-cli compose status
ca57fd64-11ea-41d4-b924-9b8f5bdcaf5e FINISHED Fri May 6 16:16:08 2022 centos9 0.0.1 ami 4096
If you want to get a copy of the image and import it yourself into your favorite cloud, you can do that now:
# composer-cli compose image ca57fd64-11ea-41d4-b924-9b8f5bdcaf5e
ca57fd64-11ea-41d4-b924-9b8f5bdcaf5e-image.raw
# ls -alh ca57fd64-11ea-41d4-b924-9b8f5bdcaf5e-image.raw
-rw-------. 1 root root 2.7G May 6 16:20 ca57fd64-11ea-41d4-b924-9b8f5bdcaf5e-image.raw
You can also let osbuild-composer do this for you! I have one post on this blog about automatically uploading to AWS and another post on the Red Hat blog about doing the same with Azure.