Docker源代码分析 -- pull repository


本系列文章是从源代码入手分析Docker部分功能或者模块的具体实现

今天我们就先来看看在我们输入了docker pull debian之后到底发生了些什么事情

注意: 本文是基于docker v0.5.1版本分析

切入点

熟悉docker的同学都知道, 我们在运行一个container之前是需要下载container的image的.

我们可以通过以下的命令实现下载镜像的功能:

docker pull debian

然后我们就可以运行基于debian的容器:

docker run -i -t --rm --name debian debian /bin/bash

那么现在我们就看看, 到底在pull后面发生了多少不为人知的秘密.

我们知道, commands.go是命令行参数的入口:

commands.go:

CmdPull作为客户端的入口, 调用utils.ParseRepositoryTag处理参数, 分离出fromImagetag两个参数, 然后调用服务端的/images/create接口.

api.go:

api.go作为服务端路由解析器, 将/images/create转发到PostImagesCreate函数上.

PostImagesCreate进行一些参数处理, 之后开始调用获取镜像函数server.go/ImagePull.

server.go:

func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error

这是ImagePull的方法声明.

ImagePull方法的重要步骤:

  1. 通过registry.NewRegistry创建Registry对象r.
  2. 为获取镜像这个行为上锁(srv.poolAdd)并且在结束时释放锁(srv.poolRemove).
  3. 通过registry.ResolveRepositoryName解析出仓库(repository)结构.
  4. 通过srv.pullRepository获取仓库结构.
  5. 如果失败了, 则通过srv.pullImage下载镜像.

下面我们继续来分析srv.pullRepositorysrv.pullImage方法.

func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, localName, remoteName, askedTag, indexEp string, sf *utils.StreamFormatter) error

这是pullRepository的方法声明.

pullRepository方法的重要步骤:

  1. 通过上面创建的Registry对象r的方法GetRepositoryData获取仓库的数据repoData.
  2. 这时候通过graph对象的UpdateChecksums方法更新仓库上所有镜像的校验码.
  3. 接下来是通过r的方法GetRemoteTags获取repoData的仓库列表上, 每个仓库的tag.
  4. 接下来会判断一下在CmdPull解析到的tag是否在上面的tags列表上, 如果存在则下载该tag和相关联的images, 否则就下载所有tag.
  5. 接下来就是通过srv.pullImage方法来下载由步骤4中指定的tag.

接下来我们分析一下pullImage函数

func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) error

这是pullImage的函数声明

pullImage方法的重要步骤:

  1. 通过r.GetRemoteHistory获取镜像的父节点history对象, 注意: 这里的父节点是包含自己的.
  2. 然后就是遍历history对象, 逐个下载镜像相关内容. > 1. 通过r.GetRemoteImageJSON获取镜像的json内容imgJSON, 还有镜像大小imgSize > 1. 由imgJSON通过NewImgJSON方法创建Image对象img > 1. 通过img获取到镜像layer结构 > 1. 最后通过graph.Register下载并保存整个镜像的内容及其相关的上下文.

最后, 我们来分析一下graph.Register方法.

graph.go

这是Register的方法声明.

func (graph *Graph) Register(layerData Archive, store bool, img *Image) error

这是Register方法的重要步骤:

  1. 先通过ValidateID验证一下镜像ID的有效性.
  2. 通过StoreImage保存下载的镜像在临时目录.
  3. 然后将下载完成的镜像保存在对应的镜像目录.
  4. 最后再添加镜像到graph的缓存里面.

that’s all