做者 | 声东 阿里云售后技术专家docker
导读:相比 K8s 集群的其余功能,私有镜像的自动拉取,看起来多是比较简单的。而镜像拉取失败,大多数状况下都和权限有关。因此,在处理相关问题的时候,咱们每每会轻松的说:这问题很简单,确定是权限问题。但实际的状况是,咱们常常为一个问题,花了多我的的时间却找不到缘由。这主要仍是咱们对镜像拉取,特别是私有镜像自动拉取的原理理解不深。这篇文章,做者将带领你们讨论下相关原理。
顺序上来讲,私有镜像自动拉取会首先经过阿里云 Acr credential helper 组件,再通过 K8s 集群的 API Server 和 kubelet 组件,最后到 docker 容器运行时。可是个人叙述,会从后往前,从最基本的 docker 镜像拉取提及。json
为了讨论方便,咱们来设想一个场景。不少人会使用网盘来存放一些文件,像照片,文档之类。当咱们存取文件的时候,咱们须要给网盘提供帐户密码,这样网盘服务就能验证咱们的身份。这时,咱们是文件资源的全部者,而网盘则扮演着资源服务器的角色。帐户密码做为认证方式,保证只有咱们本身能够存取本身的文件。安全
这个场景足够简单,但很快咱们就遇到新需求:咱们须要使用一个在线制做相册的应用。按正常的使用流程,咱们须要把网盘的照片下载到本地,而后再把照片上传到电子相册。这个过程是比较很繁琐的。咱们能想到的优化方法是,让相册应用,直接访问网盘来获取咱们的照片,而这须要咱们把用户名和密码受权给相册应用使用。服务器
这样的受权方式,优势显而易见,但缺点也是很明显的:咱们把网盘的用户名密码给了相册服务,相册服务就拥有了读写网盘的能力,从数据安全角度,这个是很可怕的。其实这是不少应用都会遇到的一个通常性场景。私有镜像拉取其实也是这个场景。这里的镜像仓库,就跟网盘同样,是资源服务器,而容器集群则是三方服务,它须要访问镜像仓库获取镜像。微信
OAuth 协议是为了解决上述问题而设计的一种标准方案,咱们的讨论针对 2.0 版本。相比把帐户密码直接给三方应用,此协议采用了一种间接的方式来达到一样的目的。以下图,这个协议包括六个步骤,分别是三方应用获取用户受权,三方应用获取临时 Token 以及三方应用存取资源。less
这六步理解起来不容易,主要是由于安全协议的设计,须要考虑协议的易证实性,因此咱们换一种方式来解释这个协议。简单来讲,这个协议其实就作了两件事情:微服务
若是用网盘的例子来讲明的话,那就是用户受权网盘服务给相册应用建立临时 token,而后相册应用使用这个 token 去网盘服务获取用户的照片。实际上 OAuth 2.0 各个变种的核心差异,在于第一件事情,就是用户受权资源服务器的方式。优化
从上面的描述咱们能够看到,资源服务器实际上扮演了鉴权和资源管理两种角色,这二者分开实现的话,协议流程会变成下图这样。阿里云
镜像仓库 Registry 的实现,目前使用“把帐户密码给三方应用”的方式。即假设用户对 Docker 足够信任,用户直接将帐户密码交给 Docker,而后 Docker 使用帐户密码跟鉴权服务器申请临时 token。编码
首先,咱们在拉取私有镜像以前,要使用 docker login 命令来登陆镜像仓库。这里的登陆其实并无和镜像仓库创建什么会话之类的关系。登陆主要就作了三件事情:
以下图,当执行登陆命令,这个命会提示输入帐户密码,这件事情对应的是大图的第一步。
这件事情在协议图中没有对应的步骤。它的做用跟 ping 差很少,只是确认下 v2 镜像仓库是否在线,以及版本是否匹配。
若是这个访问成功,则鉴权服务器会返回 jwt 格式的 token 给 docker,而后 docker 会把帐户密码编码并保存在用户目录的 .docker/docker.json 文件里。
下图是我登陆仓库以后的 docker.json 文件。这个文件做为 docker 登陆仓库的惟一证据,在后续镜像仓库操做中,会被不断的读取并使用。其中关键信息 auth 就是帐户密码的 base64 编码。
镜像通常会包括两部份内容,一个是 manifests 文件,这个文件定义了镜像的元数据,另外一个是镜像层,是实际的镜像分层文件。镜像拉取基本上是围绕这两部份内容展开。由于咱们这篇文章的重点是权限问题,因此咱们这里只以 manifests 文件拉取为例。
拉取 manifests 文件,基本上也会作三件事情:
K8s 集群通常会管理多个节点,每一个节点都有本身的 docker 环境。若是让用户分别到集群节点上登陆镜像仓库,这显然是很不方便的。为了解决这个问题,K8s 实现了自动拉取镜像的功能。这个功能的核心,是把 docker.json 内容编码,并以 Secret 的方式做为 Pod 定义的一部分传给 Kubelet。
具体来讲,步骤以下:
上边的功能,必定程度上解决了集群节点登陆镜像仓库不方便的问题。可是咱们在建立 Pod 的时候,仍然须要给 Pod 指定 imagePullSecrets。K8s 经过变动准入控制(Mutating Admission Control)进一步优化了上边的基本功能。
进一步优化的内容以下:
阿里云容器服务团队,在 K8s 的基础上实现了控制器 Acr credential helper。这个控制器可让同时使用阿里云 K8s 集群和容器镜像服务产品的用户,在不用配置本身帐户密码的状况下,自动使用私有仓库中的容器镜像。
具体来讲,控制器会监听 acr-configuration 这个 configmap 的变化,其主要关心 acr-registry 和 watch-namespace 这两个配置。前一个配置指定为临时帐户受权的镜像仓库地址,后一个配置管理能够自动拉取镜像的命名空间。当控制器发现有命名空间须要被配置却没有被配置的时候,它会经过阿里云容器镜像服务的 API,来获取临时帐户和密码。
有了临时帐户密码,Acr credential helper 为命名空间建立对应的 Secret 以及更改 default SA 来引用这个 Secret。这样,控制器和 K8s 集群自己的功能,一块儿自动化了阿里云 K8s 集群拉取阿里云容器镜像服务上的镜像的所有流程。
理解私有镜像自动拉取的实现,有一个难点和一个重点。
“ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的技术公众号。”