2. Docker 与 Kubernetes 中的镜像拉取机制

理解镜像拉取流程是设计 Secrets 加密传输方案的前提。Docker 作为容器运行时,其 docker pull 流程决定了镜像层如何从 Registry 传输到本地;而 Kubernetes 中的 kubelet 则在此基础上增加了认证、镜像拉取策略(ImagePullPolicy)和 Secrets 挂载等控制逻辑。本章将分别拆解这两个流程,并对比其关键差异。

2.1 Docker pull 流程详解

docker pull 命令的执行过程可划分为 5 个阶段:认证、Manifest 获取、层校验、并行下载、解压与索引更新。下图展示了完整的请求链路:

Docker pull 请求链路 docker CLI Docker Engine Registry HTTP POST /images/create GET /v2/…/manifests/ ① 认证(Auth) ② 获取 Manifest ③ 层校验(Digest) ④ 并行下载 Blobs ⑤ 解压 & 索引更新 关键说明: • 认证通过后,Registry 返回 Bearer ,后续请求携带该 。 • Manifest 包含镜像层列表及每个层的 digest(SHA256),用于完整性校验。

2.1.1 各阶段详细说明

阶段 触发条件 核心操作 涉及协议/数据
① 认证 Registry 返回 401 Unauthorized 向 Auth Service 请求 Bearer HTTP Basic / OAuth2
② 获取 Manifest 验证通过 请求 /v2/<name>/manifests/<tag> Docker V2 Manifest (JSON)
③ 层校验 Manifest 返回后 比对本地已有层的 digest,确定需下载的层 SHA256 摘要
④ 并行下载 确定缺失层列表 并发 HTTP GET 请求 /v2/<name>/blobs/<digest> 分块传输 / 断点续传
⑤ 解压 & 索引 所有层下载完成 解压 tar.gz 层,更新本地镜像索引 overlay2 / aufs 存储驱动
优化切入点: 阶段④的并行下载是网络 I/O 最密集的环节。若 Registry 返回的 Manifest 或 Blob 数据被篡改,将直接威胁镜像完整性。Secrets 加密传输方案应重点保护 Manifest 和 Blob 的传输通道。

2.2 kubelet 拉取镜像流程

Kubernetes 中,kubelet 负责为 Pod 拉取容器镜像。与 docker pull 不同,kubelet 的流程增加了 Pod 级别的认证管理 和 ImagePullPolicy 控制。下图展示了 kubelet 从 Pod 调度到镜像就绪的完整路径:

kubelet 拉取镜像流程 API Server kubelet 容器运行时 Watch / Pod 更新 CRI 接口调用 ① 解析 Pod Spec ② 读取 imagePullSecrets ③ 判断 ImagePullPolicy ④ 构造 Registry 认证 ⑤ 调用运行时 PullImage ⑥ 更新 Pod 状态 关键差异点: • kubelet 不直接与 Registry 交互,而是通过 CRI 调用容器运行时(如 containerd、CRI-O)。 • imagePullSecrets 以 Secret 资源形式挂载到 kubelet,支持 dockerconfigjson 格式。 • ImagePullPolicy 决定是否每次拉取:Always / IfNotPresent / Never。 • 若使用私有 Registry,kubelet 必须持有有效的 dockerconfigjson,否则拉取失败。

2.2.1 ImagePullPolicy 决策逻辑

kubelet 在步骤③中根据 ImagePullPolicy 决定是否发起拉取。以下是三种策略的行为对比:

策略 行为 适用场景 网络请求次数
Always 每次启动 Pod 都拉取最新镜像 开发环境、使用 :latest 标签 每次 Pod 启动至少 1 次
IfNotPresent 仅当本地不存在该镜像时拉取 生产环境、使用固定版本标签 仅首次部署或镜像被清理时
Never 从不拉取,仅使用本地镜像 离线环境、预置镜像场景 0 次

2.2.2 imagePullSecrets 的传递路径

Secrets 从创建到被 kubelet 使用的完整路径如下:

  1. 用户创建 Secret:kubectl create secret docker-registry <name> --docker-server=<server> --docker-username=<user> --docker-password=<pass>
  2. Secret 存储于 etcd:API Server 将 Secret 以 Base64 编码存入 etcd,默认未加密。
  3. Pod 引用 Secret:在 Pod Spec 的 spec.imagePullSecrets 字段中引用 Secret 名称。
  4. kubelet 读取 Secret:kubelet 通过 API Server 的 Watch 机制获取 Pod 更新,同时请求关联的 Secret 资源。
  5. 构造 dockerconfigjson:kubelet 将 Secret 中的 .dockerconfigjson 数据传递给容器运行时。
  6. 运行时认证:容器运行时(如 containerd)使用该凭证向 Registry 发起认证请求。
安全风险提示: 默认情况下,etcd 中的 Secret 以明文 Base64 存储。若 etcd 被攻破或备份泄露,所有 Registry 凭证将暴露。这正是后续章节引入 Secrets 加密传输方案的核心动因。

2.3 Docker pull 与 kubelet 拉取流程对比

下表从认证管理、触发方式、并发控制、安全边界四个维度对比两种拉取机制:

对比维度 Docker pull kubelet 拉取
认证来源 本地 ~/.docker/config.json Pod 关联的 imagePullSecrets
触发方式 用户手动执行命令 Pod 调度 / 重启 / ImagePullPolicy 触发
并发下载 Docker Engine 内部并发(默认 3 个 goroutine) 由容器运行时控制(如 containerd 支持并发层下载)
凭证生命周期 持久化在本地文件,长期有效 随 Pod 生命周期,可动态更新
安全边界 依赖本地文件权限(0600) 依赖 RBAC + etcd 存储安全
镜像缓存 本地镜像层缓存,全局共享 节点级缓存,由容器运行时管理

2.4 流程中的安全薄弱点

基于上述流程分析,可以识别出以下与 Secrets 相关的安全薄弱点:

  • etcd 存储未加密:imagePullSecrets 在 etcd 中仅 Base64 编码,无加密保护。
  • kubelet 与 API Server 通信:虽然使用 TLS,但若证书管理不当,存在中间人攻击风险。
  • 容器运行时与 Registry 通信:默认使用 HTTPS,但部分私有 Registry 可能降级为 HTTP。
  • Secrets 在节点上的临时缓存:kubelet 可能将 Secrets 缓存在内存或磁盘中,若节点被入侵,凭证可能泄露。
  • 镜像拉取日志泄露:kubelet 日志可能包含 Registry URL 和认证头信息。
本章小结: Docker pull 是单机手动操作,kubelet 拉取是集群级自动化流程。两者都依赖 Registry 认证,但 kubelet 的凭证管理更复杂,涉及 etcd、API Server、RBAC 等多个组件。后续章节将针对这些薄弱点,设计端到端的 Secrets 加密传输方案。