Docker Mirror 容器镜像服务解析
在实际生产环境下,机器都不能访问外部互联网,或者访问互联网络的带宽有限,同时有大量的容器镜像需要从外部下载,如果每次开发、测试、部署时都需要从互联网下载容器镜像,则将占用大量的带宽而且效率较低。同时,在某些场景如物联网场景中,需要使用移动网络接入互联网,这时带宽可能是系统部署的瓶颈。
更为严重的问题是,Docker Hub公有云容器镜像服务对客户端有限流设置,当镜像拉取操作达到一定流量时,会导致服务无法使用。Docker Hub最新免费服务条款(11月1日生效),匿名用户每6小时只允许拉取100次,登录用户200次,只有付费用户不受此限制。
Mirror代理缓存仓库的作用就是为了以上问题,通过运行一个缓存仓库允许你在本地储存镜像,减少过多的通过互联网从Docker Hub拉取镜像,这个特性对于一些在他们环境中拥有数量庞大的Docker引擎的用户来说很有用。
image命名规则
容器镜像(iamge)的命名规则是:myregistrydomain:port/foo/bar,其中包含了几个关键信息:HUB域名或IP地址、端口号、仓库名、软件名、标记名。
例如,命令docker pull harbor.caogo.local:80/library/mongo:3.6的含义是:从harbor.caogo.local服务器,通过http端口,获取liblary仓库的mongo软件镜像,标签为3.6
docker.io是Docker的官方HUB,其中/library是官方仓库所在位置,而/calico等则是开发者在官方HUB的自定义仓库。因此,Docker规定官方镜像可以采用省略所有前缀的简写方式,即:docker pull docker.io/library/ubuntu == docker pull ubuntu
常用的镜像仓库HUB包括:
registry-1.docker.ioDocker官方镜像k8s.gcr.ioGoogle Kubernetes系统镜像quay.io重要的第三方镜像registry.cn-hangzhou.aliyuncs.com阿里云的系统镜像
docker pull 镜像拉取流程
根据Pulling An Image 官方文档的解释:
An “image” is a combination of a JSON manifest and individual layer files.
一个镜像image包含了以下要素:
- 名称 name:The name of the image.
- 标记 tag:The tag for this version of the image.
- 分层文件 fsLayers:A list of layer descriptors (including digest)
- 签名 signature:A JWS used to verify the manifest content
image已经建立了行业标准 - OCI(Open Container Initiative)镜像规范,确保Docker、Podman等不同容器产品之间共享image,可以参见附录继续研究
docker pull的过程很复杂,包括鉴权、校验,下载、合并镜像层,解压缩等等,但最核心的是两个步骤:
- 发送请求
GET /v2/<name>/manifests/<reference>,获取镜像的mainfest清单文件
reference可以是标记tag,或摘要digest。 - 发送请求
GET /v2/<name>/blobs/<digest>,获取镜像层文件
通过curl -X GET http://localhost:5000/v2/alpine/manifests/3.6,获取镜像的manifest清单文件,其格式为
1 | { |
Docker Registry 镜像注册服务
Registry是Docker提供的镜像注册服务,其设计目标是一套存储和分发镜像的处理机制,实现方式是在Docker引擎中运行一个来自docker.io/library/registry的实例。
需要注意的是,Registry管理镜像是基于文件系统的,数据存储目录位于实例所在容器的/var/lib/registry
1 | ~ # tree /var/lib/registry |
Registry的默认配置文件,位于实例所在容器的/etc/docker/registry/config.yml,默认基本内容是:
全量配置信息请见config.yml
1 | ~ # cat /etc/docker/registry/config.yml |
Docker提供的标准镜像服务称为
registry,当前版本是由go语言开发的v2(之前python开发的registry v1在网上已被标为废弃,最后版本是v1.6), 新版 registry v2对镜像存储格式进行了重新设计,并且和旧版还不兼容,docker从1.6版本开始支持registry v2。
基于Registry的直通缓存服务(pull-through cache)
如果您的环境中运行着多个Docker实例,例如多个物理机或虚拟机都运行Docker,则每个Docker守护程序都将访问Internet,并从Docker Hub获取本地没有的映像。
为此,我们可以部署一个本地Registry Mirror服务,并将所有Docker引擎的Mirror Sever指向该服务器,以免产生额外的互联网流量。
Registry mirror的原理
Docker Hub的镜像数据分为两部分:index数据和registry数据。前者保存了镜像的一些元数据信息,数据量很小;后者保存了镜像的实际数据,数据量比较大。平时我们使用docker pull命令拉取一个镜像时的过程是:先去index获取镜像的一些元数据,然后再去registry获取镜像数据。
所谓registry mirror就是搭建一个registry,然后将docker hub的registry数据缓存到自己本地的registry。
整个过程是:当我们使用docker pull去拉镜像的时候,会先从我们本地的registry mirror去获取镜像数据,如果不存在,registry mirror会先从docker hub的registry拉取数据进行缓存,再传给我们。
整个过程是流式的,registry mirror并不会等全部缓存完再给我们传,而且边缓存边给客户端传。
对于缓存,我们都知道一致性非常重要。
registry mirror与docker官方保持一致的方法是:registry mirror只是缓存了docker hub的registry数据,并不缓存index数据。
所以我们pull镜像的步骤是:
- 先连docker hub的index获取镜像的元数据,如果我们registry mirror里面有该镜像的缓存,且数据与从index处获取到的元数据一致,则从registry mirror拉取;
- 如果我们的registry mirror有该镜像的缓存,但数据与index处获取的元数据不一致,或者根本就没有该镜像的缓存,则先从docker hub的registry缓存或者更新数据。
1. 服务端的部署方法
在一个已经安装Docker引擎的服务器192.168.0.147上部署一个Regsitry服务,其配置文件中,可以增加proxy配置段支持代理缓存服务,格式为:
1 | proxy: |
注意,缓存默认保存7天,配置信息位于:
1 | storage: |
uploadpurging是一个后台过程,该过程会定期从注册表的uploads目录中删除孤立的文件。
默认情况下启用该后台进程,并设置以下参数。
TODO: 缓存的镜像的有效期默认是一周(168hour),这个配置似乎并不解决???
| 参数名称 | 必选项 | 描述 |
|---|---|---|
| enabled | yes | Set to true to enable upload purging. Defaults to true. |
| age | yes | Upload directories which are older than this age will be deleted.Defaults to 168h (1 week). |
| interval | yes | The interval between upload directory purging. Defaults to 24h. |
| dryrun | yes | Set dryrun to true to obtain a summary of what directories will be deleted. Defaults to false. |
获取Resistry默认配置文件的方法:
docker run -it --rm --entrypoint cat registry:2 /etc/docker/registry/config.yml
因此,你可以在启动Registry服务时挂载自定义yml文件的方式,实现pull-through cache。
1 | docker run -d --restart=always --name registry \ |
还有一个更方便的方法,不用修改配置文件,直接在启动Registry服务时设置环境变量即可,例如:
1 | docker run -p 5000:5000 -d --restart=always --name registry \ |
2. 服务端的测试方法
检查Registry服务是否正常启动,可以通过以下方法:
1 | [root@bl-local ~]# curl -ski -X GET http://localhost:5000/v2/_catalog |
3.客户端的配置方法
作为直通镜像使用方,需要修改Docker引擎配置文件/etc/docker/daemon.json,并在其中增加registry-mirrors信息段
1 | root@d7y-2:~# cat <<EOD >/etc/docker/daemon.json |
当然,你也可以在Docker守护进程上,增加启动配置信息,例如:--registry-mirror=https://<my-docker-mirror-host>:<port-number>
结论
- Docker Registry提供的Pull-throgh cache服务部署很方便,但是缺乏UI管理界面导致维护比较困难,一般适合小规模本地部署。
- 受到Docker pull机制的制约,Mirror仅支持Docker官方镜像,第三方或者私有仓库的mirror服务比较困难。
- 由于Registry负责管理镜像的mainfest信息和镜像层文件,而所有docker pull都是基于上述标准流程和接口方式,因此Harbor、Dragnonfly等镜像管理产品都是必须基于Registry开发,并增加了一些复制、分发、UI管理等功能。