K8s 从懵圈到熟练 – 镜像拉取这件小事

做者 | 声东 阿里云售后技术专家docker

导读:相比 K8s 集群的其余功能,私有镜像的自动拉取,看起来多是比较简单的。而镜像拉取失败,大多数状况下都和权限有关。因此,在处理相关问题的时候,咱们每每会轻松的说:这问题很简单,确定是权限问题。但实际的状况是,咱们常常为一个问题,花了多我的的时间却找不到缘由。这主要仍是咱们对镜像拉取,特别是私有镜像自动拉取的原理理解不深。这篇文章,做者将带领你们讨论下相关原理。

顺序上来讲,私有镜像自动拉取会首先经过阿里云 Acr credential helper 组件,再通过 K8s 集群的 API Server 和 kubelet 组件,最后到 docker 容器运行时。可是个人叙述,会从后往前,从最基本的 docker 镜像拉取提及。json

镜像拉取这件小事

为了讨论方便,咱们来设想一个场景。不少人会使用网盘来存放一些文件,像照片,文档之类。当咱们存取文件的时候,咱们须要给网盘提供帐户密码,这样网盘服务就能验证咱们的身份。这时,咱们是文件资源的全部者,而网盘则扮演着资源服务器的角色。帐户密码做为认证方式,保证只有咱们本身能够存取本身的文件。安全

1

这个场景足够简单,但很快咱们就遇到新需求:咱们须要使用一个在线制做相册的应用。按正常的使用流程,咱们须要把网盘的照片下载到本地,而后再把照片上传到电子相册。这个过程是比较很繁琐的。咱们能想到的优化方法是,让相册应用,直接访问网盘来获取咱们的照片,而这须要咱们把用户名和密码受权给相册应用使用。服务器

这样的受权方式,优势显而易见,但缺点也是很明显的:咱们把网盘的用户名密码给了相册服务,相册服务就拥有了读写网盘的能力,从数据安全角度,这个是很可怕的。其实这是不少应用都会遇到的一个通常性场景。私有镜像拉取其实也是这个场景。这里的镜像仓库,就跟网盘同样,是资源服务器,而容器集群则是三方服务,它须要访问镜像仓库获取镜像。微信

2

理解 OAuth 2.0 协议

OAuth 协议是为了解决上述问题而设计的一种标准方案,咱们的讨论针对 2.0 版本。相比把帐户密码直接给三方应用,此协议采用了一种间接的方式来达到一样的目的。以下图,这个协议包括六个步骤,分别是三方应用获取用户受权,三方应用获取临时 Token 以及三方应用存取资源。less

3

这六步理解起来不容易,主要是由于安全协议的设计,须要考虑协议的易证实性,因此咱们换一种方式来解释这个协议。简单来讲,这个协议其实就作了两件事情:微服务

  • 在用户受权的状况下,三方应用获取 token 所表示的临时访问权限;
  • 而后三方应用使用这个 token 去获取资源。

若是用网盘的例子来讲明的话,那就是用户受权网盘服务给相册应用建立临时 token,而后相册应用使用这个 token 去网盘服务获取用户的照片。实际上 OAuth 2.0 各个变种的核心差异,在于第一件事情,就是用户受权资源服务器的方式。优化

4

  1. 最简单的一种,适用于三方应用自己就拥有被访问资源控制权限的状况。这种状况下,三方应用只须要用本身的帐户密码登陆资源服务器并申请临时 token 便可;
  2. 当用户对三方应用足够信任的状况下,用户直接把帐户密码给三方应用,三方应用使用帐户密码向资源服务器申请临时 token;
  3. 用户经过资源服务器提供的接口,登陆资源服务器并受权资源服务器给三方应用发放 token;
  4. 完整实现 OAuth 2.0 协议,也是最安全的。三方应用首先获取以验证码表示的用户受权,而后用此验证码从资源服务器换取临时 token,最后使用 token 存取资源。

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

5

Docker 扮演的角色

大图

镜像仓库 Registry 的实现,目前使用“把帐户密码给三方应用”的方式。即假设用户对 Docker 足够信任,用户直接将帐户密码交给 Docker,而后 Docker 使用帐户密码跟鉴权服务器申请临时 token。编码

6

理解 docker login

首先,咱们在拉取私有镜像以前,要使用 docker login 命令来登陆镜像仓库。这里的登陆其实并无和镜像仓库创建什么会话之类的关系。登陆主要就作了三件事情:

  • 第一件事情是跟用户要帐户密码。

以下图,当执行登陆命令,这个命会提示输入帐户密码,这件事情对应的是大图的第一步。

7

  • 第二件事情,docker 访问镜像仓库的 https 地址,并经过挑战 v2 接口来确认,接口是否会返回 Docker-Distribution-Api-Version 头字段。

这件事情在协议图中没有对应的步骤。它的做用跟 ping 差很少,只是确认下 v2 镜像仓库是否在线,以及版本是否匹配。

8

  • 第三件事情,docker 使用用户提供的帐户密码,访问 Www-Authenticate 头字段返回的鉴权服务器的地址 Bearer realm。

若是这个访问成功,则鉴权服务器会返回 jwt 格式的 token 给 docker,而后 docker 会把帐户密码编码并保存在用户目录的 .docker/docker.json 文件里。

9

下图是我登陆仓库以后的 docker.json 文件。这个文件做为 docker 登陆仓库的惟一证据,在后续镜像仓库操做中,会被不断的读取并使用。其中关键信息 auth 就是帐户密码的 base64 编码。

10

拉取镜像是怎么回事

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

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

  • 首先,docker 直接访问镜像manifests的地址,以便获取 Www-Authenticate 头字段。这个字段包括鉴权服务器的地址 Bearer realm,镜像服务地址 service,以及定义了镜像和操做的 scope。

11

  • 接着,docker 访问上边拿到的 Bearer realm 地址来鉴权,以及在鉴权以后获取一个临时的 token。这对应协议大图使用帐户密码获取临时 token 这一步,使用的帐户密码直接读取自 docker.json 文件。

12

  • 最后,使用上边的 token,以 Authorization 头字段的方式,来下载 manifests 文件。这对应的是协议大图下载镜像这一步。固然由于镜像还有分层文件,因此实际 docker 还会用这个临时 token 屡次下载文件才能完整镜像下载。

13

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

基本功能

K8s 集群通常会管理多个节点,每一个节点都有本身的 docker 环境。若是让用户分别到集群节点上登陆镜像仓库,这显然是很不方便的。为了解决这个问题,K8s 实现了自动拉取镜像的功能。这个功能的核心,是把 docker.json 内容编码,并以 Secret 的方式做为 Pod 定义的一部分传给 Kubelet。

14

具体来讲,步骤以下:

  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)进一步优化了上边的基本功能。

15

进一步优化的内容以下:

  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 集群和容器镜像服务产品的用户,在不用配置本身帐户密码的状况下,自动使用私有仓库中的容器镜像。

16

具体来讲,控制器会监听 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 在内的多个控制器协做的结果。
“ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的技术公众号。”
相关文章
相关标签/搜索