手把手教你搭建Docker Registry私服

Build, Ship and Run any App, Anywhere

关于Docker更多的概念将不在本文赘述了,作为虚拟化市场的一颗冉冉升起的新星,Docker得到了越来越多企业的青睐,越来越多的开发者决定拥入Docker的怀抱。

“集装箱”是Docker设计哲学之所在,它让一台物理机(或者虚拟机)同时运行多个彼此隔离的应用变得更为轻松简单,当然这一切还是多亏Linux的相关底层技术,当然,也包括OS X,Windows这两个操作系统。

Docker的相关教程已经够多了,但是由于Docker近年来发展迅猛,版本迭代速度较快,多个版本之间还存在不兼容的情况,如果在网上找博客文章,未必能解决自己遇到的问题。

当然,对于Docker的环境安装,基础命令之类的内容,是完全没有问题的,通读官网文档内容基本都能顺利掌握。然而,当笔者尝试着搭建一套基于SSL的Docker Registry(官网推荐的做法)却遇到了不少的麻烦,对于这部分内容,大多数博客文档内容都是直接跳过了SSL的环节,采用了HTTP的访问形式。

特此分享,通读完了此篇文章后,对于搭建Docker Registry就不再是问题了。

> docker --version

Docker version 18.03.1-ce, build 9ee9f40

以上是我的Docker环境,建议安装Docker1.6+以上版本。

除此之外,读者还需要:

1、一台安装了CentOS_7_64bit操作系统的主机(或者虚拟机);

2、申请一个域名。不然,可以改HOST文件,但不保证能成功。笔者在阿里云申请了一个个人域名,包了5年,价值¥105;

3、如果申请了域名,顺便拿一个免费的CA证书,因为需要实现HTTPS访问,SSL证书是必须的,同样,阿里云上有免费证书申请。不然,可以使用OpenSSL自己生成,这也是很多博文所提到的做法,不保证成功;

4、再装上一个nginx做代理,可选。

5、熟悉Docker的基本概念和常用命令,但不必了解Dockerfile,Compose,Swarm,Kubernetes等高阶知识。

一、从Docker镜像说起

抽象的概念阐述不多说了,笔者举两个例子,让大家感受一下:

1、我们从系统之家下载来的ISO文件,除了基础的操作系统,还可能内置了多个预装软件;

2、在使用maven管理jar包依赖的时候,为了避免每次都从中央仓库拉取依赖包,使用了nexus做了代理仓库。

可以认为,Docker镜像就是一系列软件(文件)的组合,只要将它们放在合适的宿主上,即可做到开箱即用。

关于本文需要的Docker镜像操作,有五个常用的命令:

a、拉取镜像,后跟镜像仓库名称,如果要指定某个版本,可以带上tag。

> docker pull <repo>[:tag]

b、列出所有镜像,能得到镜像的相关基本信息。

> docker images
REPOSITORY                   TAG             IMAGE ID          CREATED        SIZE
redis                       latest          bfcb1f6df2db      3 weeks ago     107MB
registry                      2             d1fd7d86a825      4 months ago    33.3MB
hyper/docker-registry-web   latest          0db5683824d8      19 months ago   599MB

c、删除镜像。可以根据镜像ID,或者镜像仓库名称进行删除。

> docker rmi <IMAGE ID>/<repo>

d、镜像打标。可以将此操作与Git打标进行类比,相当于是release一个可用的镜像版本。

> docker tag <repo> <new_repo>[:tag]

e、镜像推送。同样是可以借鉴Git领域的push操作,将打包好的镜像推送给远程仓库(即为Docker Registry)。

> docker push <new_repo>[:tag]

以上五个命令只做简单介绍,不是本文的重点。更多镜像操作命令,可以自行查阅之。

进行接下来的操作之前,请读者先将registry镜像pull下来。

> docker pull registry:2
2: Pulling from library/registry
81033e7c1d6a: Pull complete 
b235084c2315: Pull complete 
c692f3a6894b: Pull complete 
ba2177f3a70e: Pull complete 
a8d793620947: Pull complete 
Digest: sha256:672d519d7fd7bbc7a448d17956ebeefe225d5eb27509d8dc5ce67ecb4a0bce54
Status: Downloaded newer image for registry:2

此过程会持续几分钟,视网络状况而定,请读者耐心等候。

注意:笔者在pull的时候,指定了TAG,即为使用v2版本的registry,对于v1版本的registry,读者大可不必在意了,基本上是淘汰了。

二、先睹为快

对于急切想看一下Docker Registry运行效果的读者,可以先阅读本节内容。

运行如下命令即可:

> docker run -d \
 -p 5000:5000 \
 -v /usr/local/registry:/var/lib/registry \
 --restart=always \
 --name registry \
registry:2

这是一条典型的run命令,不出意外的话,Registry就在5000端口启动了。

为了验证,读者可以拉取一个busybox镜像(因为体积小),进行实验。

> docker pull busybox

拉取最新的busybox镜像后,再给其打标,准备发布到Registry中。

> docker tag busybox localhost:5000/bosybox:v1.0

最后再推送给Registry。

> docker push localhost:5000/bosybox:v1.0

此时,Registry就有了busybox:v1.0镜像了,这时可以不用再去Docker Hub上面拉取了,通过自建的Registry即可。

> docker pull localhost:5000/bosybox:v1.0

如果想查看远程仓库有哪些镜像,可以运行如下命令:

> curl http://localhost:5000/v2/_catalog

窥一斑而见全豹,通过以上命令,我们能得出一个重要的结论:

对Registry的访问都是通过一系列REST API完成的。

到此为止,我们已经搭建了一个Docker Registry的“半成品”,说是“半成品”是因为这个Registry只能在本机正常工作,如果在其他主机上试图推送镜像上来,结果是失败的。

如果要做到externally-accessible,就必须使用CA安全证书。

三、基于SSL证书改造Registry

在进行本节的操作前,请读者确认是否满足了文章开头所列的条件。

笔者申请了一个域名:iwendao.vip,并映射出来了一个二级域名:registry.iwendao.vip,专门用来作为Docker Registry的访问,然后基于此二级域名申请CA证书。

不出意外的话,从阿里云申请的免费证书都是由Symantec颁发的,将证书下载下来后,压缩包内有两份文件:xxxxxx.pem、xxxxxx.key。

将其更名为server.key,server.pem,通过ftp工具上传至主机,假设存放的目录是:/usr/local/certs。

> ll /usr/local/certs
- rw-r--r-- 1 root root 1678 May 28 13:42 server.key
- rw-r--r-- 1 root root 3662 May 28 13:42 server.pem

因为颁发的是intermediate certificate,会发现没有crt文件,可以使用如下命令得到:

> cat server.pem > server.crt

直接将pem文件内作为crt文件的内容输入,生成了server.crt文件。

至此,域名及其证书已准备就绪。

如果想使用nginx做代理,需要更改一下nginx.conf文件,以下是笔者的配置内容:

user  root root;
worker_processes  1;
events {
	worker_connections  1024;
}
http {
	include       mime.types;
	default_type  application/octet-stream;
	log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
		'$status $body_bytes_sent "$http_referer" '
		'"$http_user_agent" "$http_x_forwarded_for"';
	access_log  logs/access.log  main;
	sendfile        on;
	keepalive_timeout 60;
	gzip  on;
	server {
		listen 443;
		server_name i-wendao;
		ssl on;
		root html;
		index index.html index.htm;
		ssl_certificate   /usr/local/certs/server.pem;
		ssl_certificate_key  /usr/local/certs/server.key;
		ssl_session_timeout 5m;
		ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
		ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
		ssl_prefer_server_ciphers on;
		location ~  {
			proxy_pass_header Server;
			proxy_set_header Host $http_host;
			proxy_set_header X-Real-IP $remote_addr;
			proxy_set_header X-Scheme $scheme;
			proxy_pass https://registry;
		}
	}
	upstream registry {
		server 127.0.0.1:5000;
	}
}

在nginx.conf配置文件中,需要注意两个地方:

1、开启nginx的ssl,只需要配置之前下载好的pem和key文件,这是阿里云官方给出的示例,亲测有效;

2、因为对Registry的访问都是通过REST API完成的,而且是HTTPS的访问协议,所以location节点的配置中,proxy_pass配置的是registry,如果配置成registry,一旦Docker Registry启用了SSL后,是访问不通的。

宿主机的配置已经完成了,接下来对Docker容器进行配置。

对于Docker Registry Server的部署,官方给了两个途径:

其一,针对参数不多的情况,可以直接在docker run命令指定;

另一个是通过yaml配置文件,可以一次性配置多个参数。

在本节,笔者将使用第一种方式部署,第二种方式的部署可以参见文末的附文。

> docker run -d \
  -p 5000:5000 \
  -v /usr/local/registry:/var/lib/registry \
  -v /usr/local/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/server.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/server.key \
  --restart=always \
  --name registry \
  registry:2

如果没有安装nginx的读者,可运行这条命令:

> docker run -d \
  -p 443:443 \
  -v /usr/local/registry:/var/lib/registry \
  -v /usr/local/certs:/certs \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/server.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/server.key \
  --restart=always \
  --name registry \
  registry:2

可以看到,Docker Registry Server启用SSL的时候,用到的是crt、key两个证书文件。

接下来,我们实验一下。

> docker tag busybox registry.iwendao.vip/bosybox:v1.0

最后再推送给Registry。

> docker push registry.iwendao.vip/bosybox:v1.0

查看远程仓库有哪些镜像。

> curl https://registry.iwendao.vip/v2/_catalog

在另外一台主机,重复上述步骤,依然可以成功,说明已经搭建成功了。

四、Authentication的加持

通过对Registry Server的搭建,我们能明显感觉得到,Docker对于安全控制有足够的重视,这在全网HTTPS化的背景下是恰合时宜的做法。

这节是进阶知识,目的是将Docker的安全机制做得更彻底——加上登录校验机制。

很明显,既然是私服,就意味着不是每个人都能提交镜像的,只有凭借登录用户和密码才行。

当然,登录校验机制的前提是有HTTPS协议,否则,用户名和密码都将会明文传输。

Docker的认证机制也有很多实现,可以直接用代理(比如nginx)在Registry之前进行拦截验证,高端的一些的是有Token服务端,引导用户授权登录,实现难度较大。

本文以最简单的htpasswd在实现登录校验机制。关于htpasswd的更多介绍不在本文的范畴,请读者自行查阅之。

如果主机上没有安装此命令工具,可以运行如下命令:

> yum install httpd-tools

因为htpasswd是Apache2的附属工具命令,如果安装了Apache2,此命令理应是可以用的。

如果读者不想安装了,可以直接使用registry镜像,其内置了httpd。

假设密码文件存放在/usr/local/auth目录下面,运行如下命令

> htpasswd -Bbn admin 123456 > /usr/local/auth/passwd

使用registry镜像内置的httpd,如下:

> docker run --entrypoint htpasswd registry:2 -Bbn admin 123456 > /usr/local/auth/passwd

两种方式都能达到同样的目的:在/usr/local/auth/passwd文件中生成用户名和密码。

命令中的admin是用户名,123456即为密码。

查看passwd文件内容:

> cat /usr/local/auth/passwd
admin:$2y$05$/2H8DTcY.1JROHm0MnnK8.UulmbSclib63qTe8FGyWnnE9XWBz3cy

虽然是同样的命令,但在不同的主机,生成的结果并不相同。因此,在主机A上生成的密码文件不能用作主机B上进行认证。

接下来要启动registry容器了:

> docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  -v /usr/local/auth:/auth \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM=Registry_Realm \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/passwd \
  -v /usr/local/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/server.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/server.key \
  registry:2

启动成功后,如果试图直接查看远程仓库有哪些镜像,会提示未认证,如下:

> curl https://registry.iwendao.vip/v2/_catalog
{
	"errors": [{
		"code": "UNAUTHORIZED",
		"message": "authentication required",
		"detail": [{
			"Type": "registry",
			"Class": "",
			"Name": "catalog",
			"Action": "*"
		}]
	}]
}

包括pull,push操作也都受限了。因此,在做操作之前,需要进行登录。

> docker login https://registry.iwendao.vip

既然有登录,当然就有登出了。

> docker logout https://registry.iwendao.vip

五、WEB UI for Registry

当我们把Registry Server搭建好了之后,就意味着要开始管理我们的镜像了。这个时候会发现,并没有一个可视化的工具帮助用户进行镜像管理。

目前已经有很多开源的WEB UI管理工具:

1、docker-registry-frontend截止到目前(2018年5月),其功能主要是镜像列表查看,标签查看,还未开放镜像删除功能,在GitHub上开源,stars 1k+。

2、docker-registry-web相比docker-registry-frontend项目,此项目提供了镜像删除功能,还接入了角色系统,功能有了进一步完善,在GitHub上开源,stars 300+。

3、Rancher。这个平台的定位类似Kubernetes,不仅仅是镜像管理这么简单了,对于整个Docker容器管理都是能胜任的。

4、shipyard很可惜,作者已经没有经历维护了,从GitHub上的stars,不难看出其昔日的辉煌。

关于WEB UI的安装部署就不再赘述了,都有其对应的文档。如果对于镜像管理没有什么特别要求,可以不用WEB UI,或者使用前两个之一。

六、总结

本文详述了Docker Registry私服搭建的过程,总结了来自各类博客,官网的学习资料,帮助读者顺利搭建Docker Registry私服。


附:

1、使用yaml文件启动registry server

假设配置文件的存放路径是:/usr/local/registry/config.yml

编辑其内容,如下:

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
    maxthreads: 100
  delete:
    enabled: true
http:
  addr: 0.0.0.0:5000
  host: https://registry.iwendao.vip
  secret: yoogurt-taxi-123!@#
  headers:
    X-Content-Type-Options: [nosniff]
  tls:
    certificate: /certs/214709594090104.crt
    key: /certs/214709594090104.key
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

更多配置项,可以访问Configuring a registry

配置文件中的配置项是可以对应到前文中-e参数的环境变量,其规则就是:

1、变量名由大写字母组成;

2、前缀固定加上REGISTRY;

3、将YAML中的配置项的冒号(:)变成了下划线(_)。

比如:

REGISTRY_HTTP_TLS_CERTIFICATE,对应的是http: tls: certificate配置项;

REGISTRY_AUTH_HTPASSWD_PATH,对应的是auth: htpasswd: path配置项。

值得注意的是,配置文件中所涉及的路径都是针对容器内的,这就意味着,在启动registry镜像的时候,需要通过-v参数指定挂载目录。

保存配置文件后,即可启动容器:

> docker run -d -p 5000:5000 --restart=always --name registry \
             -v /usr/local/certs:/certs
             -v /usr/local/registry/config.yml:/etc/docker/registry/config.yml \
             registry:2

2、介绍关于Docker的书籍

  • 《第一本Docker书(修订版)》,当之无愧的Docker启蒙书。零基础入门者可以着重看前五章基础部分,掌握Docker的相关原理及其使用,可用作工具书。读此书,建议跟着内容同步进行实践,入门以后,能够建立起对Docker的兴趣,以便持续学习下去。
  • 《Docker 容器与容器云(第2版)》,知识内容有所进阶。第一部分内容为基础知识,可以快速过一遍。本书对于容器云的概念做了各方面反复的解释,非常精彩,可以吸收之。接下来就是容器编排、部署的内容了,可以加之实践,培养感觉。
  • 《Kubernetes权威指南(第2版)》,当之无愧的Kubernetes入门书籍。读完了《Docker 容器与容器云(第2版)》的Kubernetes部分,再来读此书,会轻松一些。


关注我的微信公众号:技术汇(ID:jishuhui_2015),获取更多技术分享干货!

文章被以下专栏收录

    娱乐圈没有圈,互联网没有网。世界之大,要说的有许多,却又什么都说不出来。想来想去,我还是谈谈微服务的圈内圈外吧!——IT界素人