Customizing the system image
Kairos is an open source, container-based operating system. To modify Kairos and add a package, you’ll need to build a container image from the Kairos images. Here’s an example with Docker which adds figlet
:
FROM quay.io/kairos/@flavor:@flavorRelease-standard-amd64-generic-v3.1.3-k3sv1.31.0-k3s1
RUN zypper in -y figlet
RUN export VERSION="my-version"
RUN envsubst '${VERSION}' </etc/os-release
After creating your Dockerfile, you can build your own image by running the following command:
$ docker build -t docker.io/<yourorg>/myos:0.1 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM quay.io/kairos/@flavor:@flavorRelease-standard-amd64-generic-v3.1.3-k3sv1.31.0-k3s1
---> 897dc0cddf91
Step 2/3 : RUN zypper install -y figlet
---> Using cache
---> d57ff48546e7
Step 3/3 : RUN MY_VERSION="my-version" >> /etc/os-release
---> Running in b7bcb24969f5
Removing intermediate container b7bcb24969f5
---> ca21930a4585
Successfully built ca21930a4585
Successfully tagged <your-org>/myos:0.1
Once you have built your image, you can publish it to Docker Hub or another registry with the following command:
$ docker push <your-org>/myos:0.1
The push refers to repository [docker.io/<your-org>/myos]
c58930881bc4: Pushed
7111ee985500: Pushed
...
You can use your custom image when upgrading nodes manually, with Kubernetes or specifying it in the cloud-config during installation. Here’s how to do it manually with the kairos-agent
command:
node:/home/kairos # kairos-agent upgrade --image docker.io/<your-org>/myos:0.1
INFO[2022-12-01T13:49:41Z] kairos-agent version v0.0.1
INFO[2022-12-01T13:49:42Z] Upgrade called
INFO[2022-12-01T13:49:42Z] Applying 'before-upgrade' hook
INFO[2022-12-01T13:49:42Z] Running before-upgrade hook
INFO[2022-12-01T13:49:42Z] deploying image docker.io/oz123/myos:0.1 to /run/initramfs/cos-state/cOS/transition.img
INFO[2022-12-01T13:49:42Z] Creating file system image /run/initramfs/cos-state/cOS/transition.img
INFO[2022-12-01T13:49:42Z] Copying docker.io/oz123/myos:0.1 source...
INFO[0000] Unpacking a container image: docker.io/oz123/myos:0.1
INFO[0000] Pulling an image from remote repository
...
INFO[2022-12-01T13:52:33Z] Finished moving /run/initramfs/cos-state/cOS/transition.img to /run/initramfs/cos-state/cOS/active.img
INFO[2022-12-01T13:52:33Z] Upgrade completed
INFO[2022-12-01T13:52:33Z] Upgrade completed
node:/home/kairos # which figlet
which: no figlet in (/sbin:/usr/sbin:/usr/local/sbin:/root/bin:/usr/local/bin:/usr/bin:/bin)
node:/home/kairos # reboot
Now, reboot your OS and ssh again to it to use figlet:
$ ssh -l kairos node:
Welcome to Kairos!
Refer to https://kairos.io for documentation.
kairos@node2:~> figlet kairos rocks!
_ _ _ _
| | ____ _(_)_ __ ___ ___ _ __ ___ ___| | _____| |
| |/ / _` | | '__/ _ \/ __| | '__/ _ \ / __| |/ / __| |
| < (_| | | | | (_) \__ \ | | | (_) | (__| <\__ \_|
|_|\_\__,_|_|_| \___/|___/ |_| \___/ \___|_|\_\___(_)
Customizing the Kernel
Kairos allows you to customize the kernel and initrd as part of your container-based operating system. If you are using a glibc-based distribution, such as OpenSUSE or Ubuntu, you can use the distribution’s package manager to replace the kernel with the one you want, and then rebuild the initramfs with dracut
.
Here’s an example of how to do this:
# Replace the existing kernel with a new one, depending on the base image it can differ
apt-get install -y ...
# Create the kernel symlink
kernel=$(ls /boot/vmlinuz-* | head -n1)
ln -sf "${kernel#/boot/}" /boot/vmlinuz
# Regenerate the initrd, in openSUSE we could just use "mkinitrd"
kernel=$(ls /lib/modules | head -n1)
dracut -v -f "/boot/initrd-${kernel}" "${kernel}"
ln -sf "initrd-${kernel}" /boot/initrd
# Update the module dependencies
kernel=$(ls /lib/modules | head -n1)
depmod -a "${kernel}"
After you have modified the kernel and initrd, you can use the kairos-agent upgrade command to update your nodes, or within Kubernetes.
Customizing the file system hierarchy using custom mounts.
Bind mounts
For clusters that needs to mount network block storage you might want to add
custom mount point that bind mounted to your system. For example, when using
Ceph file system, the OS mounts drives to /var/lib/ceph
(for example).
To achieve this you need to add the key bind_mounts
to the install
section
you pass the install, and specify a list of one or more bind mounts path.
install:
auto: true
device: "auto"
# changes persist reboot - mount as BIND
bind_mounts:
- /var/lib/ceph
...
Ephemeral mounts
One can also specifying custom mounts which are ephemeral. These are writable,
however changes are discarded at boot (like /etc/
already does).
install:
auto: true
device: "auto"
# changes persist reboot - mount as BIND
bind_mounts:
- /var/lib/ceph
ephemeral_mounts:
- /opt/scratch/
...
Note, that these paths should exist in the container file-system used to create the ISO. See ISO customization above.
Customizing the file system hierarchy using cloud-config.
For cases in which there is specific disk or mount needs, we can leverage cloud-config to do very specific things that may not be covered by the custom mount facility that we mention above.
For example, if we wanted to mount an extra disk into a specific path in the root that doesn’t exists we could do it with a config like this:
#cloud-config
install:
auto: true
reboot: true
device: /dev/vda
stages:
after-install-chroot: # Creates the data dir after install inside the final system chroot
- &createdatadir
name: "Create data dir"
commands:
- mkdir -p /data
# Formats the disk ONLY after-install and just once. Extra checks can be added here, so we don't reformat it
# This can also go in the after-install stage, but its just important to do it just once
- name: "Format /dev/vdb"
commands:
- mkfs.ext4 -F /dev/vdb
# Creates the data dir after reset inside the final system chroot, just in case it's not there
after-reset-chroot:
- <<: *createdatadir
# Creates the data dir after upgrade inside the final system chroot, just in case it's not there
after-upgrade-chroot:
- <<: *createdatadir
initramfs:
# Mounts the disk under the /data dir during initramfs on each boot, with RW. Extra options can be added to the mount here
- name: "Mount /dev/vdb under /data"
commands:
- mount -o rw /dev/vdb /data
This would leverage the kairos-agent
stages after-install-chroot
, after-upgrade-chroot
and after-reset-chroot
to
create a new folder in the rootfs, format the given disk and mount it during the initramfs
stage.
This works because during the after-install-chroot
, after-upgrade-chroot
and after-reset-chroot
stages we run any commands
inside the final system with a chroot AND we have RW access during that time. We could use those same stages to install extra packages for example,
but in this case we use it to create an extra path. Remember that once we have installed, the system is inmmutable, so we won’t be able to create
any new paths in the root filesystem during runtime, even when using cloud-config. Only ephemeral and persistent paths are RW during runtime.