详解docker pull

使用Docker拉取镜像的时候,我们一条命令就可以完成该工作。

有的时候可能会遇到需要登录的情况。

此时,只需要执行 docker login docker.gz.test.cn 就可以了。
如果你的好奇心多一点,会看到拉取某些镜像的时候是分层的。

那么,在这个过程中,docker是怎么认证的?又是如何实现分层拉取的呢?

Docker Registry的认证

参考文档

  • 尝试从Registry拉取镜像
  • 如果该Registry需要认证,那么会返回401未认证,并且在HTTP响应头中返回如何进行认证的信息。
  • Registry 客户端带到认证服务请求一个Bearer Token。
  • 认证服务认证通过,返回Token
  • Docker客户端在HTTP 请求头中带上 Bearer Token到registry
  • Registry验证Bearer Token之后,就可以开始拉取镜像的操作了。

docker pull

参考文档

首先,镜像是一个 JSON 格式的清单(manifest)和单个的层文件的组合。拉取镜像的过程就围绕着检索这两个组件而进行。

那么,在拉取镜像的时候,需要首先获取该镜像的清单数据。该接口的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GET /v2/<name>/manifests/<reference>
Authorization: Bearer <Token>
{
"name": <name>,
"tag": <tag>,
"fsLayers": [
{
"blobSum": <digest>
},
...
]
],
"history": <v1 images>,
"signature": <JWS>
}

获取该清单之后,我们就知道,该镜像都有哪些分层数据组成,然后将这些数据一层一层下载下来即可。

1
2
3
4
GET /v2/<name>/blobs/<digest>
//This endpoint may issue a 307 (302 for <HTTP 1.1) redirect to another service for downloading the layer and clients should be prepared to handle redirects.

这里明确指出,拉取镜像的时候会出现307或者302,在 Harbor存储使用S3的坑会 我们曾遇到类似的问题。

验证

看完官方文档,我们来验证一下。

拉取一个镜像:

1
docker pull docker.gz.test.cn/ccloud/ccloud-server:master-20f6fe2

打开Harbor的nginx的日志,可以看到如下的信息:

1
2
3
4
5
6
7
8
9
Dec 22 11:56:37 172.20.0.1 proxy[7494]: 172.18.144.48 - "GET /api/repositories/ccloud/ccloud-server/tags?detail=1 HTTP/1.1" 200 436 "https://docker.gz.cvte.cn/harbor/projects/11/repositories " "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36" 0.085 0.085 .
Dec 22 11:56:44 172.20.0.1 proxy[7494]: 10.10.14.39 - "GET /v2/ HTTP/1.1" 401 87 "-" "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \x5C(linux\x5C))" 0.003 0.003 .
Dec 22 11:56:44 172.20.0.1 proxy[7494]: 10.10.14.39 - "GET /service/token?account=admin&scope=repository%3Accloud%2Fccloud-server%3Apull&service=harbor-registry HTTP/1.1" 200 972 "-" "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \x5C(linux\x5C))" 0.032 0.032 .
Dec 22 11:56:44 172.20.0.1 proxy[7494]: 10.10.14.39 - "GET /v2/ccloud/ccloud-server/manifests/master-20f6fe2 HTTP/1.1" 200 1374 "-" "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \x5C(linux\x5C))" 0.025 0.025 .
Dec 22 11:56:44 172.20.0.1 proxy[7494]: 10.10.14.39 - "GET /v2/ccloud/ccloud-server/blobs/sha256:2ae74aa5b86007104f432e295a67af7cb68a07b6325090170aa2ee303d48a597 HTTP/1.1" 200 6196 "-" "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \x5C(linux\x5C))" 0.005 0.005 .
Dec 22 11:56:44 172.20.0.1 proxy[7494]: 10.10.14.39 - "GET /v2/ccloud/ccloud-server/blobs/sha256:e3bb99408671db266f7707cc262086fbe4cff3235314ae9bcd8054b0952c7dd0 HTTP/1.1" 200 9441203 "-" "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \x5C(linux\x5C))" 0.062 0.062 .
Dec 22 11:56:44 172.20.0.1 proxy[7494]: 10.10.14.39 - "GET /v2/ccloud/ccloud-server/blobs/sha256:85ab9793b0946166a9de84d8f30ac3a4fd0a013d06b3760e4e66ef2742afa03c HTTP/1.1" 200 7011088 "-" "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \x5C(linux\x5C))" 0.143 0.143 .
Dec 22 11:56:44 172.20.0.1 proxy[7494]: 10.10.14.39 - "GET /v2/ccloud/ccloud-server/blobs/sha256:1a05593689a6b5b5ac77f8c5de91364b73c6a56fc34292d553615dfd3ad05a96 HTTP/1.1" 200 38996911 "-" "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \x5C(linux\x5C))" 0.518 0.518 .
Dec 22 11:56:44 172.20.0.1 proxy[7494]: 10.10.14.39 - "GET /v2/ccloud/ccloud-server/blobs/sha256:dc03da3116f7f80e5c3d2be41576aa29a06a957f105da6eebc4ce2350d6ce7b9 HTTP/1.1" 200 60791498 "-" "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \x5C(linux\x5C))" 0.697 0.697 .
  • 第一行: Harbor本身检查该tags是否存在

  • 第二行:尝试pull,registry v2接口返回401未认证。

  • 第三行:Registry客户端到获取token,返回Docker客户端。

  • 第四行:Docker客户端获取镜像的清单。

  • 第五行~最后:Docker客户端按照清单,逐层下载镜像数据。

当这一系列过程都完成的时候,镜像就被拉取下来了。

各位感兴趣的还可以去探索一下:

1、已经存在的镜像层,怎么处理?

2、自己动手写一个Docker Pull的客户端(单线程版和多线程版)