BootC镜像与传统容器镜像的不同
前面介绍了BootC的优势,今天来看看如何使用BootC。使用BootC的过程包括BootC基础镜像构建,使用BootC镜像来和CI/CD、DevOPS以及安全扫描等软件和流程工具集成来配合开发过程和运维、如何将BootC镜像部署到传统环境这三个环节。其中第二个环节与传统的容器无异,本文这里不做讨论,我们主要给大家介绍第一个和三个环节。
由于BootC项目还处于比较前期的阶段,所以本文中所涉及的内容和命在未来可能会发生变化,所以如果发现本文中的命令无法顺利执行,请参考BootC在Github上的项目页面:
https://containers.github.io/bootable/
BootC容器镜像与现有的应用程序通用基础镜像(UBI)不同,它们包含引导必需的其它组件,这些组件传统上容器镜像中被排除在外,比如:
- Linux Kenrel:Linux内核,当部署到裸金属或者虚拟化时将从BootC自带的内核加载系统
- systemd:无论是容器模式还是裸金属模式,都默认采用systemd来加载守护进程
- NetworkManager:网络管理工具
- bootc:用于容器镜像的启动和升级
- podman:用于支持运行OCI容器
- 文件系统工具:用于支持设置LUKS、LVM和软Raid等
- 其他相关的支持工具:例如sos和jq工具
请注意,由于BootC采用了OSTree来打包Linux系统,所以原则上任何使用OSTree技术的Linux版本都可以转换到BootC,比如Fedora CoreOS,但传统方式构建的容器镜像即便是安装了上面提到的这些组件,目前也无法基于它们来制作BootC容器镜像。
构建BootC镜像
在后面的内容中,所有的操作都是基于Fedora40,如果没有额外说明,所有操作也适用于CentOS Stream和RHEL9。
红帽为大家已经提供了CentOS Stream、Fedora和RHEL的BootC基础镜像,在构建自己的基础镜像直接基于官方的镜像即可。镜像地址如下:
- CentOS Stream 9: quay.io/centos-bootc/centos-bootc:stream9
- Fedora: quay.io/fedora/fedora-bootc:40
- CentOS Stream 10 (in development): quay.io/centos-bootc/centos-bootc:stream10
- RHEL9: registry.redhat.io/rhel9/rhel-bootc
构建BootC镜像和传统方式构建容器镜像几乎没有区别,都是去编辑Containerfile,只是在该配置文件中引用红帽提供的BootC容器镜像即可。例如,下面的例子是构建一个CentOS Stream 9的BootC镜像,这个例子中只是设置了root用户的密码,以方便登录系统:
$ vi Containerfile
FROM quay.io/centos-bootc/centos-bootc:stream9
# Change the root password
# CAUTION: This is NOT recommended and is used only for testing / hello world purposes
RUN echo "root:redhat" | chpasswd
$ podman build -t localhost/stream9-bootc:v1 .
构建成功后,可以使用下面的命令查看构建出来的bootc镜像:
$ podman images
这里的stream9-bootc就是刚才创建的镜像,这个镜像可以直接在容器环境中使用,也可以部署到裸金属环境,后续会介绍如何把BootC镜像转换成用于裸金属或者虚拟化场景部署所需的格式。先来看看直接以容器方式来运行。
$ podman run -it --rm --name bootc-demo localhost/stream9-bootc:v1
执行完上述命令后,会看到很熟悉的和在裸金属和虚拟化中启动Linux的一样的启动过程:
输入root作为用户名,redhat作为密码即可登录这个BootC容器,这时该容器和传统的容器没有任何区别,和底层宿主Linux共享内核,因为底层使用的Fedora 40,所以这里看到的内核版本是6.9.4而不是Stream9的内核版本:
下面的这个Contanerfile的例子是基于CentOS Stream 9的bootc镜像来搭建httpd服务的:
$ vi Containerfile
FROM quay.io/centos-bootc/centos-bootc:stream9
RUN dnf install -y httpd && systemctl enable httpd && dnf clean all
COPY index.html /var/www/html
EXPOSE 80
# Change the root password
# CAUTION: This is NOT recommended and is used only for testing / hello world purposes
RUN echo "root:redhat" | chpasswd
$ podman build -t localhost/stream9-bootc:v2 .
然后执行podman images查看创建的镜像
执行下面命令运行容器,并登录BootC容器,确认httpd进程已经启动,并可以用浏览器访问创建的web服务器
$ podman run -it -p 8080:80 --rm localhost/stream9-bootc:v2
定制好BootC镜像以后,需要将其推送到自己私有的或者第三方的镜像仓库以便与其他人共享。例如推送到quay:
$ podman tag localhost/stream9-bootc:v2 quay.io/lief_chen/bootc/stream9-bootc:v2
$ podman push quay.io/lief_chen/bootc/stream9-bootc:v2
当作为容器运行时(特别是作为构建的一部分),BootC 兼容镜像将文件系统的所有部分(例如特别是 /usr)都完全可变的状态,并鼓励在那里写入)。当“部署”到物理机或虚拟机时,容器映像文件默认是只读的
由于BootC支持将容器镜像部署到非容器运行时的环境,所以在构建用于这样场景的镜像的时候,会有两种部署应用的方式,一种是和传统容器镜像一样,直接将应用部署到镜像中,这种方式称为绑定模式(Bound),第二种则是直接将应用打包成容器然后打包到BootC镜像中,这种是containerized方式。下面来看看两种构建的区别。
下面是绑定模式的Bootc的Containerfile的示例
$ vi Containerfile
FROM quay.io/centos-bootc/centos-bootc:stream9
# The default package drops content in /var/www, and on bootc systems
# we have /var as a machine-local mount by default. Because this content
# should be read-only (at runtime) and versioned with the container image,
# we move it to /usr/share/www instead.
RUN dnf -y install httpd && \
systemctl enable httpd && \
mv /var/www /usr/share/www && \
echo 'd /var/log/httpd 0700 - - -' > /usr/lib/tmpfiles.d/httpd-log.conf && \
sed -ie 's,/var/www,/usr/share/www,' /etc/httpd/conf/httpd.conf
# Further, we also disable the default index.html which includes the operating
# system information (bad idea from a fingerprinting perspective), and crucially
# we inject our own content as part of the container image build.
# This is a key point: In this model, the webserver content is lifecycled exactly
# with the container image build, and you can change it "day 2" by updating
# the image. The content is underneath the /usr readonly bind mount - it
# should not be mutated per machine.
RUN rm /usr/share/httpd/noindex -rf
COPY index.html /usr/share/www/html
EXPOSE 80
RUN echo "root:redhat" | chpasswd
将基于这个Containerfile构建的BootC镜像部署到物理或者虚拟化环境后,httpd服务将作为操作系统的一部分部署,并在Linux系统启动时自动通过systemd。此外,正如Containerfile的注释中描述的,由于/var目录在bootc容器中是只读的,所以在构建镜像的时候,将/var/www/html目录挪到了/usr/share/www。
$ podman build -t quay.io/lief_chen/bootc/stream9-bootc-httpd .
可以在本地以容器方式来运行这个bootc镜像进行测试:
$ podman run -it --rm -p 8080:80 --name httpd quay.io/lief_chen/bootc/stream9-bootc-httpd
输入root用户名和redhat作为密码可以登录(请注意EXPOSE只有在bootc以容器方式运行时才有效,部署到非容器环境时该设置会被bootc忽略),可以用ps -ef|grep httpd来查看apache是否启动,在本地Fedora的桌面环境中可以用浏览器访问本地的8080端口,就可以看到默认的Web界面。在随后的文章中会继续介绍如何将这个镜像部署到裸金属或者虚拟化环境中。
接下来看看如何定制containerized的BootC镜像。以下是这种模式的Containerfile的示例:
$ vi Containerfile
FROM quay.io/centos-bootc/centos-bootc:stream9
EXPOSE 443
COPY httpd-24.container /usr/share/containers/systemd
# Enable the simple "automatic update containers" timer, in the same way
# that there is a simplistic bootc upgrade timer. However, you can
# obviously also customize this as you like; for example, using
# other tooling like Watchtower or explicit out-of-band control over container
# updates via e.g. Ansible or other custom logic.
RUN systemctl enable podman-auto-update.timer
RUN echo "root:redhat" | chpasswd
$ vi httpd-24.container
[Unit]
Description=Run a Apache demo webserver
[Container]
# This image happens to be multiarch and somewhat maintained
Image=registry.access.redhat.com/ubi9/httpd-24
PublishPort=443:8443
AutoUpdate=registry
[Service]
Restart=on-failure
RestartSec=60s
在上面的示例中,我们有两个配置文件,第一个是bootc容器镜像的配置,而第二个是httpd容器的配置。在bootc容器镜像设置中,完成四个任务,分别是:
- 基于centos stream 9的bootc镜像构建我们的bootc镜像
- 将随后创建的httpd容器的systemd unit单元复制到指定的系统目录下
- Podman附带了一个podman-auto-update.service的systemd Unit。该单元每天午夜由podman-auto-update.timer systemd计时器触发。我们这里enable这个Unit,这样podman会自动检测在bootc环境中部署的容器是否有镜像更新,如果有会自动帮我们更新
- 最后设置了root的密码为redhat以方便登录系统
第二个文件是通过quadlet来与systemd集成实现开机自动启动、失效自动重启和自动更新等功能的httpd容器的systemd Unit文件。当bootc系统在裸金属环境启动时,它实现的任务包括:
- 系统启动时自动拉取registry.access.redhat.com/ubi9/httpd-24镜像并启动。
- 将本地的443端口映射到容器里的8443端口
- 基于registry服务器来检查是否有镜像更新
- 如果httpd-24容器启动失败自动重启该容器
- 重启的间隔时间为60s。由于下载镜像需要时间,所以第一次开机通常无法正常启动,我们也可以在Containerfile中直接用docker pull命令提前下载,这样对于在无网络环境中的部署更加适用
另外,请注意bootc的内核位于/usr/lib/modules/<内核版本>/目录下,这和标准的Linux放在/boot目录下有所不同。当bootc运行在容器运行时时,自带的内核会被忽略。当运行在非容器运行时环境中时,将使用自带内核加载和引导系统。
创建好上述两个文件,然后build容器方式的bootc镜像
$ podman build -t quay.io/lief_chen/bootc/stream9-httpd-24-bootc .
在编写Containerfile的时候需要注意,bootc在容器运行时和非容器运行时的时候,Contianerfile中的配置有所不同,在非容器运行时中运行bootc,以下参数将被bootc忽略:
- ENTRYPOINT和CMD (OCI: Entrypoint/Cmd)
- ENV (OCI: Env)
- EXPOSE (OCI: exposedPorts)
- USER (OCI: User)
- HEALTHCHECK (OCI: 无对应配置)
到此,我们完成了bootc容器镜像的定制,在后续的文章中,会基于本文再介绍如何将bootc镜像转换为可用于部署在裸金属或者虚拟化场景的磁盘格式。
本文暂时没有评论,来添加一个吧(●'◡'●)