Docker Registry服务中的两个重要组件,分别承担如下不一样的职责:git
Index:主要负责集中管理用户帐户,访问权限,镜像的校验和 以及区分公有和私有repos(也就是公共的命名空间)等,一般是以公开的web接口来实现。web
Registry:主要负责镜像数据的存储,提供Pull和Push镜像的功能,以及委派受权部分给index实现.算法
当你用docker push/pull的时候,index肯定你是否有使用/修改镜像的权限,确认以后,registry只是负责把镜像存好或把镜像经过某种方式发送给你。并且,index还能指出某个特定的镜像具体存放的registry地址并向它发出请求。除此以外,当你只是在本地运行docker images这样的命令时,并不须要依赖和Index /registry进行交互。docker
Docker Hub 是Docker公司提供的云平台服务,用来分发应用:包括容器镜像分发,变动管理,用户/团队管理,自动化生命周期工做流。实际上,Docker Hub包括了Index模块功能上的实现。ubuntu
Docker Hub提供的服务主要包括:后端
>1. 托管docker镜像 >2. 用户受权 >3. 自动构建镜像,以及像Build triggers、Web Hooks这样的工做流工具。 >4. 和Github &Bitbucket集成
Docker Trusted Registry是 Docker Hub 企业版的新版本,DTR 提供了运行和管理私有镜像的存储服务,让你在公司内部能够安全的分发、交付管理本身定制的各类镜像。一样提供了对Registry集群的各类性能上的监控。安全
Registry自己是一个无状态,高可扩展性的服务端应用,用来存储和分发docker镜像。常常被用来与subversion,git等作类比。服务器
它实现了:并发
* 以分层的方式来存储的镜像以及它的描述信息 * 实现与docker client一致的API * 以web应用的方式,简单实现一个web服务器让镜像可用。
说白了,Registry是用来存储和内容分发的系统,托管已经命名而且存在不一样tag的Docker镜像。上层封装了http接口的服务,底层存储委托给Driver,也就是在对象存储上封装了一层Repo的镜像graph关系,抽象成接口调用。默认的存储驱动是本地POSIX文件系统。app
以松耦合的方式把Web UI,用户认证,镜像元数据独立出来,给想作Private Registry服务的开发者本身实现。而Registry是全部模块的复用部分,无状态,单纯用于镜像服务。因为Registry无状态,因此能够水平扩展。
先引入v2版本的一些新的概念。
众所周知,镜像在底层是分层存储的。所谓的内容可寻址,是指镜像的层被看成能够根据digest来划分镜像的blobs。那么什么又是digest ?什么是blobs?
实际上,digest是内容惟一的标识符。在内容可寻址系统中,把Digest类型设计成一种灵活的标识符。类比与CRC,md5等,这里的digest是根据密码学中哈希算法生成的校验值。在Registry v2中,使用SHA256算法来生成每一个镜像层对应的digest。这种方式易于实现和校验。
digest的格式是一个简单的字符串,由两部分组成。
<algorithm>:<digest>
例如 :
sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
blob能够简单理解成镜像层+ JSON数据等构成的一个镜像数据块。它是存储在Registry后端文件系统中,由digest惟一标识的镜像数据。
对于docker-registry中,镜像id是须要确保它的保密性的,由于这个ID是全局性的,不一样的镜像仓库中该镜像的id是一致的。这样无疑方便了镜像管理,但同时也存在必定的风险。即,能够经过images id来获取镜像的详细信息。而镜像的layer IDs是被随机分配的,也就是说不能经过layer id来分享images,由于在registry下镜像的layer id可能不一样。同时存在着校验的复杂性。
每一层layer都有一个对应的JSON对象来对镜像层进行描述,以下图:
在新版本distribution中,镜像层是能够被直接获取的,而且支持并发的方式,提升了传输效率。这也就是所谓的镜像是内容可寻址的。镜像的id也再也不须要保证它的保密性,也更加安全,缘由以前讲过再也不赘述。具体命令以下:
docker pull ubuntu@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
镜像的digest以JSON的格式存放在manifest中,若是该层的数据有任何的改动,它的manifest中的digest也会改变。与v1版本的区别在于将全部镜像层的描述放在一个JSON Object中,也就是manifest中。以下图:
一言以蔽之,新的镜像描述文件简化了镜像的定义和提高了安全性,利用这些新特性提高registry的性能,减小了带宽和后端崩溃的可能性。
v2版本的工做流较v1版本更为简单,省略了在认证auth token时的往返鉴权方式。 v1版本在部署的时候能够选择鉴权的模式,standalone \ index_endpoint \ disable_token_auth. 若是使用disable_token_auth, 你须要本身提供权限认证的方式.(Basic auth, Oauth等)
经过以上的工做流分析,咱们能够清晰得看到两版本的认证方式的变化。 固然,Docker Registry能以无认证检查的方式跑在standalone模式。可是咱们能够在Client和Registry中架设认证受权服务,基于原生的公钥鉴权机制增长HTTP认证方式,来提供更好的权限控制。这个认证受权服务,在两版本中均由index中的认证服务来提供。
不过,在以前咱们先明确认证受权服务的具体需求:
认证服务器 为Docker Registry中的Repo充当管理员的角色,经过 使用服务帐户和公钥 来受权和管理权限的方式,来管理 用户帐户和密钥、 集中权限控制表 。
v1版本中,用户在client端发出请求,会先去index服务上作认证,找到镜像所在的registry地址并返回给client端。最后,client端再从registry下载镜像,固然下载以前,registry会先去index校验client发来请求中token的合法性。不一样的镜像能够保存在不一样的registry服务上,其索引信息都放在index服务上。
v2版本中,认证过程其实包括四个阶段。寻找认证服务器、请求token、返回token、验证token。
client端先向Registry发出请求。若是是未受权的请求,会返回401响应。而这个响应中包括三个信息,realm, scope, service 具体表明 用户可使用scope和 service的信息向realm发请求。举个例子:
realm="https://auth.docker.com/v2/token/",service="registry.docker.com",scope="repository:samalba/my-app:push"
以后客户端会知道,在头部WWW-Authenticate中 加入service 和 scope的值,向 URL https://auth.docker.com/v2/token/ 发送GET请求,来获取token。
这里分别举例解释一下遇到的参数。
参数 | 描述 | 实例 |
---|---|---|
service | 是指Registry服务所在的域名 | service="registry.docker.com" |
realm | 申请受权的URL请求地址 | realm="https://auth.docker.com/v2/token/" |
scope | 请求资源的具体内容 | scope=repository:samalba/my-app:push |
account | 客户端的用户名 | account=gzzhangyi2015 |
向认证服务器请求一个token来获取访问Repo资源的权限。须要客户端经过TLS或者某种方式受权,而且若是客户端证书中的key关联帐号信息,这个token就要签署帐号信息。若是证书关联了多个帐号,客户端必须指定帐号查询参数。返回的token是JSON web token格式,而且用认证服务器的私钥签名。
举个例子,首先,配置认证服务器容许未被验证的客户端进行握手。而后,客户端向认证服务器发送一个HTTP请求到以下的endpoint,该请求用客户端的TLS证书加密。(这个证书能够是自签的)
GET /v2/token/?service=registry.docker.com&scope=repository:samalba/my-app:push&account=jlhawn HTTP/1.1 Host: auth.docker.com
接下来,服务器首先检查客户端证书,提取出子Key,并找到与之关联的用户。 而后,服务器检查它的权限控制列表,验证用户是否具备 registry.docker.com服务下的 samlba/my-app repo的使用权限。
最后,服务器会造成一个JSON Web Token来签名并返回。
这个token是用对称算法加密,以后在Registry端用相同的算法解密后,再来验证这个token的一些信息。在整个流程中,Registry都不须要再向认证服务器发出请求。若是有,也只是须要更新认证服务器上的受信任的公钥列表,用来验证token的签名或者使用其余的API来更新认证服务器上的Repo资源记录。
在Registry端 验证Token的步骤以下:
一、确认证书颁发者(issuer :iss claim)是受信任的。通常issuer是认证服务器的FQDN。iss: auth.docker.com
二、确认Registry识别出token的接收方。aud : registry.docker.com
三、检查当前时间在有效时间范围以内。
四、若是token只能使用一次,就要检查JWT的id值是否出现过,来避免token的重复申请。一般会在registry中保持jti记录,延续一个token的有效时间,来避免重复申请token。
五、检查 repo的使用权限,access值就包含了repo名和可以对该repo执行的操做。(决定了token是否授予用户具备对该操做所需权限)。
六、验证token签名的有效性。