Docker 私有Registry搭建教程

小柊 发表于 2017年09月27日 11时01分58秒

一、背景

之前在博客里给Docker系列教程起了个头,讲了怎么在几个主流的系统上安装Docker(《C# Docker开发(一) Docker的安装》)。在讲下面的内容之前,我突然觉得我有必要先讲一下如何搭设私有Registry。

 

大家都知道github,github上所有的项目都是public的,如果需要将项目设置成private的,就需要付费,所以就出了一个gitlab。Docker官方也有一个集中存放用户push的Docker Image的地方叫做Docker Hub。但push到Docker Hub的Image默认也是公开供任意人访问的,如果是自己公司内部产品需要用的Docker Image,push到Docker Hub显然不合适。Docker官方也推出了一个私有的镜像仓库管理软件:Docker Registry。我们可以在自己的服务器上搭建自己私有的Docker Registry,那些不方便上传到Docker Hub的Docker Image就可以上传到这里。

下面的文章里,默认测试机的IP为:192.168.248.129

 

二、简单的部署

Docker Registry已经被Docker官方push到了Docker Hub中,在终端里敲下面的命令将Registry镜像从Docker Hub上pull下来就好。

 

这里没有加版本,所以默认下载latest,也就是v2版本。Registry现在最新的版本是v2版本,相较于一开始的v1版本有很多的差别,v1版本是用python写的,而v2版本则是用Go语言写的,另外,v2版本在安全和性能上做出了很多做了非常多的优化。至于两个版本之间的差距具体可以去网上搜索,这里简单提一下就不做深入了。

等镜像pull完毕,就可以启动镜像了:

 

上面的命令会使用Registry镜像后台启动一个名为“registry”的容器,同时将本机5000端口映射到容器的5000端口,至于参数“--restart=always”,它表示自动重启容器,当docker服务被重启后,会默认启动容器。

我们现在将一个镜像(centos:latest)push到我们的私有Registry里去:

 

一般来说上面的命令到最后一条命令的时候就会报错:

Get https://192.168.248.129:5000/v1/_ping: http: server gave HTTP response to HTTPS client

 

因为Docker自从1.3.x起,与Registry连接时都是默认启用HTTPS的。由于我们在这里启动Registry容器时并没有配置HTTPS证书选项,所以Registry默认会改用HTTP,而Docker Client默认只接受HTTPS,所以就报错了。

解决这个问题非常简单,只需要到Docker配置文件里设置一下:

 

这个配置文件是一个Json文件,内部遵守Json格式。

我们需要在里面加一个名为“insecure-registries”的设置项,值为包含我们私有Registry仓库地址数组,修改后的结果大概如下(第一个“registry-mirrors”设置项是Docker Hub的镜像地址,如何设置Docker Hub的镜像地址可以参考之前的文章):

 

修改好之后重启Docker服务以启用新配置:

 

如果之前启动Registry容器时没有加上“--restart=always”参数的话,要再手动启动Registry容器,否则到时候会抛出getsockopt: connection refused的错误。

下面命令中registry是之前启动时赋予容器的名字:

 

现在就可以push到我们的私有仓库里了:

 

三、进阶一:部署启用HTTPS的Registry

上一章里我们部署了一个简单的私有Registry。由于没有提供HTTPS的证书,所以Registry默认降级使用HTTP。

这一章节中,笔者将教大家如何部署一个启用HTTPS的Registry。

首先是创建证书,现在网上免费的HTTPS证书有很多,比如说Let's Encrypt之类的,当然有条件的话付费什么的也行。不过在这里我就直接使用OpenSSL自签发证书,自签发证书默认是不被系统信任的,所以在部署之后还有一些小调整。

如果没有安装OpenSSL,那就先安装一下:

 

安装完成之后自签发证书:

 

为了签发证书,会进行必要的信息输入:

Generating a 4096 bit RSA private key

..........................................................................................................................................++

...........................++

writing new private key to '/etc/docker/certs.d/registry/private.key'

-----

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:CN (国家名称,中国为CN

State or Province Name (full name) []:Zhejiang (省份名称)

Locality Name (eg, city) [Default City]:Hangzhou (城市名称)

Organization Name (eg, company) [Default Company Ltd]:Chunfeng Tech (组织名称)

Organizational Unit Name (eg, section) []:IT Dev (部门名称)

Common Name (eg, your name or your server's hostname) []:hub.yanning.wang (主机名称,仅允许域名)

Email Address []:xxxx@xxx.com (邮箱地址)

 

完成上面的步骤之后,OpenSSL工具就会为您产生HTTPS证书文件,位于/etc/docker/cert.d/registry目录下。public.crt是公钥,private.key是私钥。

 

 

由于Docker Server不允许使用纯IP的HTTPS证书(会被提示:x509: cannot validate certificate for 192.168.248.129 because it doesn't contain any IP SANs),所以在之前使用OpenSSL自签发HTTPS证书的时候笔者随手填了一个不存在的子域名:hub.yanning.wang

所以现在还需要把对应的解析关系写入hosts里,以便能正常解析域名地址.由于hosts文件默认情况下非root用户不可写,所以要先修改权限,也可直接使用su切换用户:

 

IP地址和域名中间的“\\t”会被转义为水平制表符。本文中情景应键入下面的命令:

 

完成之后刷新一下DNS缓存,或者直接重启服务器使hosts的修改生效。

现在可以使用HTTPS证书文件启动Registry容器了:

 

上面的命令中,使用-v参数将宿主机的/etc/docker/certs.d/registry目录挂载到了容器内的/certs目录下,同时用-e参数对“REGISTRY_HTTP_TLS_CERTIFICATE”,“REGISTRY_HTTP_TLS_KEY”这两个环境变量进行赋值,赋予证书文件的路径。另外,HTTPS的端口默认使用443,而Registry容器默认只监听5000端口,所以使用-p参数将宿主机的443端口映射到容器的5000端口,省的以后使用docker tag命令的时候还要带上端口号了。

上面的命令敲好之后看一下Registry容器的状态是不是Up状态:

 

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES

583d75664662        registry            "/entrypoint.sh /etc/"   3 minutes ago       Up 3 minutes        0.0.0.0:443->5000/tcp   registry

 

 

如果容器的状态(STATUS)不是Up,而是Restarting之类的,可能是参数有问题,具体信息可以使用docker logs命令查看日志输出。

 

假设您的Registry已经成功启动,我们现在尝试上传一下镜像:

 

一般来说如果是使用从专门的HTTPS签发证书机构签发的HTTPS证书,应该可以直接上传,但我们在上面用的是自己自己签发的HTTPS证书,不被系统所信任,所以会直接证书错误:

x509: certificate signed by unknown authority

 

对于这种自签发证书不被Docker信任的情况,有两种解决方法:

1.将证书放到对应位置(推荐):

对应位置一般来说就是:

/etc/docker/certs.d/[自签发证书时填写的域名]/ca.crt

本文中自签发证书时填写的域名是hub.yanning.wang,所以笔者我需要执行以下命令:

 

完成后无需重启Docker Server,可以马上进行push操作:

 

The push refers to a repository [hub.yanning.wang/centos]

cf516324493c: Pushed

latest: digest: sha256:822de5245dc5b659df56dd32795b08ae42db4cc901f3462fc509e91e97132dc0 size: 529

 

2.将证书导入系统信任证书列表

笔者不是特别建议这种方法,因为导入容易,删除就比较麻烦了。如果您的Docker版本不高,上面的方法不管用,可以试试这个方法。

CentOS下:

 

导入成功后重启Docker Server,以使得配置生效。

 

本文情境使用以下命令:

 

然后就可以进行push了。

 

Ubuntu 16.04下:

Ubuntu的操作比CentOS简单一些,就是把证书公钥文件复制到指定的文件夹中,然后更新CA证书列表,最后重启DockerServer就好:

 

本文情境下使用命令(笔者我在移动文件的时候顺便进行了重命名):

 

完成后就可以进行push操作。

 

四、进阶二:部署启用身份认证的Registry

完成上面Registry的HTTPS部署之后,还有一个新的问题:因为日后我们的这个镜像仓库是会放我们公司自己的商业产品,不能让其他无关人员进行随意进行连接访问。那该怎么办呢?

这个问题自然不用愁,因为Docker官方已经考虑到了这个身份验证的需求,Registry是可以通过设置进行身份验证的,在启用身份验证的Registry中,没有通过身份验证的用户将不能对Registry进行操作。

Registry一共可以使用三种身份验证模式,分别是silly、token和htpasswd。因为htpasswd用的比较广泛,所以本文在这里只讲述如何启用htpasswd身份验证。

htpasswd其实是apache的一个密码生成工具,它可以将用户名和密码存储在文件之中,同时密码是被加密存放的,提高了安全性。一般来说htpasswd工具在安装了apache(httpd)的系统上都会存在,如果您已经安装有apache(httpd),可以直接键入下面的命令生成密码文件:

 

如果您的服务器上没有apache(httpd),您也不想在服务器上安装它们的话,也有替代方案,Docker官方在Registry镜像中封装了htpasswd函数,您只需要执行执行下面的命令替换上面的htpasswd命令就好:

 

一切就绪后,使用下面的命令启动Registry容器:

 

和第三章比起来又多了四个参数,不要慌,都不是什么深奥的东西,多的四个参数分别是

 

分别的意义是:

第一个参数将宿主机的/etc/docker/auth目录挂载到容器内的/auth目录下;

第二个参数设置环境变量告诉Registry容器使用htpasswd验证模式;

第三个参数的意义笔者不是特别清楚,貌似是指定验证领域;

最后一个参数设置环境变量告诉Registry容器htpasswd密码文件的所在路径。

 

设置好之后,当别人想再访问我们的私有Registry的时候,必须先进行登录,否则会直接报错:

 

The push refers to a repository [hub.yanning.wang/centos]

 

no basic auth credentials

 

在对需要身份验证的Docker Registry进行操作前,需要使用docker login命令进行登陆

 

例:

 

当然你也可以在命令中不带用户名密码参数以保障密码安全:

 

例:

 

键入命令后,Docker会询问您用户名和密码,键入后回车,当提示“Login Succeeded”之后,便可以进行操作了。

 

一般来说,使用docker login命令成功登陆后,您的登陆凭据将一直被保留着。如果您现在不需要了,或公用电脑不想让其他人连接此Registry,可以使用docker logout命令登出:

 

例:

 

 

主动注销后,在未重新登陆的情况下,将再无访问此Registry的权限。

 

五、进阶三:将Registry容器中的文件挂载到宿主机

接下来考虑一个很蛋疼的情况,有一天,一个实习生上了服务器,执行了以下的命令:

 

弹指间,Registry容器中所有push上来的镜像全都没了。可不可怕?

刚刚的比喻可能比较极端,那就再举一个例子:您有一天想要之前启动Registry容器的参数,比如修改端口映射,或者修改HTTPS证书路径等等。

一般这样删除容器重新创建,之前所有push上来的镜像都没了,那可怎么办,难道要重新传一次吗?

为了解决这个问题,我们就需要将容器内存放镜像文件的目录挂载到宿主机上,就算容器被销毁了,镜像依旧可以保留,下次启动Registry容器时,重新将文件挂载回去,就不会出现镜像丢失的情况了。

Registry容器默认会将我们push上去的镜像文件保存到容器内的/var/lib/registry目录下,所以我们只要将宿主机的一个目录使用-v参数挂载到容器的/var/lib/registry目录就可以了,在这里笔者将在宿主机中创建/etc/docker/registry目录用于挂载。

 

可以看到,我们这次中途故意删除了Registry容器,但重新启动后,Registry容器依旧可以提供先前上传的镜像文件。

 

六、进阶四:部署使用配置文件的Registry

经过上面几个进阶操作,我们的私有Registry变的高大上起来。但美中不足的是,每次启动Registry时都要输入这么长一串的参数,未免太繁琐了。有没有简单的方法呢?

当然有,Registry在容器内部有一份默认的配置文件,位于/etc/docker/registry/config.yml,其内容可以通过docker exec -it registry /bin/sh命令进入容器,或使用docker save registry > registry.tar保存镜像文件后解包镜像文件,查看config.yml内容。

由于在实际使用中,我们将要挂载我们自己的配置文件到容器内覆盖掉原有文件,所以最好将默认设置拷贝出来,在此基础上进行修改,默认设置如下:

 

好的,现在让我们将进阶三中启动Registry容器命令中的环境参数写入配置文件中,并保存为/etc/docker/config/config.yml

 

上面的Yaml文档中,auth.httpwd和http.tls部分为我们新加的部分。

完成后启动Registry容器。使用配置文件可以省略掉启动时的需要加的环境变量参数-e,但并不能省略挂载参数-v,如果您愿意,可以将几个文件统一放到一个文件夹内,进行挂载,以节省参数。

 

七、还想要更加高大上?

上一章节,我们提到了配置文件。其实Registry的配置文件自由度非常高,可以进行自由设置,包括允许删除镜像等等,具体的配置手册请参考Docker官方文档《Configuring a registry》,里面有所有可配置的项目和具体说明,虽然只有英文文档,但写的非常简单易懂,强烈推荐。

 

 

 

 

小柊

2017年9月27日 10:53:24

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注