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下来就好。

docker pull registry

 

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

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

docker run -d -p 5000:5000 --name registry --restart=always registry

 

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

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

docker pull centos

docker tag centos 192.168.248.129:5000/centos

docker push 192.168.248.129:5000/centos

 

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

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配置文件里设置一下:

vim /etc/docker/daemon.json

 

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

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

 

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

systemctl daemon-reload

systemctl restart docker.service

 

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

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

docker start registry

 

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

 

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

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

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

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

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

sudo yum install -y openssl

 

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

#创建存放Registry证书的文件夹

sudo mkdir -p /etc/docker/certs.d/registry

#创建时效为365天的证书

sudo openssl req -newkey rsa:4096 -nodes -sha256 -keyout /etc/docker/certs.d/registry/private.key -x509 -days 365 -out /etc/docker/certs.d/registry/public.crt

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

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是私钥。

 

#设置证书文件只读权限,防止被修改:

sudo chmod 444 /etc/docker/certs.d/registry/public.crt

sudo chmod 444 /etc/docker/certs.d/registry/private.key

#如果启用了SELinux,Docker Server会被系统默认拒绝访问证书文件,需要设置放行

sudo chcon -Rt svirt_sandbox_file_t /etc/docker/certs.d/registry

 

由于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切换用户:

 

#将hosts文件权限设置为可写

sudo chmod 777 /etc/hosts

#插入解析记录

echo -e [IP地址]\\t[域名] >> /etc/hosts

#恢复hosts文件权限设置

sudo chmod 644 /etc/hosts

 

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

sudo chmod 777 /etc/hosts

echo -e 192.168.248.129\\thub.yanning.wang >> /etc/hosts

sudo chmod 644 /etc/hosts

 

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

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

docker run -d -p 443:5000 --restart=always --name registry \

  -v /etc/docker/certs.d/registry:/certs \

  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/public.crt \

  -e REGISTRY_HTTP_TLS_KEY=/certs/private.key \

  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状态:

docker ps

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已经成功启动,我们现在尝试上传一下镜像:

docker pull centos

docker tag centos hub.yanning.wang/centos

docker push hub.yanning.wang/centos

 

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

x509: certificate signed by unknown authority

 

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

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

对应位置一般来说就是:

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

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

#创建对应目录

sudo mkdir -p /etc/docker/certs.d/hub.yanning.wang

#复制证书公钥文件

sudo cp /etc/docker/certs.d/registry/public.crt /etc/docker/certs.d/hub.yanning.wang/ca.crt

#设置只读

sudo chmod 444 /etc/docker/certs.d/hub.yanning.wang/ca.crt

 

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

docker push hub.yanning.wang/centos

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

cf516324493c: Pushed

latest: digest: sha256:822de5245dc5b659df56dd32795b08ae42db4cc901f3462fc509e91e97132dc0 size: 529

 

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

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

CentOS下:

#修改可信证书集文件权限为可写

sudo chmod 777 /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

#导入证书

cat [您的证书公钥文件] >> /etc/pki/tls/certs/ca-bundle.crt

#恢复可信证书集文件权限设置

sudo chmod 444 /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

 

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

 

本文情境使用以下命令:

sudo chmod 777 /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

cat /etc/docker/certs.d/registry/public.crt >> /etc/pki/tls/certs/ca-bundle.crt

sudo chmod 444 /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

sudo systemctl restart docker.service

 

然后就可以进行push了。

 

Ubuntu 16.04下:

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

sudo mv [您的证书公钥文件] /usr/local/share/ca-certificates/t

sudo update-ca-certificates

sudo service docker restart

 

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

sudo mv public.crt /usr/local/share/ca-certificates/hub.yanning.wang.crt

sudo update-ca-certificates

sudo service docker restart

 

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

 

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

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

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

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

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

#创建存放密码文件的目录

sudo mkdir -p /etc/docker/auth

#修改目录权限为可写

sudo chmod 777 /etc/docker/auth

#生成密码并储存到文件中

#htpasswd -Bbn 用户名 密码 >> /etc/docker/auth/registry.htpasswd

#例如使用 用户名:admin 密码:admin

htpasswd -Bbn admin admin >> /etc/docker/auth/registry.htpasswd

#设置密码文件只读

sudo chmod 444 /etc/docker/auth/registry.htpasswd

#恢复文件夹drwxr-xr-x的权限设置

sudo chmod 755 /etc/docker/auth

#如果启用了SELinux,Docker Server会被系统默认拒绝访问密码文件,需要设置放行

sudo chcon -Rt svirt_sandbox_file_t /etc/docker/auth

 

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

#生成密码并储存到文件中

#htpasswd -Bbn 用户名 密码 >> /etc/docker/auth/registry.htpasswd 改为

#docker run --entrypoint htpasswd registry -Bbn 用户名 密码>> /etc/docker/auth/registry.htpasswd

#例如使用 用户名:admin 密码:admin

docker run --entrypoint htpasswd registry -Bbn admin admin >> /etc/docker/auth/registry.htpasswd

 

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

docker run -d -p 443:5000 --restart=always --name registry \

  -v /etc/docker/auth:/auth \

  -e REGISTRY_AUTH=htpasswd \

  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \

  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.htpasswd \

  -v /etc/docker/certs.d/registry:/certs \

  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/public.crt \

  -e REGISTRY_HTTP_TLS_KEY=/certs/private.key \

  registry

 

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

-v /etc/docker/auth:/auth \

-e REGISTRY_AUTH=htpasswd \

-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \

-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.htpasswd \

 

分别的意义是:

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

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

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

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

 

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

docker push hub.yanning.wang/centos

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

 

no basic auth credentials

 

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

docker login -u [用户名] -p [密码] [Registry地址]

例:

docker login -u admin -p admin hub.yanning.wang

 

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

docker login [Registry地址]

例:

docker login hub.yanning.wang

 

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

 

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

docker logout [Registry地址]

例:

docker logout hub.yanning.wang

 

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

 

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

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

docker stop registry

docker rm registry

 

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

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

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

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

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

 

#创建本地目录

sudo mkdir -p /etc/docker/registry

#如果启用了SELinux,Docker Server会被系统默认拒绝访问此目录,需要设置放行

sudo chcon -Rt svirt_sandbox_file_t /etc/docker/registry

#启动Registry容器

docker run -d -p 443:5000 --restart=always --name registry \

  -v /etc/docker/registry:/var/lib/registry \

  -v /etc/docker/auth:/auth \

  -e REGISTRY_AUTH=htpasswd \

  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \

  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.htpasswd \

  -v /etc/docker/certs.d/registry:/certs \

  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/public.crt \

  -e REGISTRY_HTTP_TLS_KEY=/certs/private.key \

  registry

#先登陆Registry

docker login -u admin -p admin hub.yanning.wang

#push些镜像上去

docker push hub.yanning.wang/centos

docker push hub.yanning.wang/ubuntu

#假装手滑删了Registry容器和镜像

docker rmi hub.yanning.wang/centos

docker rmi hub.yanning.wang/ubuntu

docker stop registry

docker rm registry

#再次启动Registry容器

docker run -d -p 443:5000 --restart=always --name registry \

  -v /etc/docker/registry:/var/lib/registry \

  -v /etc/docker/auth:/auth \

  -e REGISTRY_AUTH=htpasswd \

  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \

  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.htpasswd \

  -v /etc/docker/certs.d/registry:/certs \

  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/public.crt \

  -e REGISTRY_HTTP_TLS_KEY=/certs/private.key \

  registry

#从Registry中下载先前上传的镜像

docker pull hub.yanning.wang/centos

docker pull hub.yanning.wang/ubuntu

 

可以看到,我们这次中途故意删除了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,如果您愿意,可以将几个文件统一放到一个文件夹内,进行挂载,以节省参数。

docker run -d -p 443:5000 --restart=always --name registry \

  -v /etc/docker/registry:/var/lib/registry \

  -v /etc/docker/auth:/auth \

  -v /etc/docker/certs.d/registry:/certs \

  -v /etc/docker/config/config.yml:/etc/docker/registry/config.yml \

  registry

 

七、还想要更加高大上?

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

 

 

 

 

小柊

2017年9月27日 10:53:24

相关文章

发表评论

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