上次发了一篇帖子说了自己碰见的一个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 入门


本文仍然基于亲身体验,若有错误,烦请指正。