DEVOPS: K8S 拉取镜像的那些事

概述

  • 相比 K8s 集群的其他功能,私有镜像的自动拉取,看起来可能是比较简单的
  • 而镜像拉取失败,大多数情况下都和权限有关
  • 所以,在处理相关问题的时候,我们往往会轻松的说:这问题很简单,肯定是权限问题
  • 但实际的情况是,我们经常为一个问题,花了多个人的时间却找不到原因
  • 这主要还是我们对镜像拉取,特别是私有镜像自动拉取的原理理解不深
  • 顺序上来说,私有镜像自动拉取会首先通过阿里云 Acr credential helper 组件
  • 再经过 K8s 集群的 API Server 和 kubelet 组件,最后到 docker 容器运行时
  • 但是我们从后往前,从最基本的 docker 镜像拉取说起

镜像拉取这件小事

  • 为了讨论方便,我们来设想一个场景

  • 很多人会使用网盘来存放一些文件,像照片,文档之类

  • 当我们存取文件的时候,我们需要给网盘提供账户密码,这样网盘服务就能验证我们的身份

  • 这时,我们是文件资源的所有者,而网盘则扮演着资源服务器的角色

  • 账户密码作为认证方式,保证只有我们自己可以存取自己的文件

  • 这个场景足够简单,但很快我们就遇到新需求:我们需要使用一个在线制作相册的应用

  • 按正常的使用流程,我们需要把网盘的照片下载到本地,然后再把照片上传到电子相册,这个过程是比较很繁琐的

  • 我们能想到的优化方法是,让相册应用,直接访问网盘来获取我们的照片,而这需要我们把用户名和密码授权给相册应用使用

  • 这样的授权方式,优点显而易见,但缺点也是很明显的:我们把网盘的用户名密码给了相册服务,相册服务就拥有了读写网盘的能力,从数据安全角度,这个是很可怕的

  • 其实这是很多应用都会遇到的一个一般性场景,私有镜像拉取其实也是这个场景

  • 这里的镜像仓库,就跟网盘一样,是资源服务器,而容器集群则是三方服务,它需要访问镜像仓库获取镜像

理解 OAuth 2.0 协议

  • OAuth 协议是为了解决上述问题而设计的一种标准方案,我们的讨论针对 2.0版本

  • 相比把账户密码直接给三方应用,此协议采用了一种间接的方式来达到同样的目的

  • 如下图,这个协议包括六个步骤,分别是三方应用获取用户授权,三方应用获取临时 Token 以及三方应用存取资源

  • 这六步理解起来不容易,主要是因为安全协议的设计,需要考虑协议的易证明性,所以我们换一种方式来解释这个协议。简单来说,这个协议其实就做了两件事情:

    • 在用户授权的情况下,三方应用获取 token 所表示的临时访问权限
    • 然后三方应用使用这个 token 去获取资源
  • 如果用网盘的例子来说明的话,那就是用户授权网盘服务给相册应用创建临时 token,然后相册应用使用这个 token 去网盘服务获取用户的照片

  • 实际上 OAuth 2.0 各个变种的核心差别,在于第一件事情,就是用户授权资源服务器的方式

  1. 最简单的一种,适用于三方应用本身就拥有被访问资源控制权限的情况。这种情况下,三方应用只需要用自己的账户密码登录资源服务器并申请临时token 即可;

  2. 当用户对三方应用足够信任的情况下,用户直接把账户密码给三方应用,三方应用使用账户密码向资源服务器申请临时 token;

  3. 用户通过资源服务器提供的接口,登录资源服务器并授权资源服务器给三方应用发放 token;

  4. 完整实现 OAuth 2.0 协议,也是最安全的。三方应用首先获取以验证码表示的用户授权,然后用此验证码从资源服务器换取临时 token,最后使用 token 存取资源

从上面的描述我们可以看到,资源服务器实际上扮演了鉴权和资源管理两种角色,这两者分开实现的话,协议流程会变成下图这样

Docker 扮演的角色

1 )大图

  • 镜像仓库 Registry 的实现,目前使用“把账户密码给三方应用”的方式

  • 即假设用户对 Docker 足够信任,用户直接将账户密码交给 Docker,然后 Docker 使用账户密码跟鉴权服务器申请临时 token

2 ) 理解 docker login

  • 首先,我们在拉取私有镜像之前,要使用 docker login 命令来登录镜像仓库
  • 这里的登录其实并没有和镜像仓库建立什么会话之类的关系。登录主要就做了三件事情:
    • 第一件事情是跟用户要账户密码。
      • 如下图,当执行登录命令,这个命会提示输入账户密码
      • 这件事情对应的是大图的第一步


    • 第二件事情,docker 访问镜像仓库的 https 地址,并通过挑战 v2 接口来确认
      • 接口是否会返回 Docker-Distribution-Api-Version 头字段
      • 这件事情在协议图中没有对应的步骤。它的作用跟 ping 差不多
      • 只是确认下 v2镜像仓库是否在线,以及版本是否匹配


    • 第三件事情,docker 使用用户提供的账户密码
      • 访问 Www-Authenticate 头字段返回的鉴权服务器的地址 Bearer realm
      • 如果这个访问成功,则鉴权服务器会返回 jwt 格式的 token 给 docker
      • 然后 docker 会把账户密码编码并保存在用户目录的 .docker/docker.json 文件里
      • 下图是我登录仓库之后的 docker.json 文件
      • 这个文件作为 docker 登录仓库的唯一证据,在后续镜像仓库操作中,会被不断的读取并使用
      • 其中关键信息 auth 就是账户密码的 base64 编码

拉取镜像是怎么回事

  • 镜像一般会包括两部分内容,一个是 manifests 文件,这个文件定义了镜像的元数据
  • 另一个是镜像层,是实际的镜像分层文件,镜像拉取基本上是围绕这两部分内容展开
  • 因为我们这篇文章的重点是权限问题,所以我们这里只以 manifests 文件拉取为例

拉取 manifests 文件,基本上也会做三件事情:

  • 首先,docker 直接访问镜像 manifests 的地址,以便获取 Www-Authenticate 头字段

    • 这个字段包括鉴权服务器的地址 Bearer realm
    • 镜像服务地址 service,以及定义了镜像和操作的 scope
  • 接着,docker 访问上边拿到的 Bearer realm 地址来鉴权,以及在鉴权之后获取一个临时的 token

    • 这对应协议大图使用账户密码获取临时 token 这一步,使用的账户密码直接读取自 docker.json 文件
  • 最 后, 使 用 上 边 的 token, 以 Authorization 头 字 段 的 方 式, 来 下 载 manifests 文件

    • 这对应的是协议大图下载镜像这一步。当然因为镜像还有分层文件
    • 所以实际 docker 还会用这个临时 token 多次下载文件才能完整镜像下载

K8s 实现的私有镜像自动拉取

1 ) 基本功能

  • K8s 集群一般会管理多个节点,每个节点都有自己的 docker 环境,如果让用户分别到集群节点上登录镜像仓库,这显然是很不方便的

  • 为了解决这个问题,K8s 实现了自动拉取镜像的功能,这个功能的核心,是把 docker.json 内容编码,并以
    Secret 的方式作为 Pod 定义的一部分传给 Kubelet

  • 具体来说,步骤如下:

    • 1 )创 建 secret。 这 个 secret 的 .dockerconfigjson 数 据 项 包 括 了 一 份 base64 编码的 docker.json 文件
    • 2 )创建 pod,且 pod 编排中 imagePullSecrets 指向第一步创建的 secret
    • 3 )Kubelet 作为集群控制器,监控着集群的变化。当它发现新的 pod 被创建,就会通过 API Server 获取 pod 的定义,这包括 imagePullSecrets 引用的 secret
    • 4 )Kubelet 调用 docker 创建容器且把 .dockerconfigjson 传给 docker
    • 5 )最后 docker 使用解码出来的账户密码拉取镜像,这和上一节的方法一致

进阶方式

  • 上边的功能,一定程度上解决了集群节点登录镜像仓库不方便的问题

  • 但是我们,在创建 Pod 的时候,仍然需要给 Pod 指定 imagePullSecrets

  • K8s 通过变更准入控制(Mutating Admission Control)进一步优化了上边的基本功能

  • 进一步优化的内容如下:

    • 1 )在 第 一 步 创 建 secret 之 后, 添 加 default service account 对 imagePullSecrets 的引用;
    • 2 )Pod 默认使用 default service account,而 service account 变更准入控制器会在 default service account 引用 imagePullSecrets 的情况下,添加 imagePullSecrets 配置到 pod 的编排里

阿里云实现的 Acr credential helper

  • 阿里云容器服务团队,在 K8s 的基础上实现了控制器 Acr credential helper。

  • 这个控制器可以让同时使用阿里云 K8s 集群和容器镜像服务产品的用户,在不用配置自己账户密码的情况下,自动使用私有仓库中的容器镜像

  • 具体来说,控制器会监听 acr-configuration 这个 configmap 的变化,其主要关心 acr-registry 和 watch-namespace 这两个配置。前一个配置指定为临时账户授权的镜像仓库地址,后一个配置管理可以自动拉取镜像的命名空间

  • 当控制器发现有命名空间需要被配置却没有被配置的时候,它会通过阿里云容器镜像服务的 API,
    来获取临时账户和密码

  • 有了临时账户密码,Acr credential helper 为命名空间创建对应的 Secret 以及更改 default SA 来引用这个 Secret。这样,控制器和 K8s 集群本身的功能,一起自动化了阿里云 K8s 集群拉取阿里云容器镜像服务上的镜像的全部流程

总结

  • 理解私有镜像自动拉取的实现,有一个难点和一个重点
    • 难点是 OAuth 2.0 安全协议的原理,上文主要分析了为什么 OAuth 会这么设计
    • 重点是集群控制器原理,因为整个自动化的过程,实际上是包括 Admission control 和 Acr credential helper 在内的多个控制器协作的结果
### 批量修改Kubernetes中的容器镜像策略 为了批量更改Kubernetes集群中多个Pod或Deployment的镜像策略,通常有两种主要方法: #### 方法一:通过编辑YAML配置文件并重新应用 如果拥有所有目标资源(如Deployments, StatefulSets等)的原始YAML定义文件,则可以直接编辑这些文件来改变`imagePullPolicy`字段。之后使用命令行工具`kubectl apply -f <your-file>.yaml`再次部署更新后的资源配置。 对于已经存在于集群内的资源而没有保存其对应的YAML文件的情况下,可以通过导出现有的资源描述作为基础来进行修改: ```bash # 导出特定命名空间下的deployment为yaml格式,并排除自动添加的状态信息 kubectl get deployments -n your-namespace -o yaml --export > deployments.yaml ``` 接着手动打开生成的`deployments.yaml`文件,在每一个容器模板里找到`spec.template.spec.containers[*].imagePullPolicy`项将其设置成期望值(Always/Never/IfNotPresent),最后再执行apply操作[^1]。 #### 方法二:利用`jsonpath`或者`patch`方式在线上直接打补丁 另一种更灵活的方式是在不下载任何文件的前提下直接对线上资源做局部调整。这需要用到`kubectl patch`命令配合JSON Patch语法实现精准定位和替换。下面是一个例子展示如何一次性将某命名空间内所有的ReplicaSet对象里的镜像策略改为`Always`: ```bash # 获该命名空间下所有replicasets的名字列表 REPLICASETS=$(kubectl get rs -n your-namespace -o name) for RS in $REPLICASETS; do # 对每个replicaset逐个打补丁 kubectl patch $RS \ -p='{"spec":{"template":{"spec":{"containers":[{"name":"*","imagePullPolicy":"Always"}]}}}}'\ --type=merge -n your-namespace; done ``` 需要注意的是这里的`"*"`通配符仅适用于某些版本的Kubernetes;如果是较新版本可能需要具体指明容器名称而不是使用星号匹配全部容器。另外这种方法会覆盖原有配置中关于此属性的内容,请谨慎评估风险后再实施[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值