DockerregistryV2源码解读【⼀】整体架构与启动过程Docker registry概述
⽤⼀句话解释Docker registry就是:存放docker image的远程仓库。在使⽤docker的过程中,我们⼀定会⽤到docker Registry,当我们使⽤docker的pull命令(下载镜像),或者run⼀个本地没有的镜像时,docker engine会从默认的仓库下载对应的镜像。
⽬前,docker pull命令默认仓库是docker的官⽅仓库,这样就导致⼀个问题,下载镜像速度⽐较慢。所以在⼤型分布式docker集中,通常都会配置⼀个私有的docker registry,这样能提⾼镜像下载速度,从⽽提升应⽤的启动速度;也⽅便管理镜像。
docker registry的安装⽅式也很简单,docker公司已经把registry封装在⼀个docker容器中了,我们只需要下载这个容器,然后启动,就可以使⽤了,⼗分⽅便。但是,这样启动的⽅式,只能有⼀个registry,在稍⼤⼀点的集中,单点故障和性能瓶颈问题就⽐较突出了,扩展成⾼可⽤的分布式结构势在必⾏,所以很多公司在优化registry⽅⾯做了很多⼯作,⽬前开源的有VMware的Habor[1]和京东的
speedy[2]。
Docker Registry发展历史
- 2013年3⽉13⽇,docker在github上有了第⼀个release[3]
- 2013年7⽉3⽇,docker在github上发布了docker registry v1[4]
- 2015年1⽉30⽇,docker registry v2(项⽬名叫docker distribution)有了第⼀个release,同时停⽌更新docker registry v1[5]
在使⽤docker registry v2的时候需要注意,只有docker1.6以上版本才⽀持registry v2,这并不意味着1.6以后只能⽤v2版本,我们从源代码⾥可以看出,docker Engine在下载镜像的时候,会⾃动判断远端仓库是v1还是v2版,从⽽使⽤不同的下载策略,这个策略可太重要了。下⾯我们就讲⼀下v1和v2下载策略的区别。
图1 v1版串⾏下载layer
我们知道,⼀个docker image是由很多的layer组成的,registry v1的下载过程如图1所⽰,下载镜像时也是以layer为最⼩单元下载的,在v1的时代docker image,镜像结构有⼀种链表⼀样的组织,当下载完⼀个layer时,才能得到parent信息,然后再去下载parent layer,这样结构显然效率不⾼,所以在v2中,改变了这种结构,在image的manifest⽂件中存储了所有的layer信息,这样拿到所有的layer信息,就可以并⾏下载了,提⾼了下载效率,过程如图⼆所⽰。
图⼆ v2版升级为并⾏下载layer
还有就是使⽤的开发语⾔也有改变,从python变成go。
Docker registryV2整体架构图
图3 docker registry 2.0架构图[6]
从架构图上我们发现,registry v2的架构还是很简单的,它的核⼼是⼀个web服务器,从阅读源码也会发现,具体实现是⽤go语⾔的
net/http包中的http.Server,在registry初始化时绑定了rest接⼝。请求会触发相应的handler,handler会从后端存储中取出具体的数据并写⼊response。这个过程也很容易理解。
Registry启动源码分析
下⾯我们就讲⼀下registry的启动过程,我也是第⼀次细读开源项⽬的源码,也讲⼀下我作为⼀个新⼿是如何阅读源代码的。
既然registry是以⼀个docker container形式运⾏的,要看它是如何启动的,当然⾸先看它的Dockerfile
图4 Dockerfile
主要做了两件事:
1. 拷贝代码到容器中;
2. 编译成⼆进制可执⾏⽂件;
3. 指定程序⼊⼝;
当我们运⾏docker run –p 5000:5000 registry:2时,容器内部registry的启动命令其实是registry serve
/etc/docker/l,
进⼊到项⽬中main函数,发现程序的⼊⼝⽂件是/cmd/,main函数也只有⼀句话:
其实在加载RootCmd时已经做了命令绑定,⼦命令serve对应的实现在/ L 30,调⽤的内容主要有:
1. 读取配置⽂件;
2. 把配置参数传递给NewRegistry()函数,⽤来实例化⼀个registry对象(虽然对象这个词⽤在go语⾔⾥并不合适,单张这样类⽐更好理解);
3. registry进⼊端⼝监听状态,启动完毕。
Registry对象的结构定义在/ L68,Registry结构体声明了三个成员,如图5所⽰,有配置参数,app,还有⼀个http Server,显⽽易见,最重要的就是app这个成员了。docker进入容器
图5 Registry结构体
App结构体的定义在/registry/ L54,成员长,主要有:
1. driver 指明了后端存储,可以通过driver进⾏读/写/查询等操作
2. router 包含了http路由规则,把不同的请求分发到不同的handler上
3. registry 主要的app后端
4. accessController 访问控制器
NewApp()函数完成了App实例的初始化,实现在/registry/ L91。该函数的⼤体流程如下:
1. 声明⼀个app实例
2. 给app实例绑定web handler
3. 初始化app的后端存储驱动
4. 初始化app的密钥
5. 配置app的redis缓存
6. 初始化app的后端存储重定向功能
7. 根据参数初始化app的后端registry
8. 返回app实例
⾄此,/的NewRegistry()也调⽤结束,返回了registry实例,调⽤registry的ListenAndServe()进⼊监听状态,直到registry结束。
体验&总结
作为⼀个阅读源码的新⼈,深感有⼀个好的IDE对于阅读源码是多么重要,我使⽤的是Idea14+GO插件作为开发环境,有⼀点需要注意的是,docker registry项⽬的引⽤都是github/***开头,所以需要把源码放在go语⾔的src下才能引⽤的到,这样也⽅便代码间跳转。同时,使⽤IDE的代码间Forward/Backward功能,可以快速跳转到上⼀个光标位置,这样也会提⾼代码阅读效率。
Docker在云计算中扮演了越来越重要的⾓⾊,Docker registry是整个平台重要的⼀环,但是它现在还存在很多问题,优化registry,是⼀个商⽤的容器云平台必须要做的⼀件事,阅读它的代码便是做优化的前提,下⼀期,为您带来《docker registry后端存储源码解读》。
Docker registryV2源码解读【下】镜像下载
镜像下载过程⽰意图
Docker engine发⽣了什么
Docker registry的相关接⼝
从driver实现的接⼝反推调⽤关系
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论