跳转到主内容

与 Kubernetes 集群共享您的本地 podman 镜像

·9 分钟阅读
Florent Benoit
首席软件工程师

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

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

您会同意,在 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

通过这样做,我们还需要将 `/var/lib/containers` 文件夹从主机(Podman 机器)挂载到容器中。而在 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 && \

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 将使用 `latest` 标签在您的镜像上使用镜像拉取策略 `Always`。因此它可能会尝试获取/拉取/刷新您本地构建的镜像。使用特定标签或将您的部署中的 `imagePullPolicy` 更改为 `IfNotPresent`。

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

crictl images

结论

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

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

以下是 Podman Desktop 计划采取的下一步措施,以增强用户易用性

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