利用Img构建容器镜像


Img是一个开源项目,由该领域最著名的软件工程师之一Jessie Frazelle发起,旨在满足对无守护程序和无root权限构建容器镜像的需求,尤其是在Kubernetes中。她的博客文章《在Kubernetes上安全地构建容器镜像》对该项目做了描述。

Img实际上是对另一个叫做BuildKit的开源的构建技术的封装,该技术以库的形式嵌入在Img中。 BuildKit是一个功能强大的多功能构建引擎,它源自于Docker制定的Moby项目。在研究BuildKit提供的构建功能之前,让我们看一下Img的功能。

Img

Img直接面向那些熟悉Dockerfile的开发者 ,以docker build命令作为构建OCI兼容容器镜像的方法。

Img CLI

从UX的角度看,Img提供了一组命令,这些命令模仿容器镜像构建,分发和操作相关的Docker CLI命令。除了镜像构建,Img还具有与容器镜像仓库进行交互的命令, img login、img pull、img push等。与Docker一样,在本地缓存中处理镜像时,可以列出,删除,标记和保存镜像。甚至还有一些管理命令方便日常操作,比如用于监视磁盘使用情况。

对于镜像构建,Img build命令照搬了docker build命令所提供的最重要的子集,从而为熟悉Docker镜像制作者带来无缝的体验。使用Img构建镜像就像将“docker”替换为“img”一样简单。
$ img build --build-arg VERSION=1.0 -t mycorp/myapp .

在这方面,Img与Podman(我们在上一篇文章中讨论过)相似,但仅限于镜像构建任务,而Podman可用于构建镜像,运行容器等。

构建状态

那些使用Docker守护程序构建镜像的镜像作者无需考虑镜像在本地的存储位置,这项任务由Docker守护程序处理。如果没有守护程序(如Img)负责存储,则镜像制作者就要考虑相关问题。 Img将镜像存储在名为img的目录中,该目录位于为用户设置的XDG_DATA_HOME变量的值下;如果未设置XDG_DATA_HOME,则存放在$ HOME / .local / share;如果未设置XDG_DATA_HOME或HOME变量,则将在/ tmp存储。该位置是可配置的,但要记住的是,如果将Img以容器化状态运行,则需要将“state”目录作为容器的可用卷。构建的镜像和构建缓存存放在卷中,以备后续的Img调用。

无根构建

以非特权用户身份构建镜像是Img的主要目标,因此需要对各种相关项目打上安全补丁。其中包括在容器中运行无根构建和在Kubernetes Pod中运行无根构建。

Img使用用户命名空间来实现无根构建,但是在使用overlayfs时(Ubuntu发行版除外)受限,即目前缺乏对非特权用户命名空间中挂载的支持。 Overlayfs通常是用于组装容器文件系统的最佳联合挂载方法,由于Img不支持非特权用户命名空间,因此Img必须依靠复制文件来进行复制,效率非常低。

尽管有此限制,Img仍实现了提供无根构建的目标,同时又利用了BuildKit提供的一些高级构建功能。

BuildKit

BuildKit可以被描述为下一代容器镜像构建引擎,并且可以将其视为构建过程中的“后端”组件。正如我们将在以后的文章中所发现的那样,它已被合并到最新版本的Docker引擎中,但它是一个独立的项目,可以独立于Docker使用。Img证明了这一点,但BuildKit并不止提供技术。实际上,BuildKit与技术无关,它可以在用“前端”表示的任何域中进行构建步骤,该“前端”将步骤转换为BuildKit的内部表示形式(In fact, BuildKit is technology agnostic and can be used to perform build steps for any domain that can be expressed using a ‘frontend’ that converts the steps into BuildKit’s internal representation.)。那么,BuildKit有何特别之处?它有许多创新功能, Img只用到一部分功能。

并行构建

Moby项目在Docker 17.05版引擎中引入了多阶段构建,从而可以将镜像构建分离到单独的逻辑阶段。多阶段构建对镜像作者来说是一个巨大的好处,但是在利用Docker守护进程构建的旧引擎中执行构建步骤的顺序性,意味着无关的构建阶段无法从并行执行各自的构建步骤中受益。BuildKit来解围!
parallel-build-execution.png

BuildKit将构建步骤的内部表示形式组装为有向非循环图(DAG),这使它能够确定哪些构建步骤可以并行。对于包含多个构建阶段的Dockerfile,这可以显着减少构建完成的时间。对于Img使用者来说,这有明显的优势,因为镜像制作者无需具备相关知识。

跨平台/系统构建

Img通过其用户界面展现了BuildKit另外一个功能——跨平台和跨OS构建。使用此功能时,虽然有一些额外的注意事项,但可以在完全不同的平台上为不同的架构和操作系统的组合构建镜像。例如,可以使用Linux / amd64平台构建适合Linux / arm64的镜像。该功能由—platform参数实现。

Img和Kubernetes

在Kubernetes上安全地构建镜像为Img提供了动力,容器镜像可以在Kubernetes的Pod中被构建。该镜像的Dockerfile也可用于审查所需,并且也可以用作创建定制镜像的基础。

对于镜像制作的主要贡献是:
  1. 构建镜像无需特权账户
  2. 下级uid / gid文件用于为非特权用户定义用户命名空间映射
  3. 二进制文件newuidmap和newgidmap,用于对每次在构建步骤中运行容器时创建的用户命名空间进行映射


使用Img在Kubernetes上运行无根构建时,不同的层(layer)在参与工作。构建步骤在Img容器内嵌套的BuildKit worker容器中执行。 Img容器很可能在Docker容器运行时下运行,所有这些都由Kubernetes容器运行时接口(CRI)管理。安全地消除各层特权执行的约束这一任务并非易事,那么存在一些限制也就不足为奇了。

目前,尽管仍可以使用AppArmor和Seccomp来保护容器,但AppArmor和Seccomp配置文件无法应用于执行无根构建的Img pod。 Pod中的Img容器还需要对其/ proc文件系统具有“unmasked”的访问权限,对于从1.12开始的Kubernetes版本,可以使用Pod安全特性securityContext.procMount来实现。这需要设置为Unmasked。

让Img pod能追踪不断变化的构建状态是在Kubernetes中使用Img进行构建的现实需求。这样,对Img的分开调用就可以利用本地存储区中构建的镜像和缓存层。例如,构建的镜像可能需要作为CI / CD工作流程的一部分被推送到镜像仓库,并且构建迭代肯定会受益于重用存储在缓存中的先前构建的层。使用Kubernetes(持久)卷抽象,可以使构建状态对Img pod可用。

结论

很难判断Img的普及程度或者它将会多么成功,因为它没有像许多开源项目一样有利益团体的支持。但是该项目有助于打破运行无根容器的障碍。

为Img所使用的构建引擎BuildKit具有许多Img当前未公开的功能。如果这些附加功能及时进入Img的用户界面,它将成为构建容器镜像的利器——特别是在Kubernetes环境中进行无根构建。

原文链接:Building Container Images with Img(翻译:吴世曦)

0 个评论

要回复文章请先登录注册