在容器内运行 Buildah 的建议

将容器运行时解耦为单独的工具组件有什么好处? 特别是,这些工具可以开始组合起来,以便它们相互保护。

在容器内运行 Buildah 的建议

许多人被在其中构建容器化 OCI 镜像的想法所吸引 Kubernetes 或类似的系统。 假设我们有一个持续收集图像的 CI/CD,那么类似 红帽OpenShift/Kubernetes 在构建期间的负载平衡方面非常有用。 直到最近,大多数人只是简单地授予容器访问 Docker 套接字的权限,并允许它们运行 docker build 命令。 几年前我们展示了这是非常不安全的,事实上,它比给予无密码 root 或 sudo 更糟糕。

这就是为什么人们不断尝试在容器中运行 Buildah。 简而言之,我们创建了 例子 我们认为,最好如何在容器内运行 Buildah,并将相应的图像发布到 quay.io/buildah。 让我们开始吧...

调整

这些图像是从 Dockerfiles 构建的,可以在 Buildah 存储库的文件夹中找到 构建图像.
这里我们将考虑 Dockerfile 的稳定版本.

# stable/Dockerfile
#
# Build a Buildah container image from the latest
# stable version of Buildah on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=buildah
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM fedora:latest

# Don't include container-selinux and remove
# directories used by dnf that are just taking
# up space.
RUN yum -y install buildah fuse-overlayfs --exclude container-selinux; rm -rf /var/cache /var/log/dnf* /var/log/yum.*

# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf

我们使用容器内的程序,而不是在主机 Linux 内核级别实现的 OverlayFS 保险丝覆盖层,因为目前 OverlayFS 只能在您使用 Linux 功能为其授予 SYS_ADMIN 权限时才能挂载。 我们希望在没有任何 root 权限的情况下运行 Buildah 容器。 Fuse-overlay 工作速度相当快,并且比 VFS 存储驱动程序具有更好的性能。 请注意,运行使用 Fuse 的 Buildah 容器时,您必须提供 /dev/fuse 设备。

podman run --device /dev/fuse quay.io/buildahctr ...
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock

接下来我们创建一个目录用于额外存储。 容器/储存 支持连接附加只读图像存储的概念。 例如,您可以在一台机器上配置覆盖存储区域,然后使用 NFS 将该存储挂载到另一台机器上并使用其中的镜像,而无需通过拉取下载。 我们需要此存储,以便能够从主机连接一些图像存储作为卷并在容器内使用它。

# Set up environment variables to note that this is
# not starting with user namespace and default to
# isolate the filesystem with chroot.
ENV _BUILDAH_STARTED_IN_USERNS="" BUILDAH_ISOLATION=chroot

最后,通过使用 BUILDAH_ISOLATION 环境变量,我们告诉 Buildah 容器默认以 chroot 隔离运行。 这里不需要额外的隔热层,因为我们已经在容器中工作了。 为了让 Buildah 创建自己的命名空间分隔的容器,需要 SYS_ADMIN 权限,这需要放宽容器的 SELinux 和 SECCOMP 规则,这与我们从安全容器构建的偏好相反。

在容器内运行 Buildah

上面讨论的 Buildah 容器镜像图允许您灵活地改变启动此类容器的方法。

速度与安全

计算机安全始终是过程速度和周围保护程度之间的折衷。 在组装容器时,这种说法也是正确的,所以下面我们将考虑这种折衷的选择。

上面讨论的容器镜像将其存储在 /var/lib/containers 中。 因此,我们需要将内容挂载到这个文件夹中,而如何挂载将极大地影响构建容器镜像的速度。

让我们考虑三种选择。

1选项。 如果需要最大程度的安全性,那么对于每个容器,您可以为容器/图像创建自己的文件夹,并通过卷挂载将其连接到容器。 此外,将上下文目录放置在容器本身的 /build 文件夹中:

# mkdir /var/lib/containers1
# podman run -v ./build:/build:z -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable
buildah  -t image1 bud /build
# podman run -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable buildah  push  image1 registry.company.com/myuser
# rm -rf /var/lib/containers1

安全性。 在这样的容器中运行的 Buildah 具有最大的安全性:它不会被授予使用功能的任何 root 权限,并且所有 SECOMP 和 SELinux 限制都适用于它。这样的容器甚至可以通过添加像 —uidmap 0 这样的选项来使用用户命名空间隔离来运行: 100000:10000。

性能。 但这里的性能是最低的,因为每次来自容器注册表的任何图像都会复制到主机,并且缓存根本不起作用。 完成工作后,Buildah 容器必须将映像发送到注册表并销毁主机上的内容。 下次构建容器映像时,必须再次从注册表下载它,因为到那时主机上将不再有任何内容。

2选项。 如果需要Docker级别的性能,可以将主机容器/存储直接挂载到容器中。

# podman run -v ./build:/build:z -v /var/lib/containers:/var/lib/containers --security-opt label:disabled quay.io/buildah/stable buildah  -t image2 bud /build
# podman run -v /var/lib/containers:/var/lib/containers --security-opt label:disabled  quay.io/buildah/stable buildah push image2 registry.company.com/myuser

安全性。 这是构建容器的最不安全的方式,因为它允许容器修改主机上的存储,并可能向 Podman 或 CRI-O 提供恶意镜像。 此外,您需要禁用 SELinux 分离,以便 Buildah 容器中的进程可以与主机上的存储进行交互。 请注意,此选项仍然比 Docker 套接字更好,因为容器被剩余的安全功能锁定,并且不能简单地在主机上运行容器。

性能。 这里它是最大值,因为缓存已被充分使用。 如果 Podman 或 CRI-O 已经将所需的镜像下载到主机上,那么容器内的 Buildah 进程就不必再次下载,并且后续基于该镜像的构建也能够从缓存中获取所需的内容。

3选项。 这种方法的本质是将多个镜像合并到一个项目中,并使用一个容器镜像的公共文件夹。

# mkdir /var/lib/project3
# podman run --security-opt label_level=s0:C100, C200 -v ./build:/build:z 
-v /var/lib/project3:/var/lib/containers:Z quay.io/buildah/stable buildah  -t image3 bud /build
# podman run --security-opt label_level=s0:C100, C200 
-v /var/lib/project3:/var/lib/containers quay.io/buildah/stable buildah push image3  registry.company.com/myuser

在此示例中,我们不会在运行之间删除项目文件夹 (/var/lib/project3),因此项目内的所有后续构建都会受益于缓存。

安全性。 介于选项 1 和 2 之间。一方面,容器无权访问主机上的内容,因此无法将不良内容放入 Podman/CRI-O 镜像存储中。 另一方面,作为其设计的一部分,容器可能会干扰其他容器的组装。

性能。 这里比在主机级别使用共享缓存更糟糕,因为您无法使用已使用 Podman/CRI-O 下载的映像。 但是,一旦 Buildah 下载了映像,该映像就可以在项目内的任何后续构建中使用。

额外存储空间

У 容器/存储 有一个很酷的东西,叫做附加存储(additional store),通过它,在启动和构建容器时,容器引擎可以以只读覆盖模式使用外部镜像存储。 本质上,您可以将一个或多个只读存储添加到 storage.conf 文件中,以便在启动容器时,容器引擎在其中查找所需的镜像。 此外,仅当在任何这些存储中都找不到该映像时,它才会从注册表下载该映像。 容器引擎只能写入可写存储......

如果您向上滚动并查看我们用于构建镜像 quay.io/buildah/stable 的 Dockerfile,会发现如下几行:

# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock

在第一行中,我们修改容器映像内的 /etc/containers/storage.conf ,告诉存储驱动程序使用 /var/lib/shared 文件夹中的“additionalimagestores”。 在下一行中,我们创建一个共享文件夹并添加几个锁定文件,以便容器/存储不会被滥用。 本质上,我们只是创建一个空的容器镜像存储。

如果您将容器/存储挂载到高于此文件夹的级别,Buildah 将能够使用这些映像。

现在让我们回到上面讨论的选项 2,当 Buildah 容器可以读取和写入主机上的容器/存储时,由于在 Podman/CRI-O 级别缓存图像,因此具有最大性能,但提供了最低限度的安全性,因为它可以直接写入存储。 现在,让我们在这里添加额外的存储空间,并获得两全其美的效果。

# mkdir /var/lib/containers4
# podman run -v ./build:/build:z -v /var/lib/containers/storage:/var/lib/shared:ro -v  /var/lib/containers4:/var/lib/containers:Z  quay.io/buildah/stable 
 buildah  -t image4 bud /build
# podman run -v /var/lib/containers/storage:/var/lib/shared:ro  
-v >/var/lib/containers4:/var/lib/containers:Z quay.io/buildah/stable buildah push image4  registry.company.com/myuser
# rm -rf /var/lib/continers4

请注意,主机的 /var/lib/containers/storage 以只读模式挂载到容器内的 /var/lib/shared 。 因此,在容器中工作时,Buildah 可以使用之前使用 Podman/CRI-O 下载的任何镜像(hello、speed),但只能写入自己的存储(hello、security)。 另请注意,这是在不禁用容器 SELinux 分离的情况下完成的。

重要的细微差别

在任何情况下都不应从底层存储库中删除任何图像。 否则,Buildah 容器可能会崩溃。

而这些还不是全部优点

附加存储的可能性并不限于上述场景。 例如,您可以将所有容器映像放置在共享网络存储上,并授予所有 Buildah 容器访问它的权限。 假设我们的 CI/CD 系统经常使用数百个镜像来构建容器镜像。 我们将所有这些镜像集中在一台存储主机上,然后使用首选的网络存储工具(NFS、Gluster、Ceph、ISCSI、S3...),向所有 Buildah 或 Kubernetes 节点开放对此存储的一般访问。

现在只需将此网络存储安装到 /var/lib/shared 上的 Buildah 容器中就足够了 - Buildah 容器不再需要通过 pull 下载镜像。 因此,我们抛弃了预填充阶段并立即准备好推出容器。

当然,这可以在实时 Kubernetes 系统或容器基础设施中使用,以便在任何地方启动和运行容器,而无需任何镜像下载。 此外,容器注册表收到上传更新镜像的推送请求后,可以自动将该镜像发送到共享网络存储,并立即可供所有节点使用。

容器映像的大小有时可以达到许多千兆字节。 附加存储的功能使您可以避免跨节点克隆此类映像,并使启动容器几乎是即时的。

此外,我们目前正在开发一项称为覆盖卷安装的新功能,这将使构建容器变得更快。

结论

在 Kubernetes/CRI-O、Podman 甚至 Docker 中的容器内运行 Buildah 是可行、简单的,并且比使用 docker.socket 更安全。 我们极大地提高了处理图像的灵活性,因此您可以通过多种方式运行它们,以优化安全性和性能之间的平衡。

附加存储的功能使您可以加快甚至完全消除将图像下载到节点的速度。

来源: habr.com

添加评论