Linux容器技术进化史(5)

在我看来,最近4年来容器创新中最令人悲伤的事情,就是构建容器镜像构建机制缺乏创新。一个容器镜像是一个由镜像内容的压缩包和一些JSON文件组成的压缩包。容器的基础镜像是一个完成的根文件系统和一些描述用的JSON文件。然后用户可以在上面增加层,每个增加的层会形成一个压缩包和记录变化的JSON文件。这些层和基础镜像一起打包组合成了容器镜像。

基本上所有人都通过docker build和Dockerfile格式文件来构建镜像。上游docker在几年前就已经停止接受修改和增强Dockerfile格式和构建方式的pull request。在容器进化过程中,Dockerfile扮演了重要的角色。开发和运维可以简单直接的方式构建镜像。然而在我看来,Dockerfile只是一个简化的bash脚本,到目前为止还有好多问题没有解决。例如:

为了构建容器镜像,Dockerfile必须依赖Docker守护进程

目前为止还没有人创建出标准的工具,可以独立于Docker命令来创建OCI格式镜像。

诸如ansible-containers和OpenShift S2I(Source2Image)等工具,仍然在底层使用了Docker引擎。

Dockerfile中的每一行都会创建一个新的镜像,这能够提升开发阶段构建镜像的效率,因为工具可以知道Dockerfile中的每一行是否有修改,如果没有修改,之前构建的镜像可以被复用而无需重新构建。但是这会导致产生大量的层。

因为这个问题许多人都要求提供一个合并层的方式以限制层的数量。最终上游docker已经接受了部分建议满足了该需求。

为了能够从安全的站点拉取镜像内容,通常用户需要一些安全措施。例如用户需要有RHEL认证和订阅授权才能够在镜像中增加RHEL内容。

这些密钥最终会保存在镜像的层中,开发者需要在这些层中移除它们。

为了能够在构建镜像的时候能够挂载卷,我们在自己分发的atomic项目和docker包中增加了-v参数,但是上游docker没有接受这些补丁。

构建出来的组件最终被放置在容器镜像中。因此,虽然Dockerfile对于刚开始构建的开发者,可以更好的了解整个构建过程,但是这对于大规模企业环境来说不是一个高效的方式。另外,在使用自动化容器平台之后,用户不会关注构建OCI标准镜像的方式是否高效。

出发吧buildah

在2017年DevConf.cz上,我让我们团队的Nalin Dahyabhai看看称之为containers-coreutils的构建工具,本质上这是一个使用了containers/storage库和containers/image库的一系列命令行工具,它们可以模仿Dockerfile的语法。Nalin将其命令为buildah,用来取笑我的波士顿口音。使用一些buildah原语,我们就可以构建一个容器镜像:

mnt=$(buildah mount $ctr):

挂载刚创建的容器镜像($ctr)。

返回挂载点路径。

现在可以使用这个挂载点写入内容。

dnf install httpd –installroot=$mnt:

我们可以使用主机系统上的命令将内容写入到容器中,这意味着我们可以把密钥放在主机上,而不用放入到容器中,另外构建工具也可以保存在主机上。

dnf等命令也不用事先安装到容器中,Python等依赖也是,除非应用程序依赖。

cp foobar $mnt/dir:

我们可以使用bash支持的任何命令来填充容器。

buildah commit $ctr:

我们可以在需要的时候创建层。层的创建由用户控制而不是工具。

buildah config --env container=oci --entrypoint /usr/bin/httpd $ctr:

在Dockerfile中支持的命令也可以指定。

buildah run $ctr dnf -y install httpd:

buildah同样支持run命令,它不依赖容器运行时守护进程,而是通过执行runc直接在锁定的��器中执行命令。

buildah build-using-dockerfile -f Dockerfile .:

buildah同样支持使用Dockerfile来构建镜像。

我们希望将类似ansible-containers和OpenShift S2I这样的工具改成buildah,而非依赖一个容器运行时守护进程。

构建容器镜像和运行容器采用相同的运行时环境,对于生产环境还会遇到一个大问题,既针对容器安全需要同时满足二者的权限需求。通常构建容器镜像的时候需要远多于运行容器所需要的权限。例如,默认情况下我们会允许mknod能力。mknod能力允许进程能够创建设备节点,但是在生产环境几乎没有应用程序需要这个能力。在生产环境中移除mknod能力能够让系统变得更加安全。

另一个例子是我们会默认给容器镜像以读写权限,因为安装进程需要将软件包安装到/usr目录中。但是在生产环境中,我个人建议应该将所有容器运行在只读模式。容器中的进程应该只能允许写入tmpfs或者挂载到容器内的卷上。通过将构建镜像和运行容器分离,我们可以修改这些默认设置,让运行环境更加安全。

Kubernetes的运行时抽象:CRI-O

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/12978.html