作者 | 声东 阿里云售后技术专家
导读:相比 K8s 集群的其他功能,私有镜像的自动拉取,看起来可能是比较简单的。而镜像拉取失败,大多数情况下都和权限有关。所以,在处理相关问题的时候,我们往往会轻松的说:这问题很简单,肯定是权限问题。但实际的情况是,我们经常为一个问题,花了多个人的时间却找不到原因。这主要还是我们对镜像拉取,特别是私有镜像自动拉取的原理理解不深。这篇文章,作者将带领大家讨论下相关原理。
顺序上来说,私有镜像自动拉取会首先通过阿里云 Acr credential helper 组件,再经过 K8s 集群的 API Server 和 kubelet 组件,最后到 docker 容器运行时。但是我的叙述,会从后往前,从最基本的 docker 镜像拉取说起。
为了讨论方便,我们来设想一个场景。很多人会使用网盘来存放一些文件,像照片,文档之类。当我们存取文件的时候,我们需要给网盘提供账户密码,这样网盘服务就能验证我们的身份。这时,我们是文件资源的所有者,而网盘则扮演着资源服务器的角色。账户密码作为认证方式,保证只有我们自己可以存取自己的文件。
cdn.com/6b9d7ab4225d4d10ec91b23f609d34f92f6df4fd.png">
这个场景足够简单,但很快我们就遇到新需求:我们需要使用一个在线制作相册的应用。按正常的使用流程,我们需要把网盘的照片下载到本地,然后再把照片上传到电子相册。这个过程是比较很繁琐的。我们能想到的优化方法是,让相册应用,直接访问网盘来获取我们的照片,而这需要我们把用户名和密码授权给相册应用使用。
这样的授权方式,优点显而易见,但缺点也是很明显的:我们把网盘的用户名密码给了相册服务,相册服务就拥有了读写网盘的能力,从数据安全角度,这个是很可怕的。其实这是很多应用都会遇到的一个一般性场景。私有镜像拉取其实也是这个场景。这里的镜像仓库,就跟网盘一样,是资源服务器,而容器集群则是三方服务,它需要访问镜像仓库获取镜像。
OAuth 协议是为了解决上述问题而设计的一种标准方案,我们的讨论针对 2.0 版本。相比把账户密码直接给三方应用,此协议采用了一种间接的方式来达到同样的目的。如下图,这个协议包括六个步骤,分别是三方应用获取用户授权,三方应用获取临时 Token 以及三方应用存取资源。
这六步理解起来不容易,主要是因为安全协议的设计,需要考虑协议的易证明性,所以我们换一种方式来解释这个协议。简单来说,这个协议其实就做了两件事情:
如果用网盘的例子来说明的话,那就是用户授权网盘服务给相册应用创建临时 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 集群拉取阿里云容器镜像服务上的镜像的全部流程。
理解私有镜像自动拉取的实现,有一个难点和一个重点。
**“ 阿里巴巴云×××icloudnative×××erverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发×××
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。