docker pull 到底向 registry 发了什么?这篇在本机起一个 registry,直接读它的访问日志,把 push 和 pull 的每一个 HTTP 请求列出来,再亲手用 HTTP 上传一个镜像、读 manifest 和 blob。
GET /v2/ → 200;响应头 Docker-Distribution-Api-Version = registry/2.0;正文 = {}registry:2 是一个普通的 HTTP 服务。GET /v2/ 返回 200 和响应头 Docker-Distribution-Api-Version: registry/2.0。我是在本机的 registry:2 容器上做的实验,不同的 registry 实现(Docker Hub、Harbor 等)的细节可能不同,没有测。
push 退出码 0;共 14 个请求,按首次出现顺序:
HEAD /v2/c15/app/blobs/sha256:7be9bb… → 404
HEAD /v2/c15/app/blobs/sha256:db95e1… → 404
HEAD /v2/c15/app/blobs/sha256:3f26bc… → 404
HEAD /v2/c15/app/blobs/sha256:db4141… → 404
POST /v2/c15/app/blobs/uploads/ → 202 (共 4 次)
PUT /v2/c15/app/blobs/uploads/<uuid> → 201 (共 4 次)
HEAD /v2/c15/app/manifests/1 → 404
PUT /v2/c15/app/manifests/1 → 201先对每个 blob 发 HEAD,404 就说明 registry 里没有;再 POST 开启上传、PUT 写入;所有 blob 到位后,最后才 PUT manifest。顺序保证了 manifest 引用到的内容在它出现时一定已经存在。请求次数来自 registry 的访问日志。
pull 退出码 0;输出里出现 Already exists 的行数 2;共 1 个请求:
HEAD /v2/c15/app/manifests/1 → 200
镜像已经是最新时再 pull 一次:1 个请求:
HEAD /v2/c15/app/manifests/1 → 200删掉镜像的名字后重新 pull,registry 只收到一个 HEAD manifests/1,docker 输出 2 行 Already exists——本机还留着内容。别用这种方式去估算“拉一个镜像要多少请求”。
手工上传:层 119 字节(上传返回 201),config 194 字节(返回 201),manifest 418 字节(返回 201)
docker pull 退出码 0;输出里 Already exists 的行数 0;共 4 个请求:
HEAD /v2/c15/hand/manifests/v1 → 200
GET /v2/c15/hand/manifests/sha256:596dee… → 200
GET /v2/c15/hand/blobs/sha256:0046d1… → 200
GET /v2/c15/hand/blobs/sha256:de1bc8… → 200
拉下来的镜像:docker image inspect 的层数 = 1,diff_id 与我写进 config 的一致吗?True要看完整的 pull,就得拉本机从没有过的内容。我用 Python 手工往 registry 上传一个只有一层的镜像(用到的正是 c14 讲的结构:config、layer 两个 blob 加一个 manifest),再 docker pull:HEAD manifest(按 tag)、GET manifest(按摘要)、GET config blob、GET layer blob,共 4 个请求。
GET /v2/_catalog → {"repositories":["c15/app","c15/hand"]}
GET /v2/c15/app/tags/list → {"name":"c15/app","tags":["1"]}
GET manifests/1(Accept: OCI manifest)→ 200;Content-Type = application/vnd.oci.image.manifest.v1+json;正文 856 字节;Docker-Content-Digest = sha256:86a95bc35fcb…;自己对正文算 sha256 一致吗?True
不带 Accept 头再请求同一个 tag → 404;Content-Type = application/json; charset=utf-8;正文是同一份吗?False
只接受 Docker schema2 manifest 类型(Accept: application/vnd.docker.distribution.manifest.v2+json)→ 404;Content-Type = application/json; charset=utf-8
HEAD manifests/<digest> → 200;Content-Length = 856
GET blobs/<config digest> → 200;1038 字节;sha256 与 digest 一致吗?True
GET manifests/nope → 404;正文 = {"errors":[{"code":"MANIFEST_UNKNOWN","message":"manifest unknown","detail":{"Tag":"nope"}}]}
GET blobs/<不存在的摘要> → 404_catalog、tags/list 返回 JSON;manifest 的响应头 Docker-Content-Digest 就是正文的 sha256,blob 同理,客户端可以自己校验。这个 registry 在 Accept 头不包含 OCI manifest 类型时返回 404 和一份 JSON(正文我没有记录),不是 406,所以自己写客户端要带对 Accept。