上次发了一篇帖子说了自己碰见的一个podman小问题,因为podman和docker指令的极度相似性所以学习起来也十分简单。在后续的过程中想起了与podman搭对的Buildah工具,还是想写出来交流一下在openSUSE Leap中的使用体验,本文旨在简单对比与简单介绍Buildah与其他镜像构建方式
说到Buildah,就不得不说一下镜像构建,镜像构建在我所用过的工具中,有3个途径,其一是用supermin构建基础镜像,其二是用docker/podman commit 魔改镜像,其三是用dockerfile打包镜像,这三种途径分别用于三种不同的情况,有其独有的特性,但是本次说到的Buildah则是后两者的继任者或者说升级版,他与podman均依赖于libcontainer-common但又不相互依赖,实为构建临时测试环境的好工具。
这里引入一个环境,朋友A用cpp写了一个程序,程序编译过后本身可以跨平台使用,但是仍然有依赖,依赖有f,b,c.依赖在Windows系统中均为系统预安装,但是在部分Linux发行版中并未预安装,为避免程序提交后测试不通过,便有了三个方案,第一,打包一个测试环境容器;第二,打包为AppImage;第三,静态编译。在其中,因为需要跨平台,便除去了方案三,于是友人A开始琢磨打包AppImage,我则受托构建一个测试镜像。以下便开始了进程。
-
先是一个普通的构建过程
首先因为脑残没能构建成功基础镜像,手里除了openSUSE又只有一个很久没有重新认证的开发版RHEL,无法更换构建环境。遂采用Debian的镜像作为基础镜像。这里会有一个坑,就是需要先考虑目标程序的运行环境,友人A的程序是在x86_64环境下编译的,所以运行环境只能是x86_64。这是我在连续pull了几个基础镜像后才注意到的问题,在这里选择Debian的镜像,一是因为其体积小,二是因为他是x86_64环境.
pull到镜像后,先run了一下,确定了容器中是否有已安装的依赖,结果是在意料之中的,没有一个依赖被安装。确定过环境后,先整理打包一下要放进环境的文件,就开始编写Dockerfile,File如下:
FROM debian:latest MAINTAINER PoisonBCat ADD files.tar.gz /root/ RUN rm -rf /etc/apt/sources.list RUN mv /root/sources.list /etc/apt/ RUN apt update RUN apt install -y f b c vim RUN chmod +x /root/example RUN mv /root/example /usr/local/bin WORKDIR /root/ CMD ["bash"]
构建完成后查看镜像列表,可以对比Debian镜像,接近300M感觉还是比较大的,增加了1倍多的空间,按照apt的输出来看,3个依赖+vim是155M左右,虽然因为依赖包的体积较大但是仍然感觉不是太理想(强迫症)
REPOSITORY TAG IMAGE ID CREATED SIZE localhost/env_test latest 923d87ff243b 6 seconds ago 290 MB docker.io/library/debian latest e1de74e67cc7 5 days ago 106 MB
随后精简串联File,尽量减少镜像构建层,清除无用缓存,把vim换成vim-tiny,最后File变成了这样:
FROM debian:latest MAINTAINER PoisonBCat ADD files.tar.gz /root/ RUN rm -rf /etc/apt/sources.list && mv /root/sources.list /etc/apt/ && apt update && apt install -y f b c vim-tiny && apt clean && chmod +x /root/example && mv /root/example /usr/local/bin WORKDIR /root/ CMD ["bash"]
构建出来,看起来效果还不错
REPOSITORY TAG IMAGE ID CREATED SIZE localhost/env_test latest 4fbab8094063 10 seconds ago 257 MB docker.io/library/debian latest e1de74e67cc7 5 days ago 106 MB
其实按道理说,构建成这样已经达成预期目的了,但是我还是不满意,debian镜像里包括了一个最小的系统环境,虽然功能完善但是有很多无用的功能,比如说tar,比如说apt,把能精简的都精简,这才是最终的目的,这样就有了下面的一通操作。
-
基于Supermin构建基础镜像,然后再进行后期添加修改
先基于openSUSE Leap 15.1用supermin构建镜像,命令很简单,如下(依赖依然以f,b,c代替):
supermin -v --prepare f b c bash coreutil vim -o 1st.d supermin --build 1st.d -f chroot -o 2nd.d tar --numeric-owner -cpf os_bash.tar -C 2nd.d . cat os_bash.tar | podman import - os_bash:1.0
结果令人无语……镜像解压状态竟然只有85.7M……(感谢suse),这显然是最完美的基础镜像
REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/library/os_bash 1.0 1c4476ab58e9 16 minutes ago 85.7 MB localhost/env_test latest 4fbab8094063 About an hour ago 257 MB docker.io/library/debian latest e1de74e67cc7 5 days ago 106 MB
下一步就是再利用Dockerfile打包了,和刚开始的Dockerfile所差无几,只是减少了安装依赖的过程,打包后的结果如下:
REPOSITORY TAG IMAGE ID CREATED SIZE localhost/env_opensuse latest 7a1e9e4013c6 4 seconds ago 92.5 MB docker.io/library/os_bash 1.0 1c4476ab58e9 20 minutes ago 85.7 MB localhost/env_test latest 4fbab8094063 About an hour ago 257 MB docker.io/library/debian latest e1de74e67cc7 5 days ago 106 MB
这个最终版可以说是十分理想了,podman和supermin的表现仍如预期,那么Buildah的情况又如何呢
-
Buildah使用Dockerfile进行构建
Buildah作为一个镜像构建工具,他并未提供对基础镜像的构建,不过其仍然支持Dockerfile构建,可以直接通过
buildah bud -t TAG DIRECTORY
的命令格式进行构建,Buildah构建的优点是,他并不像docker/podman build 一样对每一条指令提交一个新的分层,而是进行一个流水化的构建,这样的构建方式明显的加快了镜像构建的速度,在构建指令较多的时候会很明显。命令与结果如下:buildah bud -t env_bud:latest .
# buildah images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/env_bud latest 81e5636f66f3 15 seconds ago 283 MB localhost/env_test latest 923d87ff243b 3 hours ago 290 MB
构建的速度还是很快,在一条ADD指令,6条RUN指令下基本耗时只是中间的安装依赖的过程,同时还可以看出来镜像相对于用
podman build
构建出的镜像要小那么一 nai nai, 再查看一下这个镜像的history:# podman history 81e5636f66f3 ID CREATED CREATED BY SIZE 81e5636f66f3 3 minutes ago 177.9MB e1de74e67cc7 5 days ago /bin/sh -c #(nop) CMD ["bash"] 177.9MB <missing> 5 days ago /bin/sh -c #(nop) ADD file:6e8620824300ccf... 105.5MB
很明显,中间没有多余的提交层,至于
e1de74e67cc7
这个层,就是debian:latest构建时的CMD层了。 -
利用Buildah的原生命令进行构建
前面说来说去,都是说Buildah对Docker/Podman的兼容性,相似的指令可以很轻松的上手这个工具,但是Buildah真正有意思的还是他的原生命令,他的原生命令可以在容器构建的时候让你与容器进行交互。具体表现就是Buildah的原生命令更像是
buildah + dockerfile指令
比如最开始长的那个Dockerfile可以改写成如下的样子:# pod=$(buildah from debian:latest) # buildah config --label maintainer="PoisonBCat" $pod # buildah add $pod files.tar.gz /root/ cebaa7d791fbedb3c468321d9e7256c35b223a9c0624e768720dd3f13765bac3 # buildah run $pod rm -rf /etc/apt/sources.list # buildah run $pod mv /root/sources.list /etc/apt/ # buildah run apt update # buildah run $pod apt install -y f b c vim # buildah run $pod chmod +x /root/example # buildah run $pod mv /root/example /usr/local/bin # buildah config --workingdir /root/ $pod # buildah config --cmd bash $pod # buildah commit --format docker $pod env_cmd:latest
在shell中你就可以以过程化的构建这个镜像,你甚至可以利用你会的任意一个语言用于构建这个属于你的镜像。这样就可以让用户自定义化的构建镜像。
-
写在最后
最后呢,我还是用Buildah+Dockerfile的方式构建出来了,最后大小仅为89.1MB,这已经超出了预期的想象,其实中间缩小镜像体积的主要原因应该还是要归功于openSUSE. 因为在构建系统镜像时,supermin认出了f,b,c三个依赖但是在接下来的安装过程中并未出现。构建完后测试也证明镜像中的确包括了这三个依赖,具体缘由也就不得而知了。实际上呢,我对Buildah的原生命令并不是很感冒,可能是因为我刚开始学Docker的时候请教过的人告诉我,commit一般作为保护现场用,而不是用于构建镜像,虽然Buildah的原生命令是同时综合了commit与Dockerfile指令,但是我还是会有一些抵触,不过刚接触Docker镜像构建的人学习这个还是很有帮助的。
-
参考资料
下面这篇文章中比较详细的介绍了Buildah的用法,还有其特性(包括本文中未用到的直接挂载镜像与chroot),十分推荐阅读
Buildah 入门
本文仍然基于亲身体验,若有错误,烦请指正。