3. 镜像拉取认证原理:kubelet 如何通过 imagePullSecrets 与私有仓库交互

当 Pod 被调度到某个节点后,kubelet 会负责拉取 Pod 中定义的容器镜像。如果镜像存储在私有仓库(如 Harbor、Docker Registry、AWS ECR 等),kubelet 必须携带有效的认证凭据才能通过仓库的访问控制。这一过程的核心机制就是 imagePullSecrets。

3.1 认证流程的完整链路

从 Pod 创建到镜像拉取成功,kubelet 与私有仓库之间的认证交互遵循以下完整链路:

  1. Pod 提交到 API Server:用户或控制器创建 Pod,Pod 的 YAML 中可指定 imagePullSecrets 字段。
  2. API Server 持久化 Pod 对象:Pod 被写入 etcd,调度器将其绑定到某个节点。
  3. kubelet 从 API Server 获取 Pod 定义:节点上的 kubelet 通过 watch 机制收到 Pod 更新事件。
  4. kubelet 解析 imagePullSecrets:kubelet 读取 Pod 的 spec.imagePullSecrets 列表,获取 Secret 名称。
  5. kubelet 从 API Server 拉取 Secret 对象:kubelet 使用自身 ServiceAccount 的权限,向 API Server 请求对应命名空间下的 Secret。
  6. kubelet 解码 Secret 数据:Secret 的 .dockerconfigjson 字段包含 base64 编码的 Docker 配置(registry 地址、用户名、密码或 )。
  7. kubelet 构造 Docker 认证头:将解码后的凭据转换为 Authorization: Basic <base64(user:password)> 或 Bearer 。
  8. kubelet 向私有仓库发起镜像拉取请求:携带认证头,请求 GET /v2/<image>/manifests/<tag>。
  9. 私有仓库验证凭据:仓库检查认证头,若有效则返回镜像 manifest。
  10. kubelet 下载镜像层:通过认证后,kubelet 逐层拉取镜像 blob,完成拉取。
关键点:kubelet 不会缓存 Secret 的原始内容到本地磁盘,但会在内存中保留解析后的认证信息,用于后续镜像拉取重试。如果 Secret 被更新,kubelet 需要 Pod 重新调度或重启才能使用新凭据。

3.2 imagePullSecrets 的数据结构

imagePullSecrets 引用的 Secret 类型必须是 kubernetes.io/dockerconfigjson。其核心数据字段 .dockerconfigjson 的结构如下:

{
  "auths": {
    "https://index.docker.io/v1/": {
      "username": "myuser",
      "password": "mypassword",
      "auth": "bXl1c2VyOm15cGFzc3dvcmQ="
    },
    "harbor.example.com": {
      "username": "admin",
      "password": "Harbor12345",
      "auth": "YWRtaW46SGFyYm9yMTIzNDU="
    }
  }
}
字段 说明 是否必须
auths 仓库地址到凭据的映射 是
username 仓库登录用户名 推荐
password 仓库登录密码或 推荐
auth base64(username:password) 的预计算值 可选(kubelet 优先使用)
最佳实践:建议在创建 Secret 时使用 kubectl create secret docker-registry 命令,它会自动计算 auth 字段,避免手动 base64 编码错误。

3.3 kubelet 与私有仓库认证交互流程图

下图展示了 kubelet 从 Pod 定义中提取 imagePullSecrets,到最终与私有仓库完成认证的完整数据流:

kubelet 镜像拉取认证交互流程 Pod 定义 imagePullSecrets Secret 对象 .dockerconfigjson kubelet 内存 私有仓库 镜像 Manifest 说明: 1. kubelet 通过 API Server 获取 Pod 定义中的 imagePullSecrets 列表。 2. kubelet 使用自身 ServiceAccount 权限拉取对应的 Secret 对象。 3. 解码后的凭据暂存于 kubelet 内存,用于构造 HTTP 认证头向私有仓库发起请求。

3.4 认证失败常见原因

在完整链路中,任何一个环节出错都会导致 ErrImagePull 或 ImagePullBackOff。以下是各环节对应的典型故障:

环节 常见故障 错误特征
Pod 定义 imagePullSecrets 未指定或拼写错误 Pod 事件中无 Secret 引用记录
Secret 拉取 Secret 不存在或 kubelet 无权限访问 secret "xxx" not found
Secret 解码 .dockerconfigjson 格式错误或 base64 损坏 failed to decode docker config
认证头构造 auth 字段与 username/password 不一致 authentication required
仓库验证 凭据过期、仓库地址不匹配、 无效 unauthorized: access denied
注意:kubelet 在拉取镜像时,如果 Pod 没有指定 imagePullSecrets,但镜像仓库需要认证,kubelet 不会自动注入任何凭据。此时必须确保 Pod 显式引用正确的 Secret,或者将 Secret 添加到 Pod 的 ServiceAccount 中(通过 imagePullSecrets 字段)。

3.5 多仓库场景下的认证选择逻辑

当 Pod 引用多个 imagePullSecrets,或单个 Secret 中包含多个仓库凭据时,kubelet 的匹配逻辑如下:

  • 按仓库 URL 精确匹配:kubelet 从所有引用的 Secret 中提取 .dockerconfigjson,遍历 auths 下的 key,与镜像仓库地址进行字符串匹配。
  • 支持通配符:如果仓库地址以 https:// 开头,kubelet 会尝试匹配完整 URL;如果只写域名(如 harbor.example.com),则匹配该域名下的所有路径。
  • 第一个匹配优先:当多个 Secret 包含同一仓库的凭据时,kubelet 使用第一个匹配到的凭据。
  • 无匹配时使用匿名:如果所有 Secret 中都没有匹配的仓库地址,kubelet 会尝试匿名拉取(通常会被仓库拒绝)。
排查技巧:使用 kubectl get pod <pod-name> -o jsonpath='{.spec.imagePullSecrets}' 查看 Pod 实际引用的 Secret 列表;使用 kubectl get secret <secret-name> -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d 解码查看凭据内容是否包含目标仓库。