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

导读:相比 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 扮演的角色

大图

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

理解 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 实现的私有镜像自动拉取

基本功能

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 在内的多个控制器协作的结果。

“ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

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

  1. 如何拉取k8s镜像_K8s 从懵圈到熟练 – 镜像拉取这件小事

    导读:相比 K8s 集群的其他功能,私有镜像的自动拉取,看起来可能是比较简单的.而镜像拉取失败,大多数情况下都和权限有关.所以,在处理相关问题的时候,我们往往会轻松的说:这问题很简单,肯定是权限问题. ...

  2. K8S 从懵圈到熟练--大数据平台技术栈18

    回顾:大数据平台技术栈 (ps:可点击查看),今天就来说说其中的K8S! 来自:阿里技术公众号 阿里妹导读:排查完全陌生的问题.不熟悉的系统组件,对许多工程师来说是无与伦比的工作乐趣,当然也是一大挑战 ...

  3. K8s 从懵圈到熟练-集群伸缩原理

    作者 | 声东  阿里云技术专家 <关注公众号,回复 排查 即可下载电子书> <深入浅出 Kubernetes>一书共汇集 12 篇技术文章,帮助你一次搞懂 6 个核心原理,吃 ...

  4. K8S从懵圈到熟练 - 节点下线姊妹篇

    之前分享过一例集群节点NotReady的问题.在那个问题中,我们的排查路劲,从K8S集群到容器运行时,再到sdbus和systemd,不可谓不复杂.那个问题目前已经在systemd中做了修复,所以基本 ...

  5. K8S从懵圈到熟练 - 我们为什么会删除不了集群的命名空间?

    阿里云售后技术团队的同学,每天都在处理各式各样千奇百怪的线上问题.常见的有,网络连接失败,服务器宕机,性能不达标,请求响应慢等.但如果要评选,什么问题看起来微不足道事实上却足以让人绞尽脑汁,我相信答案 ...

  6. K8s 从懵圈到熟练 – 集群网络详解

    作者 | 声东 阿里云售后技术专家 导读:阿里云 K8S 集群网络目前有两种方案:一种是 flannel 方案:另外一种是基于 calico 和弹性网卡 eni 的 terway 方案.Terway ...

  7. K8S从懵圈到熟练 – 集群网络详解

    阿里云K8S集群网络目前有两种方案,一种是flannel方案,另外一种是基于calico和弹性网卡eni的terway方案.Terway和flannel类似,不同的地方在于,terway支持Pod弹性 ...

  8. 分享一个很香的k8s.gcr.io Docker镜像拉取方法

    经常k8s.gcr.io ,quay.io之类的国外镜像拉取不过来,那怎么办呢? 本文安利一种使用github action拉取镜像推送到Dockerhub的方法.推送好了之后,自己拉取镜像retag ...

  9. 如何使用Aliyun容器镜像服务对海外gcr、quay仓库镜像进行镜像拉取构建?

    关注「WeiyiGeek」公众号 设为「特别关注」每天带你玩转网络安全运维.应用开发.物联网IOT学习! 本章目录: 0x00 前言简述 0x01 操作实践 原文地址: 如何使用Aliyun容器镜像服 ...

最新文章

  1. 干货:排名前 16 的 Java 工具类!
  2. 2018年视频云服务市场格局进入整合阶段,阿里云视频云位居市场竞争力领导者的位置...
  3. 对外星智能的搜索得到了重大的升级
  4. opencv 图像雾检测_OpenCV图像处理-基于OpenPose的关键点检测
  5. 2017年度盘点:15个最流行的GitHub机器学习项目 By 机器之心2017年12月21日 15:23 在本文中,作者列出了 2017 年 GitHub 平台上最为热门的知识库,囊括了数据科学、机
  6. myeclipse2014下卸载,安装maven插件。
  7. 1. 初探MongoDB —— 介绍、安装和配置
  8. BAPI_PO_CREATE1
  9. 深度学习练手项目(一)-----利用PyTorch实现MNIST手写数字识别
  10. golang beego安装及入门示例
  11. 数据库事务和并发控制
  12. linux进程和计划任务,linux进程和计划任务
  13. wince7 屏幕控制_技术干货:WinCE 7.0下的触摸屏驱动
  14. python静态递归函数_python—递归函数
  15. (转)中国首单运用区块链技术的交易所ABS获批
  16. 微信测试拉黑的软件,如何检测微信里有没有人把你拉黑?教你一招!
  17. linux 合并文本文件,Shell脚本合并文本文件
  18. P6615 Kruskal + 构造
  19. 教育知识与能力(中学)
  20. GLSL里的矩阵对向量的乘法与OpenGL里用数组生成矩阵进行向量乘法的非直觉不一致

热门文章

  1. bash读取txt文件, 并在浏览器中以表格形式输出
  2. 11、HTML <head>标签
  3. 1、数据库是什么?关系型数据库和非关系型数据库又是什么?
  4. 验证输入字符串是否包含特殊字符
  5. C++中的空指针和void * 指针
  6. 功能内聚C语言例子,为每种类型的模块内聚举一个具体的例子
  7. 你只会用 ! = null 判空?嘿嘿!
  8. 这 56 个代码注释让我笑吐了
  9. 坚持,这两个字非常重要!
  10. springmvc教程--注解开发基础详解