所有文章

Docker - 私有仓库的搭建与管理

Docker Registry 简介

怎样在几个机器之间使用同一个镜像?难道要先上传到官方的仓库,另几个机器去下载吗?当然不是了,最好的方法就是搭建一个本地的仓库。 Docker Registry 就是官方提供的搭建本地仓库的方案,它有两种搭建方式, http 与 https ,如果你只简单的在本地使用,第一种就足够了,如果考虑到安全方面,就用 https ,这种方式当然就需要为你的仓库服务器申请证书了,或者自建一个证书机构,自已为自已颁发证书。本文只介绍 http 方式,并介绍仓库的上传、下载、查询、删除等常用操作。

环境描述

假设我有三台机器,他它们的/etc/hosts文件包含如下内容:

[user@ser1 ~]$ cat /etc/hosts
10.100.100.101  ser1.node.com  registry.io
10.100.100.102  ser2.node.com
10.100.100.103  ser3.node.com

很显然,我要在第一台机器上部署 Docker Registry ,并且这三台机器都已安装好了Docker服务

Docker Registry 搭建

官方提供的私有仓库是以镜像像方式提供的,也就是说你只要把这个镜像下载下来启动就可以了,当然还要自已定制一些运行参数。 关于所有可配置项,可以去官网查看,具体地址在本文尾部。

在第一台机器上运行官方提供的 Registry 容器

sudo mkdir -p /data/docker-registry

docker run -dt \
--name registry \
-p 10.100.100.101:5000:5000 \
--restart=always \
-v /data/docker-registry:/var/lib/registry \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
-e REGISTRY_STORAGE_DELETE_REDIRECT=true \
registry:2

参数说明: -p 10.100.100.101:5000:5000 这是把容器的端口映射到物理机,这样在其它机器直接访问物理机就相当于访问仓库了 --restart=always 这个参数可以让容器在发生意外的时候还可以自已启动,比如当我重启 Docker 服务的时候 -v /data/docker-registry:/var/lib/registry 找一个容量大的挂载点映射给容器,用来存放上传的镜像 -e 最后这两个是让我们可以远程删除已经上传的镜像

看下启动了没有

[user@ser1 ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                           NAMES
867106b006da        registry:2          "/entrypoint.sh /etc/"   10 seconds ago      Up 10 seconds       10.100.100.101:5000->5000/tcp   registry

候改其它每台机器上的 Docker 配置文件,使得其它机器信任此仓库,在其配置文件中找到ExecStart一行,在后面追加内容 –insecure-registry registry.io:5000 这里以 CentOS7 为例,如下:

[user@ser1 ~]$ sudo vi /usr/lib/systemd/system/docker.service
...
[Service]
ExecStart=/usr/bin/dockerd --insecure-registry registry.io:5000 
ExecReload=/bin/kill -s HUP $MAINPID
...

重新启动 Docker 服务,使配置生效

sudo systemctl daemon-reload ; sudo systemctl restart docker

我们在第二台机器上,上传一个镜像试一下

[user@ser2 ~]$ docker images     # 看一下本地有哪些镜像
paulczar/glusterfs         latest              dc2e1257bba4        2 years ago         277 MB
[user@ser2 ~]$ docker tag paulczar/glusterfs:latest registry.io:5000/glusterfs    # 修改镜像名
[user@ser2 ~]$ docker push registry.io:5000/glusterfs    # 上传到自已的仓库
2e92feb4cbe1: Pushing [==============================>                    ] 189.1 MB/277 MB

OK!!,顺利上传至我们的私有仓库,而且速度很快

Docker Registry REST API 简介

有了自已的仓库,那我们想看一下仓库中已经上传了哪些镜像,或者想删除自已上传的镜像,怎么办?难道我要去看 /data/docker-registry 目录下的内容??不用担心,官方已经提供了方法, Registry 本身对外提供了 HTTP 操作接口,很方便。 先看一个例子,下面这条命令是测试 registry 版本是不是2.x版本的,这里我们在ser2这台机器上执行

[user@ser2 ~]$ curl -i -X GET registry.io:5000/v2/
HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json; charset=utf-8
Docker-Distribution-Api-Version: registry/2.0
X-Content-Type-Options: nosniff
Date: Sat, 21 Jan 2017 14:13:25 GMT

注意看输出信息的第一行,响应码200表示请求成功,也就是说我们的 registry 版本确实是2.x版本

查看仓库中的镜像

那我们再来一条命令,看一下我们已经上传的的有镜像

[user@ser2 ~]$ curl -H "Docker-Distribution-API-Version: registry/2.0" -X GET registry.io:5000/v2/_catalog {"repositories":["glusterfs"]}

它返回了一个Json串,里面包含了我们刚刚上传的镜像,是不是很棒??但聪明的你也许已经发现,这个返回的信息里应该包含镜像标签的啊,后面不是应该有个:latest吗? 不着急,再来一条命令,用来查询某一镜像的所有标签(或者说版本)

[user@ser2 ~]$ curl -H "Docker-Distribution-API-Version: registry/2.0" -X GET registry.io:5000/v2/glusterfs/tags/list {"name":"glusterfs","tags":["latest"]}

看到了吧,如果有多个tag的话,这里会全部列出来

删除仓库中的镜像

删除要稍麻烦一点,分两步

先获取某镜像的 digest (返回的消息头中的 Docker-Content-Digest 后面部分)

[user@ser2 ~]$ curl -H "Authorization: Basic <hash_here>" -i -X GET \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
http://registry.io:5000/v2/glusterfs/manifests/v1
HTTP/1.1 200 OK
Content-Length: 745
Content-Type: application/vnd.docker.distribution.manifest.v2+json
Docker-Content-Digest: sha256:97de138832d894bf87af449c012538fe1c94dd078a2dc8941f0790f498738c4a
Docker-Distribution-Api-Version: registry/2.0
Etag: "sha256:97de138832d894bf87af449c012538f31c94dd078a2dc8941f0790f498738c4a"
X-Content-Type-Options: nosniff
Date: Sat, 21 Jan 2017 14:41:32 GMT
 
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
   "config": {
      "mediaType": "application/vnd.docker.container.image.v1+json",
      "size": 1232,
      "digest": "sha256:78f3c04c6b8654dd86d0e09148dac08f34eaddd008aeb2926cd6a3a74j84f3d5"
   },
   "layers": [
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 1300037181,
         "digest": "sha256:065b2f8d16ae85886f19afb12a9ed3eaad2k04484a6143f218ebb39eac43a61d"
      },
      {
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": 5600560669,
         "digest": "sha256:f074337a2c9c17aaf3ce1ed9edcb8b08cbc5e3f730032b721917f24a8920a653"
      }
   ]
}

然后用上面返回的消息头中的 Docker-Content-Digest 后面的值,带入下一条命令

[user@ser2 ~]$ curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -i -X DELETE \
http://registry.io:5000/v2/glusterfs/manifests/sha256:97de138832d894bf87af449c012538fe1c94dd078a2dc8941f0790f498738c4a
HTTP/1.1 200 OK
...

返回码200表示删除成功了,但是!!这里注意一下,此时镜像并不一定是真的被从仓库中的物理机上删除了,大部分情况下只是让我们看不到了而已,为什么说不一定呢?这要从镜像的组成原理说起,一个镜像通常是由多个层组成的,理论上,只有我们删除的这个镜像的所有依赖的层都被删了,它才可能被真正删除,而在我多次实验中,即使把所有的层都删除了,镜像也没有从物理机上消失,不知为何,感兴趣的朋友可以试下。

参考资料

  1. Registry 所有可能配置项
  2. Docker REST API 使用

编写日期:2017-01-21