[k8s]-疑问:pod重新分配到同样的node上,pullpolicy是always,会存储两份相同的镜像吗?
开门见山,在 Kubernetes 中,当 Pod 被重新调度到同一个 Node 上,并且其容器的 imagePullPolicy
设置为 Always
时,通常是不会导致 Node 上存储两份完全相同的镜像文件。原因在于容器运行时(如 Docker, containerd)的镜像层管理机制。
-
imagePullPolicy: Always
的行为:- 这个策略告诉 kubelet(Node 上的代理),每次启动 Pod 中的容器之前,必须尝试从配置的容器镜像仓库(如 Docker Hub, 私有仓库)拉取指定的镜像标签(例如
myapp:latest
)。 - 关键点: 这个“拉取”操作不一定意味着完整下载整个镜像。运行时首先会与仓库通信,检查该镜像标签对应的清单(manifest),获取其唯一的内容摘要(Digest)。
- 这个策略告诉 kubelet(Node 上的代理),每次启动 Pod 中的容器之前,必须尝试从配置的容器镜像仓库(如 Docker Hub, 私有仓库)拉取指定的镜像标签(例如
-
容器运行时的镜像缓存机制:
- 当 kubelet 指令容器运行时(如 containerd)去拉取镜像时,运行时会执行以下步骤:
- 检查本地缓存: 查看本地是否已经存在一个镜像,其仓库名+标签名所指向的内容摘要(Digest) 与仓库中该标签当前指向的摘要完全一致。
- 摘要匹配(最常见的情况 - 镜像未更新):
- 如果本地已经存在一个镜像,其摘要与仓库中该标签的当前摘要完全相同,那么容器运行时不会下载任何新的数据层。
- 它会直接复用本地已有的镜像层。
- 在日志中,你可能会看到类似于
Image is up to date
或Image already exists
的消息,或者看起来像是在“下载”但实际上传输量为 0。运行时只是确认了本地镜像是最新的。
- 摘要不匹配(镜像在仓库中被更新了):
- 如果仓库中该标签指向的镜像内容发生了变化(即摘要不同),那么运行时就会开始真正下载新的镜像层。
- 新下载的镜像会作为一个新的镜像对象存储在本地。
- 旧的、被该标签之前引用的镜像并不会被自动删除。 它变成了一个“悬空镜像”(dangling image),不再被任何标签直接引用(但它可能还被旧版本的容器引用着,直到容器停止)。
- 当 kubelet 指令容器运行时(如 containerd)去拉取镜像时,运行时会执行以下步骤:
-
Pod 重新调度到同一个 Node 的场景演绎:
- 假设 Pod 第一次在该 Node 上创建时,成功拉取了镜像
myapp:latest
(摘要为sha256:abc123
)。 - 该镜像的层被存储在 Node 的本地缓存中。
- 后来 Pod 被删除(或失败重启),然后立即或在很短时间后被重新调度到同一个 Node。
- 新的 Pod 定义中,容器的
imagePullPolicy
仍是Always
,镜像名仍是myapp:latest
。 - kubelet 执行
Always
策略,指示运行时拉取myapp:latest
。 - 运行时联系仓库,发现
myapp:latest
标签当前指向的摘要仍然是sha256:abc123
。 - 运行时检查本地缓存,发现已经存在一个镜像摘要为
sha256:abc123
。 - 结果:运行时不会下载任何新数据。它会直接使用本地缓存的镜像层来启动新容器。Node 上仍然只有一份
myapp:latest
镜像(对应摘要sha256:abc123
)的数据。
- 假设 Pod 第一次在该 Node 上创建时,成功拉取了镜像
-
何时会产生两份镜像?
只有满足以下两个条件时,重新调度到同一个 Node 的 Pod 才可能导致 Node 上存储两份镜像数据:条件一:镜像在仓库中被更新了: 在 Pod 被删除和重新调度的间隙,仓库中的
myapp:latest
标签被推送(push)了一个内容不同的新镜像(比如由sha256:abc123
更新为sha256:def456
)。条件二:
imagePullPolicy: Always
: 强制运行时去检查仓库。- 此时,运行时发现仓库中的标签指向了新摘要
sha256:def456
,而本地只有旧摘要sha256:abc123
的镜像。 - 结果: 运行时会下载新镜像
sha256:def456
的所有层。Node 上现在会存储两份镜像数据:- 旧镜像:
myapp:latest
(摘要sha256:abc123
) - 现在变成了“悬空镜像”。 - 新镜像:
myapp:latest
(摘要sha256:def456
) - 被新 Pod 使用。
- 旧镜像:
- 此时,运行时发现仓库中的标签指向了新摘要
总结:
- 在
imagePullPolicy: Always
下,Pod 重新调度到同一个 Node 上,如果镜像在仓库中没有更新,容器运行时不会重复下载相同的镜像层,它会直接使用本地缓存。Node 上只有一份该镜像数据。 - 容器运行时通过比较镜像内容摘要 来决定是否需要下载新数据。
- 只有当仓库中的镜像标签被更新为指向一个内容不同的镜像时,
Always
策略才会导致在同一个 Node 上拉取并存储一份新的镜像数据,同时旧的镜像数据(如果未被清理)也会存在,形成两份。 - 悬空镜像会占用磁盘空间,需要定期清理(例如通过容器的垃圾回收机制或手动运行
docker image prune
/crictl rmi --prune
)。
**简单来说:Always
策略强制检查仓库是否有更新,但只会在内容确实更新时才下载新镜像。内容没更新就直接用本地的。