跳至主要内容

将本地 Podman 镜像与 Kubernetes 集群共享

·阅读时长 8 分钟
Florent Benoit
首席软件工程师

作为开发者,我们不断改进和完善我们的应用程序。我们在使用容器镜像和 Kubernetes 部署/Pod 时面临的挑战之一是快速迭代。

例如,当我们想在 Kubernetes Pod 中尝试一个新的镜像时,这个镜像需要在私有/公共注册表或 Kubernetes 集群的节点上可用。有时我们需要调用额外的命令,例如 `kind load docker-image` 或 `minikube cache add <image>`,或者先将镜像发布到第三方注册表。

您会同意,在 Kubernetes Pod 中尝试新镜像应该与构建镜像本身一样无缝。

在这篇博文中,我们将探索使用 Podman Desktop 简化 Kubernetes 中镜像迭代流程的最佳实践。

hero


介绍

当使用 kind 或 minikube 或其他第三方工具来设置本地 Kubernetes 集群时,我们有几种方法可以发布镜像。

Minikube 在 https://minikube.cn/docs/handbook/pushing/ 上发布了 8 种执行此操作的方法。

每种方法都有其优缺点。使用第三方注册表意味着您需要在每次构建镜像后发布镜像,才能在 Kubernetes 集群中使用它。虽然 Podman Desktop 可以自动化本地注册表(您在其中执行 `podman build`)和第三方注册表之间的同步,但在本地和第三方注册表之间仍然存在层级的重复。如果更改第一层,则可能需要花费很长时间才能再次发送所有数据。

加载镜像需要打包/解包文件,因此不适合大型镜像。

Minikube 提供了 Podman 环境,但需要注意的是,它在 Podman 机器内部的容器中使用了 3.4 版本。这意味着存在两个 Podman 实例。

3.4 版本已经过时,不提供对 compose、与 Docker REST API 的兼容性以及第三方工具的最新增强和支持。

我们能否直接构建镜像并在 Kubernetes 中使用它?

podman 和 kubernetes/cri-o

在 Kubernetes 世界中,我们需要一个容器引擎运行时。在早期阶段,容器运行时与 docker、rkt 或其他运行时之上的临时解决方案集成在一起。

但为了分离关注点并使其可扩展,添加了一个新接口:CRI,即“容器运行时接口”。使用 CRI 接口,我们可以插入容器引擎。并且有一些运行时,例如 containerd、cri-o 等。 https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/container-runtime-interface.md

对我们来说,cri-o 项目很有意思。这个项目实现了 CRI 接口,但也采用了 containers 组织中的一些项目,其中 podmanpodman-desktop 存在。

这意味着 cri-o 使用 https://github.com/containers/image 项目的镜像管理,并使用 https://github.com/containers/storage 项目处理存储。

这对 Podman 用户来说非常有趣。由于它使用 cri-o 和 podman 之间的通用库,这意味着在同一个环境中,podman 和 cri-o 会在 ` /var/lib/containers` 文件夹中的公共位置读写镜像。

如果我们更进一步,将 podman 的 ` /var/lib/containers` 文件夹挂载到 cri-o 容器中,这意味着 Kubernetes 集群可以找到 podman 机器正在构建的镜像。

等等,这意味着不需要再使用注册表,需要额外的步骤吗?是的,只需要构建和加载就可以了。

Minikube 救援

虽然我们的目标是同时使用 cri-o 和 podman,但我们可以探索当前的项目,使我们能够快速设置 Kubernetes 集群。

kind

在 ` kind` 方面,存在一个使用 containerd 的默认配置,并且没有计划支持 cri-o 等替代方案。 https://github.com/kubernetes-sigs/kind/issues/1369#issuecomment-867440704

也就是说,有些人试图维护一种方法来做到这一点,但这并不是官方方法。 https://gist.github.com/aojea/bd1fb766302779b77b8f68fa0a81c0f2

这样做,我们还需要将主机(podman 机器)的 ` /var/lib/containers` 文件夹挂载到容器中。在 kind 中没有简单的标志。

Minikube

Minikube 选项

Minikube 支持更多选项,并提供了一种选择容器引擎运行时的方法。它还包括对 cri-o 的支持。

我们可以使用 ` container-runtime` 参数并请求 ` cri-o` 运行时。命令行应包含 ` --container-runtime=cri-o`。

然后,我们在容器内部拥有 podman/cri-o 环境,可以使用 ` eval $(minikube podman-env)` 使用 Windows/macOS podman CLI。

一个问题是,我们现在有两个“Podman 引擎”:一个在 Podman 机器内部运行,另一个在容器内部运行。容器中包含的 Podman 使用的是 3.4 版本,因为它在 Debian/Ubuntu 稳定版中,而最新版本的 Podman 是 4.7.x。

我们能否将 podman 机器 ` /var/lib/containers` 挂载到容器中?

是的!minikube 提供了一些选项,可以使用 ` --mount-string` 参数进行额外的挂载。这不是很明显,但您还需要在该参数之外添加 ` --mount`。完整参数是 ` --mount --mount-string "<host-path:container-path>"`

但 ` /var` 已经是挂载的文件夹了。所以这里的想法是更改 cri-o 存储数据的路径。

因此,我们可以提供一个自定义挂载的路径,让 cri-o 使用该自定义位置。让我们选择 ` /host-containers`。

启动 minikube 时,我们需要添加 ` --mount --mount-string "/var/lib/containers:/host-containers"`。

关于 cri-o 的配置,目前无法使用 Minikube 选项来实现。但是,Minikube 提供了修改基础镜像的灵活性。

Minikube kicbase 镜像

让我们自己创建一个名为 kicbase 镜像的基础镜像。

Minikube 包含一个用于 cri-o 的默认配置文件。 https://github.com/kubernetes/minikube/blob/v1.32.0/deploy/kicbase/02-crio.conf

我们需要更改此默认配置,使其说明 cri-o 需要使用另一个目录来存储镜像。此新目录 ` /host-containers` 将从 podman 机器内的 ` /var/lib/containers` 文件夹挂载。这就是 cri-o 能够看到 podman 镜像的方式。

让我们将配置部分包含在此文件中。

[crio]
root = "/host-containers/storage"
runroot = "/host-containers/storage"

让我们还通过在 Dockerfile 中添加必要的指令来升级容器中的 Podman。

Dockerfile 来自 https://github.com/kubernetes/minikube/blob/v1.32.0/deploy/kicbase/Dockerfile#L178-L186

在文件中,替换

RUN clean-install podman && \

with

RUN sh -c "echo 'deb https://downloadcontent.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_22.04/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" && \
curl -LO https://downloadcontent.opensuse.org/repositories/devel:kubic:libcontainers:unstable/xUbuntu_22.04/Release.key && \
apt-key add - < Release.key && \
# need to add dbus-user-session else we have
# cannot open sd-bus: No such file or directory: OCI runtime attempted to invoke a command that was not found
clean-install dbus-user-session podman && \

让我们重建镜像并发布它。您可以在 ` quay.io/fbenoit/kicbase:multiarch-2023-11-06` 中找到它。要构建镜像,请克隆 https://github.com/kubernetes/minikube 存储库,并编辑之前引用的文件。

构建 kicbase 镜像的命令是 ` make local-kicbase`。

尝试使用 podman 机器存储文件夹的 cri-o

在撰写这篇博文时,使用了版本 ` v1.32.0-beta.0`。对于不同的版本,您可能需要构建自己的 kicbase 镜像。

一个重要说明:cri-o 在 root 模式下运行,这就是为什么我们将它挂载到 ` /var/lib/containers`(然后在 rootful 模式下)的原因。

为了简单起见,让我们使用一个 rootful podman 机器,将同一个文件夹映射到两个位置。

好的,现在让我们分两步尝试

  1. 创建一个 podman 机器
podman machine init --cpus 4 --memory 6000 --rootful
  1. 使用我们的 kicbase 镜像启动集群

我们指定 Podman 作为驱动程序(默认值为 Docker),表明我们希望使用 cri-o 作为容器运行时而不是 containerd,并使用我们的自定义镜像,该镜像执行 Podman 版本的更新以及 cri-o 配置的修改以使用不同的存储文件夹。最后,我们指定了一个额外的挂载。

minikube start --driver=podman --container-runtime=cri-o --base-image=quay.io/fbenoit/kicbase:multiarch-2023-11-06 --mount --mount-string "/var/lib/containers:/host-containers"

验证

假设集群能够启动,则在 $HOME/.kube/config 文件中配置了一个新的 Kubernetes 集群。

使用 kubectl,我们可以启动一个部署

kubectl apply -f https://k8s.io/examples/application/deployment.yaml

你可以使用以下命令检查 Pod 是否正在运行

kubectl get pods -l app=nginx

如果你检查你的 Podman 镜像

podman images

你将看到 nginx 被列出,因此镜像是共享的。

现在,你可以使用 Containerfile 构建镜像或拉取镜像,连接到 Podman Desktop 中的控制平面实例(在 minikube 容器中打开一个 shell),并运行

crictl images

它将列出 Podman 的镜像

注意:默认情况下,Kubernetes 将使用 Always 镜像拉取策略,使用镜像上的 latest 标签。因此,它可能会尝试获取/拉取/刷新你本地构建的镜像。在你的部署中使用特定的标签或将 imagePullPolicy 更改为 IfNotPresent

你现在可以使用自己的 Containerfile/Dockerfile 并使用 podman build 命令构建镜像。然后通过列出镜像来检查该镜像是否也存在于 Kubernetes 集群中。

crictl images

结论

我们已经探讨了开发人员如何通过无缝集成 Podman 和 Kubernetes 来显著缩短周转时间。

现在,让我们尝试一下,并通过 Podman Desktop 问题追踪器 https://github.com/containers/podman-desktop/issues/ 提供反馈。

以下是 Podman Desktop 计划采取的下一步措施,以提高用户的使用便捷性

  • 为了简化操作,请通过添加此设置到创建向导来自动化流程。
  • 与上游 Minikube 项目合作,简化选择并取消对自定义 kicbase 镜像的需求。
  • 增强解决方案的整体用户友好性,以改善开发人员体验。