我们知道Docker官方提供了一个公有的registry叫做Docker Hub。但是企业内部可能有些镜像还是不方便放到公网上去,所以docker也提供了registry镜像来让需要的人自己搭建私有仓库。本文从零开始搭建Docker Registry的运行环境,并添加用户界面和认证功能。
准备工作
我们需要先安装virtualBox和vagrant。通过vagrant来驱动virtualBox搭建一个虚拟测试环境。首先在本地任意路径新建一个空文件夹比如test
,运行以下命令:
virtual box host1 2 3 4
| mkdir test cd test vagrant init minimum/ubuntu-trusty64-docker vi Vagrantfile
|
里面应该有一句config.vm.box = "minimum/ubuntu-trusty64-docker"
,在它的下面添加如下几行代码,相当于给它分配两台虚拟机,一台叫做registry,它的IP是192.168.33.18;另一台叫做client,它的IP是192.168.33.19。Registry配上界面会比较耗内存,所以我们给它1G内存,默认是512M。
Vagrantfile1 2 3 4 5 6 7 8 9 10 11 12
| config.vm.define "registry" do | host | host.vm.hostname = "registry" host.vm.network "private_network", ip: "192.168.33.18" host.vm.provider "virtualbox" do |v| v.memory = 1024 end end config.vm.define "client" do | host | host.vm.hostname = "client" host.vm.network "private_network", ip: "192.168.33.19" end
|
这个vagrant镜像已经在ubuntu的基础上帮我们安装了docker,用起来很方便。然后分别在两个终端运行以下命令启动并连接两台虚拟机。
virtual box host terminal 11 2
| vagrant up vagrant ssh registry
|
搭建环境
启动一个registry是很容易的:
registry1 2 3 4 5 6
| docker run -d \ -p 5000:5000 \ --name registry \ --restart=always \ -v /var/lib/registry:/var/lib/registry \ registry:2.3.0
|
这里指定了一个/var/lib/registry
的卷,是为了把真实的镜像数据储存在主机上,而别在容器挂掉之后丢失数据。就算这样,也还是不保险。要是主机挂了呢?Docker官方建议可以放到ceph、swift这样的存储里,或是亚马逊S3、微软Azure、谷歌GCS、阿里云OSS之类的云商那里。Docker registry提供了配置文件,可以从容器里复制出来查看:
registry1 2
| docker cp registry:/etc/docker/registry/config.yml config.yml cat config.yml
|
配置文件里有一个storage
,按照这里写的配置,然后执行以下命令重新挂载这个文件来启动registry就可以了,有条件的话可以去试一试:
registry1 2 3 4 5 6 7 8
| docker rm -fv registry docker run -d \ -p 5000:5000 \ --name registry \ --restart=always \ -v /var/lib/registry:/var/lib/registry \ -v `pwd`/config.yml:/etc/docker/registry/config.yml \ registry:2.3.0
|
Docker Registry配置完了,我们在client上传一个镜像试试:
client1 2 3
| docker pull busybox:1.24.1 docker tag busybox:1.24.1 192.168.33.18:5000/busybox:1.24.1 docker push 192.168.33.18:5000/busybox:1.24.1
|
结果push的时候就挂了。原来是我们没有配置认证信息,所以这是一个“不安全”的registry。Docker要求在docker daemon的启动参数里增加--insecure-registry
,才能允许我们上传镜像:
client1 2 3
| sudo sh -c 'echo DOCKER_OPTS=\"--insecure-registry 192.168.33.18:5000\" >> /etc/default/docker' sudo service docker restart docker push 192.168.33.18:5000/busybox:1.24.1
|
这回就没问题啦。同样地在registry端也配置一下,然后把registry:2.3.0这个镜像上传:
registry1 2 3 4
| sudo sh -c 'echo DOCKER_OPTS=\"--insecure-registry 192.168.33.18:5000\" >> /etc/default/docker' sudo service docker restart docker tag registry:2.3.0 192.168.33.18:5000/library/registry:2.3.0 docker push 192.168.33.18:5000/library/registry:2.3.0
|
如果是没有用户的镜像(通常是官方镜像),打标签和上传都需要加一个library/
。客户端必须再配置一个参数--registry-mirror
才能在我们自己的私有registry里下载镜像:
client1 2 3 4
| sudo sed -i '$d' /etc/default/docker sudo sh -c 'echo DOCKER_OPTS=\"--insecure-registry 192.168.33.18:5000 --registry-mirror http://192.168.33.18:5000\" >> /etc/default/docker' sudo service docker restart docker pull registry:2.3.0
|
应该有飞一般的感觉了吧。如果镜像不在registry里,客户端会自动去docker hub下载。但是每次打标签再上传岂不是很麻烦?所幸docker提供了一个proxy的功能。只要在config.yml
里增加如下配置,重启registry容器即可。这样,客户端pull的镜像,也会自动同步到registry里去。
1 2
| proxy: remoteurl: https://registry-1.docker.io
|
界面
Docker官方只提供了REST API,并没有给我们一个界面。好在有热心人士出马,所以我们只需执行以下命令就可以给我们的私有库提供一个UI了:
registry1 2 3 4 5 6
| docker run -d \ -p 8080:8080 \ --name web \ -e REGISTRY_HOST=172.17.0.1 \ -e REGISTRY_PORT=5000\ hyper/docker-registry-web
|
然后打开http://192.168.33.18:8080
,应该就能看到如下界面:
上面是个简易版,如果有更深入的需求,可以尝试SUSE的Portus。除了界面以外,它还提供了更细粒度的权限控制、用户认证等功能。
认证
我们刚刚配好的insecure registry是不支持认证的,如果要上产品环境,找CA申请一个证书吧。我们自己测试的话,可以用自签名证书。我们准备使用IP代替域名,所以需要在证书里面包含我们的IP:
registry1 2 3 4 5
| sudo mkdir /certs sudo sed -i '/^\[ v3_ca \]$/a subjectAltName = IP:192.168.33.18' /etc/ssl/openssl.cnf sudo sh -c "openssl req \ -newkey rsa:4096 -nodes -sha256 -keyout /certs/domain.key \ -x509 -days 365 -out /certs/domain.crt"
|
随便填点值完成这繁琐的流程,就能看见certs里面多了两个文件。现在可以用以下命令来启动registry:
registry1 2 3 4 5 6 7 8 9 10
| docker rm -f registry docker run -d \ -p 5000:5000 \ --name registry \ --restart=always \ -v /var/lib/registry:/var/lib/registry \ -v /certs:/certs \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ registry:2.3.0
|
客户端现在就不需要--insecure-registry
了,但是由于这是自签名证书,客户端还需要把证书文件复制过去:
client1 2 3 4
| sudo sed -i '$d' /etc/default/docker sudo mkdir -p /etc/docker/certs.d/192.168.33.18:5000/ sudo scp vagrant@192.168.33.18:/certs/domain.crt /etc/docker/certs.d/192.168.33.18:5000/ca.crt sudo service docker restart
|
注意vagrant的默认密码也是vagrant。现在push就没有问题了:
client1
| docker push 192.168.33.18:5000/busybox:1.24.1
|
提示镜像已经存在,并没有阻止我们提交。接下来我们加上认证。首先在registry生成用户名hello和密码world:
registry1 2
| sudo mkdir /auth sudo sh -c "docker run --entrypoint htpasswd registry:2.3.0 -Bbn hello world > /auth/htpasswd"
|
还得指定认证方式和认证文件等参数,重新启动registry容器:
registry1 2 3 4 5 6 7 8 9 10 11 12 13 14
| docker rm -f registry docker run -d \ -p 5000:5000 \ --name registry \ --restart=always \ -v /var/lib/registry:/var/lib/registry \ -v /auth:/auth \ -v /certs:/certs \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ registry:2.3.0
|
这回客户端用docker push 192.168.33.18:5000/busybox:1.24.1
来尝试push就会失败啦。但是我们可以用用户名hello和密码world登录啦:
client1
| docker login -u hello -p world -e email_whatever 192.168.33.18:5000
|
再次push,就没有问题了:
client1
| docker push 192.168.33.18:5000/busybox:1.24.1
|