在资源受限或离线(隔离)环境中,镜像拉取速度慢会让人非常头疼,甚至可能导致 Kubernetes 的启动时间超过可接受范围。如今越来越多的 AI 应用依赖超大镜像(动辄几十 GB、上百 GB),这个问题更加突出。
本文将介绍 K3s 提供的一些机制,帮助用户在处理大型镜像时提升体验。
在线 & 离线场景的镜像处理策略 📦
K3s 提供了两类方法,分别应对在线和离线环境中拉取大镜像的需求:
- 在线集群:为了避免 Pod 启动时从外部镜像仓库拉取镜像过慢,K3s 可以通过一个清单文件提前
pre-pull镜像。 - 离线集群:在完全没有外网的场景下,K3s 可以直接从本地的镜像 tar 包中
import镜像。
1. 使用清单文件预拉取镜像(在线场景)
在可联网的场景下,目标是尽早并高效地启动镜像拉取。K3s 可以在启动时,或运行过程中,根据一个文本文件提前顺序拉取一组镜像,将其放入内置的 containerd 存储中。
这样可以确保基础镜像在集群启动或应用部署时已经就绪。
⚠️ 注意 如果你在 K3s 启动前就预拉取镜像、且镜像非常大,整个拉取过程超时(>15 分钟),K3s 可能会启动失败。因此,如果怀疑镜像过大,最好在 K3s 正在运行时再执行预拉取。
使用方法
在某个节点上创建目录并将镜像列表文件放进去,一行一个镜像名称。例如:
mkdir -p /var/lib/rancher/k3s/agent/images \
&& echo docker.io/pytorch/pytorch:2.9.0-cuda12.6-cudnn9-runtime \
> /var/lib/rancher/k3s/agent/images/pytorch.txt
当 K3s 发现该文件后,会通过 CRI API 开始拉取镜像,并在日志中打印类似内容:
# 检测到镜像清单文件
level=info msg="Pulling images from /var/lib/rancher/k3s/agent/images/example.txt"
level=info msg="Pulling image docker.io/pytorch/pytorch:2.9.0-cuda12.6-cudnn9-runtime"
# 拉取完成(包含耗时)
level=info msg="Imported docker.io/pytorch/pytorch:2.9.0-cuda12.6-cudnn9-runtime"
level=info msg="Imported images from ... in 6m1.178972902s"
2. 从镜像 Tar 包导入(离线 & 超高速)
在完全隔离或需要最高速度的场景下,建议将镜像保存为 tar 包,使 K3s 在本地直接加载这些镜像,而无需任何网络请求。
将通过 docker save 或 ctr save 创建的镜像压缩包放入 /var/lib/rancher/k3s/agent/images 目录即可。例如:
mkdir -p /var/lib/rancher/k3s/agent/images
cp microservices-demo.tar.gz /var/lib/rancher/k3s/agent/images/
K3s 进程会自动解压镜像 tar 包并载入镜像,日志如下:
level=info msg="Importing images from /var/lib/rancher/k3s/agent/images/microservices-demo.tar.gz"
level=info msg="Imported images ... in 1m39.8610592s"
你可以随时使用 K3s 内置的 ctr 客户端查看镜像:
k3s ctr images list
使用 tar 包优化 K3s 启动时间
默认情况下,K3s 在每次启动时都会重新导入镜像压缩包。对于非常大的镜像,这会明显拖慢启动速度。例如上面的 microservices-demo.tar.gz 导入一次就需要 1 分 39 秒。
为优化这一点,K3s 支持缓存功能,只在 tar 包发生变化时重新导入:
touch /var/lib/rancher/k3s/agent/images/.cache.json
这个 .cache.json 文件会记录镜像压缩包的元数据(如大小、修改时间)。下次启动时,如果文件没有变化,K3s 会跳过导入,显著缩短启动时间。因此,要检查此功能是否正常工作,请检查缓存文件 .cache.json 是否为空,并在重启后检查日志中是否不再出现 Importing images 相关的日志
⚠️ 提示 启用缓存后,如果某个镜像被删除或被清理,需要手动重新导入以恢复一致性。更多细节可查阅文档。
内置镜像加速器:Embedded Registry Mirror 🕸️
K3s 内置 Spegel 作为集群内的容器镜像缓存加速器。其主要作用是加速镜像拉取、减少外部网络依赖,使节点在可能的情况下从集群内的其他节点拉取已缓存的镜像内容,而不是每个节点都直接访问外部仓库。比如当节点 A 拉取了某个镜像后,节点 B 再拉取时可直接从节点 A 获取,而不是重复从外部仓库下载。
要启用 Spegel,需要在 server 节点使用 --embedded-registry 参数,或在配置文件中设置 embedded-registry: true。启用后,集群中的每个节点都会立即成为一个无状态、本地镜像镜像点,监听端口 6443。节点之间会通过端口 5001 的 P2P 网络持续同步可用镜像列表。
# 启用内嵌 registry mirror
embedded-registry: true
# 启用可帮助监控镜像镜像器的指标
supervisor-metrics: true
然后,你需要在所有节点上添加一个 registries.yaml,其中指定允许哪些 registry 在节点间同步镜像内容。
如果某个 registry 只在部分节点上启用,那么只有启用了该 registry 的节点才会互相交换镜像。
例如:
mirrors:
docker.io:
registry.k8s.io:
成功启动后,你将看到:
level=info msg="Starting distributed registry mirror ..."
level=info msg="Starting distributed registry P2P node ..."
可以通过 supervisor 的 metrics 端点查看 Spegel 相关指标:
kubectl get --server https://10.11.0.11:6443 --raw /metrics | grep spegel
更多详细信息请查看 K3s 文档。
另外,我们之前也详细介绍过 K3s 的嵌入式容器镜像共享机制,可参考:打造高效 Kubernetes 集群:深入解析 K3s 的嵌入式容器镜像共享机制
使用 eStargz 镜像 ⚡
另一种加速 Pod 创建的方法是使用一种特殊的镜像格式:eStargz。它支持延迟拉取(lazy pulling),意味着应用可以几乎立即启动,其余镜像内容会在后台继续拉取。
这种方式要求:
-
镜像必须以 eStargz 格式构建
-
K3s agent 必须配置使用 stargz snapshotter(
--snapshotter=estargz或配置文件中snapshotter: estargz)
这是 K3s 中的实验性功能,更多信息可在文档高级章节中找到。如果你正在使用它,我们非常希望听到你的反馈。
总结 🏁
面对如今动辄几十 GB 的云原生与 AI 镜像,K3s 提供了一系列实用而灵活的镜像加速能力,包括:
- 使用清单文件预拉取镜像
- 从 tar 包导入镜像(离线 & 超高速)
- 利用 cache 优化启动时间
- 启用内置的 Spegel 镜像加速器
- 使用 eStargz 格式实现 lazy pulling
这些机制帮助你将「慢速网络操作」转变为「快速本地操作」,让资源受限或离线环境下的 K3s 集群获得更快、更可控的启动体验。