注册 登录
  • 欢迎访问"运维那点事",推荐使用Google浏览器访问,可以扫码关注本站的"微信公众号"。
  • 如果您觉得本站对你有帮助,那么可以扫码捐助以帮助本站更好地发展。

Docker:搭建私有仓库(Registry 2.4)

Docker 彭东稳 4705次浏览 已收录 4个评论

背景

首先,Docker Hub是一个很好的用于管理公共镜像的地方,我们可以在上面找到想要的镜像(Docker Hub的下载量已经达到数亿次);而且我们也可以把自己的镜像推送上去。但是,有的时候,使用场景需要我们有一个私有的镜像仓库用于管理自己的镜像,这个时候我们就通过Registry来实现此目的。本文详细介绍了本地镜像仓库Docker Registry & Portus的搭建过程。

Registry作为Docker的核心组件之一负责镜像内容的存储与分发,客户端的docker pull以及push命令都将直接与registry进行交互。最初版本的registry 由Python实现。由于设计初期在安全性,性能以及API的设计上有着诸多的缺陷,该版本在0.9之后停止了开发,新的项目distribution来重新设计并开发下一代registry。新的项目由go语言开发,所有的API,底层存储方式,系统架构都进行了全面的重新设计已解决上一代registry中存在的问题。2016年4月份rgistry 2.0正式发布,docker 1.6版本开始支持registry 2.0,而八月份随着docker 1.8 发布,docker hub正式启用2.1版本registry全面替代之前版本 registry。新版registry对镜像存储格式进行了重新设计并和旧版不兼容,docker 1.5和之前的版本无法读取2.0的镜像。

另外,Registry 2.4版本之后支持了回收站机制,也就是可以删除镜像了。在2.4版本之前是无法支持删除镜像的,所以如果你要使用最好是大于Registry 2.4版本的。

Registry V2的变化

Docker build镜像时会为每个layer生成一串layer id,这个layer id是一个客户端随机生成的字符串,和镜像内容无关。我们可以通过一个简单的例子来查看。提供一个简单的dockerfile。

加上–no-cache强制重新build。

接下来再次build。

可以看到使用cache层的layer id一致,其余layer的id都发生了变化。这种随机layer id以及layer id与内容无关的设计会带来很多的问题。

首先, registry v1通过id来判断镜像是否存在,客户需不需要重新push,而由于镜像内容和id无关,再重新build后layer在内容不变的情况下很可能id发生变化,造成无法利用registry中已有layer反复push相同内容。服务器端也会有重复存储造成空间浪费。

其次,尽管id由32字节组成但是依然存在id碰撞的可能,在存在相同id的情况下,后一个layer由于id和仓库中已有layer相同无法被push到registry中,导致数据的丢失。用户也可以通过这个方法来探测某个id是否存在。

最后,同样是由于这个原因如果程序恶意伪造大量layer push到registry中占位会导致新的layer无法被push到registry中。Docker官方重新设计新版registry的一个主要原因也就是为了解决该问题。

新版的registry吸取了旧版的教训,在服务器端会对镜像内容进行哈希,通过内容的哈希值来判断layer在registry中是否存在,是否需要重新传输。这个新版本中的哈希值被称为digest是一个和镜像内容相关的字符串,相同的内容会生成相同的digest。由于digest和内容相关,因此只要重新build的内容相同理论上讲无需重新push,但是由于安全性的考量在特定情况下layer依然要重新传输。由于layer是按digest进行存储,相对v1按照随机id存储可以大幅减小磁盘空间占用。registry服务端会对冲突digest进一步进行处理,同时由于digest是由registry服务端生成,用户无法伪造digest也很大程度上保证了registry内容的安全性。

1)安全性改进

除了对image内容进行唯一性哈希外,新版registry还在鉴权方式以及layer权限上上进行了大幅度调整。鉴权方式:

旧版本的服务鉴权模型如下图所示:

Docker:搭建私有仓库(Registry 2.4)

该模型每次client端和registry的交互都要多次和index打交道,新版本的鉴权模型去除了上图中的第四第五步,如下图所示:

Docker:搭建私有仓库(Registry 2.4)

新版本的鉴权模型需要registry和authorization service在部署时分别配置好彼此的信息,并将对方信息作为生成token的字符串,已减少后续的交互操作。新模型客户端只需要和authorization service进行一次交互获得对应token即可和registry进行交互,减少了复杂的流程。同时registry和authorization service一一对应的方式也降低了被攻击的可能。

2)权限控制

旧版的registry中对layer没有任何权限控制,所有的权限相关内容都由index完成。在新版registry中加入了对layer的权限控制,每个layer都有一个manifest来标识该layer由哪些repository共享,将权限做到repository级别。

3)Pull性能改进

旧版registry中镜像的每个layer都包含一个ancestry的json文件包含了父亲layer的信息,因此当我们pull镜像时需要串行下载,下载完一个layer后才知道下一个layer的id是多少再去下载。如下图所示:

Docker:搭建私有仓库(Registry 2.4)

新版registry在image的manifest中包含了所有layer的信息,客户端可以并行下载所有的layer如下图所示:

Docker:搭建私有仓库(Registry 2.4)

4)其他改进

– 全新的API。

– push和pull支持断点。

– 后端存储的插件。

– notification机制。

– 支持删除镜像,有了回收站机制。

安装配置Registry

直接下载registry

v2.4.1的registry是把image文件放到了/var/lib/registry下。

最简单方式启动,启动一个registry是很容易的,如下:

–name:指定容器名称。

–privileged=true :CentOS7中的安全模块selinux把权限禁掉了,参数给容器加特权,不加上传镜像会报权限错误。

这里指定了一个/var/lib/registry的卷,是为了把真实的镜像数据储存在主机上,而别在容器挂掉之后丢失数据。就算这样,也还是不保险。要是主机挂了呢?Docker官方建议可以放到ceph 、 swift这样的存储里,或是亚马逊S3 、微软Azure 、谷歌GCS 、阿里云OSS之类的云商那里。Docker registry提供了配置文件,可以从容器里复制出来查看:

配置文件里有一个storage ,按照这里写的配置,然后执行以下命令重新挂载这个文件来启动registry就可以了,有条件的话可以去试一试:

Docker Registry配置完了,然后可以在本机通过docker push 127.0.0.1:5000/xxx的方式推送镜像到registry中(推送镜像必须使用docker images可查看)。

但是只能在本地使用127.0.0.1进行推送,不能在其他主机push镜像,包括本机通过IP地址也不可以推送镜像。当在其他主机或者在本机通过IP推送镜像时,docker默认会认为地址是HTTPS加密的,而实际上我们启动registry时并没有加密,所以会报错。如下:

解决方案:

第一种:在需要推送镜像的服务器上修改dockerd启动参数【官方资料】,然后重启docker。再推送镜像时就会认为这个地址是HTTP,不会报错了,但在每一台主机添加这个配置是很麻烦和危险的。另外我参照官方的做法和网上的做法根本没有办法解决。后来就在网上翻了很久找到了一个解决办法,在docker host端的/etc/docker目录下添加一个daemon.json文件,内容如下:

然后重启docker,就OK了。

如果有多个地址,可以这么写。

再次PUSH镜像就成功了。

第二种:自建证书,以TLS的方式启动,【官方资料】。

启动:

启动后访问会报错:x509: certificate signed by unknown authority,因为这是个自签名的证书。解决方案是将刚生成的docker.crt复制到/etc/docker/certs.d/${registry_address}/ca.crt,docker在验证TLS时会自动读取这个目录下的证书。然后重启docker即可。

如果在其他客户端上push镜像,那么需要把此证书复制到其他主机即可,操作如下:

然后再次PUSH镜像,又遇到了一点小问题:

解决办法:修改/etc/pki/tls/openssl.cnf配置,在该文件中找到[ v3_ca ] ,在它下面添加如下内容:

再次重启docker,问题解决。至此, docker registry私有仓库安装成功。但是还是有些缺点:只要有了证书,还是谁都可以往库里推镜像。简单的解决方案就是使用用户认证。

操作Registry镜像

下面都是以http方式访问,如果你加了证书就需要使用https进行访问了。

1)列出当前所有镜像

2)列出当前指定镜像

3)搜索镜像

4)确认Registry是否正常工作

返回{}就表示正常工作。

5)删除镜像

Docker仓库在2.1版本中支持了删除镜像的API,但这个删除操作只会删除镜像元数据,不会删除层数据。在2.4版本中对这一问题进行了解决,增加了一个垃圾回收命令,删除未被引用的层数据。但有一些条件限制,具体操作步骤如下:

启动仓库容器

这里需要说明一点,在启动仓库时,需在配置文件中的storage配置中增加delete=true配置项,允许删除镜像,本次试验采用如下配置文件:

查看数据进行仓库容器中,通过du命令查看大小,可以看到当前仓库数据大小为339M。

删除镜像对应的API如下:

name:镜像名称。

reference:镜像对应sha256值。

首先查看要删除镜像的sha256

进行删除操作

执行垃圾回收

命令:registry garbage-collect config.yml

再看数据大小

可以看到镜像数据已被删除,从339M变成了88K。

PS:尝试过直接在目录中把镜像删除,然后重启docker daemon,此镜像也会删除。

下载镜像

PS:注意后面还可以跟上tags,默认就是latest。

用户认证

首先在registry生成用户名hello和密码world:

还得指定认证方式和认证文件等参数,重新启动registry容器:

再次push就会失败啦。

但是我们可以用用户名hello和密码world登录,然后在进行push:

登录成功后,再次push就会成功了。如果想退出登录,使用logout即可。

Docker私有仓库到这里就结束了,个人感觉还是有很多不足。有兴趣可以看看Docker:使用Portus构建私有仓库


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (11)or分享 (0)
关于作者:

您必须 登录 才能发表评论!

(4)个小伙伴在吐槽
  1. 操作正确,我看了下,总大小变小了,但是使用curl http://xxxx:5000/v2/_catalog命令查看所有的镜像,被删除的镜像名还是会查出来
    tjytad2017-04-26 14:45 Windows 7 | Chrome 45.0.2454.101
    • 我有点忘记我当时测试结果了,没有截图。其实你都执行完了之后进目录去直接删掉也不会有事的。有时间我再测一下看看。
      彭东稳2017-04-26 23:09 未知操作系统 | Chrome 51.0.2704.103
  2. 删除镜像后,用curl http://10.99.73.10:5000/v2/_catalog查看,还是能够查的到被删除镜像的名字啊
    tjytad2017-04-25 17:32 Windows 7 | Chrome 45.0.2454.101
    • 你都操作正确了,开启允许删除,删除后执行垃圾回收。
      彭东稳2017-04-25 19:43 未知操作系统 | Chrome 56.0.2924.87