Docker registry是专门用于存放docker镜像的,docker官方提供了docker hub,是全球最大的docker镜像存储中心。但是在中国既没有服务器也没有CDN,所以导致pull镜像特别的慢,而且很不稳定。解决这个问题的方式一般有两种:
- 搭建自己私有的docker registry,存储镜像,并定期同步官方常用的镜像。
- 搭建docker mirror。
其实,选用哪一种或者both主要取决于自己的使用场景。如果你想要一个类似docker hub的私有registry,那肯定是第一种。如果你只是想解决从docker hub拉取镜像慢的问题,那就选择第二种,因为第一种的维护成本比较高。当然,不管是哪一种,我们都可以使用docker官方开源的registry镜像去实现。如果你想部署私有的registry,可以关注一下VMware开源的Harbor项目,其在开源registry的基础上增加了一些实际应用场景中需要的一些特性,比如项目权限、角色管理等。本文介绍如何使用部署registry mirror。
在这之前,需要强调一下目前如果registry部署为mirror方式,将只能pull镜像而不能push镜像。
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缓存或者更新数据。
registry mirror的工作原理我们就介绍完了,下面介绍如何利用docker开源的registry镜像部署自己的registry mirror。
registry mirror部署
NB:假设我们将缓存的数据存放到/data
目录。
- 从官方拉取registry的镜像,目前最新的registry镜像是2.5版本(我使用的是2.5.0)。
获取registry的默认配置:
docker run -it --rm --entrypoint cat registry:2.5.0 /etc/docker/registry/config.yml > config.yml
文件的内容大概是下面这样:
version: 0.1 log: fields: service: registry storage: cache: blobdescriptor: inmemory filesystem: rootdirectory: /var/lib/registry http: addr: :5000 headers: X-Content-Type-Options: [nosniff] health: storagedriver: enabled: true interval: 10s threshold: 3
我们在最后面加上如下配置:
proxy: remoteurl: https://registry-1.docker.io username: [username] password: [password]
username
和password
是可选的,如果配置了的话,那registry mirror除了可以缓存所有的公共镜像外,也可以访问这个用户所有的私有镜像。启动registry容器:
docker run --restart=always -p 5000:5000 --name v2-mirror -v /data:/var/lib/registry -v $PWD/config.yml:/etc/registry/config.yml registry:2.5.0 /etc/registry/config.yml
当然我们也可以使用docker-compose启动:
version: '2' services: registry: image: library/registry:2.5.0 container_name: registry_mirror restart: always volumes: - /data:/var/lib/registry - ./config.yml:/etc/registry/config.yml ports: - 5000:5000 command: ["serve", "/etc/registry/config.yml"]
当我们看到如下日志输出的时候就说明已经启动成功了:
time="2016-12-19T14:22:35Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.6.3 instance.id=da5468c4-1ee1-4df2-95cf-1336127c87bb version=v2.5.0 time="2016-12-19T14:22:35Z" level=info msg="redis not configured" go.version=go1.6.3 instance.id=da5468c4-1ee1-4df2-95cf-1336127c87bb version=v2.5.0 time="2016-12-19T14:22:35Z" level=info msg="Starting upload purge in 39m0s" go.version=go1.6.3 instance.id=da5468c4-1ee1-4df2-95cf-1336127c87bb version=v2.5.0 time="2016-12-19T14:22:35Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.6.3 instance.id=da5468c4-1ee1-4df2-95cf-1336127c87bb version=v2.5.0 time="2016-12-19T14:22:35Z" level=info msg="Starting cached object TTL expiration scheduler..." go.version=go1.6.3 instance.id=da5468c4-1ee1-4df2-95cf-1336127c87bb version=v2.5.0 time="2016-12-19T14:22:35Z" level=info msg="Registry configured as a proxy cache to https://registry-1.docker.io" go.version=go1.6.3 instance.id=da5468c4-1ee1-4df2-95cf-1336127c87bb version=v2.5.0 time="2016-12-19T14:22:35Z" level=info msg="listening on [::]:5000" go.version=go1.6.3 instance.id=da5468c4-1ee1-4df2-95cf-1336127c87bb version=v2.5.0
至此,registrymirror就算部署完了。我们也可以用curl验证一下服务是否启动OK:
curl -I http://registrycache.example.com:5000/v2/
HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json; charset=utf-8
Docker-Distribution-Api-Version: registry/2.0
Date: Thu, 17 Sep 2015 21:42:02 GMT
registry mirror使用
要使用registry mirror,我们需要配置一下自己的docker daemon。
对于Mac:在docker的客户端的Preferences——>Advanced——>Registry mirrors
里面添加你的地址,然后重启。
对于Ubuntu 14.04:在/etc/default/docker
文件中添加DOCKER_OPTS="$DOCKER_OPTS --registry-mirror=http://registrycache.example.com:5000”
,然后重启docker(services docker restart
)。
其他系统和发行版的配置方法请Google之。
然后我们pull一个本地不存在的镜像,这时去查看registry mirror服务器的data目录下面已经有了数据。执行如下命令也可以看到效果:
curl https://mycache.example.com:5000/v2/library/busybox/tags/list
需要说明的是缓存的镜像的有效期默认是一周(168hour),而且如果registry被配置成mirror模式,这个时间是不能通过maintenance部分来改变的:
maintenance:
uploadpurging:
enabled: true
age: 168h
interval: 24h
dryrun: false
readonly:
enabled: false
我研究了好久发现怎么改都不能生效,最后发现mirror模式下这个时间竟然在registry的代码里面写死了:
// todo(richardscothern): from cache control header or config
const repositoryTTL = time.Duration(24 * 7 * time.Hour)
囧o(╯□╰)o~不过看这TODO,应该是后面会改的~
当然如果你想你的registry mirror是https的话,在config.yml的http部分增加tls配置即可:
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
tls:
certificate: /etc/registry/domain.crt
key: /etc/registry/domain.key
OK,至此registry mirror就介绍完了。
我部署
registry
镜像遇到个问题,只有library/xxxxx
这样的镜像能pull下来,其他的社区镜像,比如microsoft/dotnet
这种镜像死活pull不下来,不知道是什么问题,求大神指点。死活pull不下来最大的两种可能是:1. 镜像不存在。2. 网络问题,比如google镜像仓库的国内没梯子就pull不下来。所以,你可以先单独
docker pull
一下,看是是哪个问题。我估计可能是网络问题。可能是我没说明白吧。我的意思registry镜像模式下,docker官方的镜像,比如
library
目录的镜像,如(nginx,mysql,mariadb,node .etc)等没问题,但是社区镜像,例如微软旗下的microsoft
,比如gitlab的gitlab
,他们的镜像都无法pull。另外,我的registry的上游设置的是
registry.docker-cn.com
。哦,这样啊。那从
registry.docker-cn.com
直接拉能拉下来吗(直接把本地镜像库设置成这个)?如果也不行,那就和你搭建方式没关系了。如果可以,那就看下你registry的日志,看下拉microsoft/dotnet
之类镜像的时候有没有什么错误日志。ps: 我用的是harbor
registry配置如下