docker push 流程调用接口解析

时间:2024-10-22 17:00:39

docker push 接口详情

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iNQwLoON-1574158114105)(…/pic/)]

step 1. Api 版本确认

registry 服务器会通过/v2/接口根据返回的状态码来提供api的版本信息,请求格式如下

GET /v2
  • 1
  • 如果返回200 OK 则表明registry实现了V2接口,并且客户端可以安全的去使用其他所有的v2接口
  • 如果返回401 Unauthorized, 则表示客户端需要根据WWW-Authenticate header 重新请求接口。根据访问控制设置,用户即使这里通过验证,在访问不同的资源的时候可能仍需身份验证
  • 如果返回404 NOT FOUND ,则客户端应认为registry未实现v2接口

step 2. 校验layer是否存在

可以通过一个HEAD 请求来校验一个layer是否存在。 请求格式如下:

HEAD /v2/<name>/blobs/<digest> 
  • 1

如果返回的状态码为200 OK ,则表示指定的layer已存在,若已存在则client 则跳过该layer的上传。由于HEAD 请求根据HTTP规范没有body,因此HEADER中带了以下信息

200
Content-Length: <length of blob>
Docker-Content-Digest: <digest> 
  • 1
  • 2
  • 3

以下为抓包的结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hkjxgopU-1574158114106)(…/pic/)]

step 3. 发送post请求

该请求的参数是镜像命名空间,layer将在该命名空间被链接。该请求的格式是:

POST  /v2/<name>/blobs/uploads/
  • 1

如果返回202 Accepted 则表明请求成功,其HEADER携带了以下信息:

202 Accepted
Location: /v2/<name>/blobs/uploads/<uuid>
Range: bytes=0-<offset>
Content-Length: 0
Docker-Upload-UUID: <uuid>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 其中Location 返回下一步镜像上传的地址,其API格式为/v2/<name>/blobs/uploads/<uuid>
  • 如果本地客户端想和远程registry的上传状态相关联,可以使用Docker-Upload-UUID中的值。这个id可以作为上一次 location header 的key 来实现可恢复上传。

抓包结果:

POST /v2/hello-world/blobs/uploads/ HTTP/1.1
Host: 
User-Agent: docker/17.12.0-ce go/go1.9.2 git-commit/c97c6d6 kernel/3.10.0-693.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.12.0-ce \(linux\))
Content-Length: 0
Accept-Encoding: gzip
Content-Type: 
X-Forwarded-For: 172.17.0.1, 192.168.79.136
X-Forwarded-Proto: https
X-Real-Ip: 172.17.0.1

HTTP/1.1 202 Accepted
Content-Length: 0
Docker-Distribution-Api-Version: registry/2.0
Docker-Upload-Uuid: 87399c24-26ba-4f39-a1db-ca8b5469133d
Location: /v2/hello-world/blobs/uploads/87399c24-26ba-4f39-a1db-ca8b5469133d?_state=uog2S7ReISUOCxLS2uU3Z3S2h1_aV4Sq8T5CmA09B_p7Ik5hbWUiOiJoZWxsby13b3JsZCIsIlVVSUQiOiI4NzM5OWMyNC0yNmJhLTRmMzktYTFkYi1jYThiNTQ2OTEzM2QiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMTktMTEtMThUMTY6MzA6MzMuMTI4NzA5NTE1WiJ9
Range: 0-0
X-Content-Type-Options: nosniff
Date: Mon, 18 Nov 2019 16:30:33 GMT
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

step 4. 分块上传

客户端可以通过携带包含range的Header和该layer的文件,来完成对应分块的上传:

PATCH /v2/<name>/blobs/uploads/<uuid>
Content-Length: <size of chunk>
Content-Range: <start of range>-<end of range>
Content-Type: application/octet-stream

<Layer Chunk Binary Data>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

抓包结果:

PATCH /v2/hello-world/blobs/uploads/87399c24-26ba-4f39-a1db-ca8b5469133d?_state=uog2S7ReISUOCxLS2uU3Z3S2h1_aV4Sq8T5CmA09B_p7Ik5hbWUiOiJoZWxsby13b3JsZCIsIlVVSUQiOiI4NzM5OWMyNC0yNmJhLTRmMzktYTFkYi1jYThiNTQ2OTEzM2QiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMTktMTEtMThUMTY6MzA6MzMuMTI4NzA5NTE1WiJ9 HTTP/1.1
Host: 
User-Agent: docker/17.12.0-ce go/go1.9.2 git-commit/c97c6d6 kernel/3.10.0-693.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.12.0-ce \(linux\))
Content-Length: 977
Accept-Encoding: gzip
X-Forwarded-For: 172.17.0.1, 192.168.79.136
X-Forwarded-Proto: https
X-Real-Ip: 172.17.0.1

............>.l.0......cY..	U..../.....;...].=.M|.!>....W|..
...,.? D..'.C`...Ig....A......}..<.%<].:.w....
...........w
~.....\Wn.&........G..w...5.'......=..?...
hB...|...Q..x.~....?....+}..K..~..........K.......c..
.Y....w.../|.v..`..o}y.....}....*...Ea.....Mk...7...t...........	....?.........7&...<....Z\.....m:]:.......K_....
'.W...i......7...J&x]..\...DBc..fC.:.c..1.}.)..&....L..F.).$....R<......#Ut.6....H....c96|M|.7.............L.....d...Od.:.6C).q...._....X.8.."1...W....:...Y.nt_wp.4.@qf.`...*..$..U..).\... #...............|.... S......,...u.{Z=..gI.'...[.6.....U"R..O..>..cI...._...n...,5.!K.....U.1..m:....np+.0+.>...|..&.I.6..db^..*
b96giX...IC.].t0".....(..".....*.|.%...\K...6..ZP...@+f...W.'.....}.....s.,,...;.N_..cs.......r...:........;L3..D....\.{...!#...>.(.pp....l....v......Y....Y._43..r..{....j..Z...2....Z...Y<...........-..gsecv.....~.q......k...........g.............

HTTP/1.1 202 Accepted
Content-Length: 0
Docker-Distribution-Api-Version: registry/2.0
Docker-Upload-Uuid: 87399c24-26ba-4f39-a1db-ca8b5469133d
Location: /v2/hello-world/blobs/uploads/87399c24-26ba-4f39-a1db-ca8b5469133d?_state=lkoT88kcYkqkNrIHRetc7JVpbPvK690THF_WixElh4J7Ik5hbWUiOiJoZWxsby13b3JsZCIsIlVVSUQiOiI4NzM5OWMyNC0yNmJhLTRmMzktYTFkYi1jYThiNTQ2OTEzM2QiLCJPZmZzZXQiOjk3NywiU3RhcnRlZEF0IjoiMjAxOS0xMS0xOFQxNjozMDozM1oifQ%3D%3D
Range: 0-976
X-Content-Type-Options: nosniff
Date: Mon, 18 Nov 2019 16:30:33 GMT
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

服务器必须顺序的接受每一个分块,但是不限制每个layer是如何分割的。服务器可能设置了一个最小块的大小。如果服务器无法接收这个chunk,则会返回416的状态码,并在HEADER中返回一个Range 标识当前的状态

416 Requested Range Not Satisfiable
Location: /v2/<name>/blobs/uploads/<uuid>
Range: 0-<last valid range>
Content-Length: 0
Docker-Upload-UUID: <uuid>
  • 1
  • 2
  • 3
  • 4
  • 5

如果返回的值为202 Accepted 客户端需要从最后一个有效的range开始上传后续的分块。

202 Accepted
Location: /v2/<name>/blobs/uploads/<uuid>
Range: bytes=0-<offset>
Content-Length: 0
Docker-Upload-UUID: <uuid>
  • 1
  • 2
  • 3
  • 4
  • 5

step 5. 完成yayer上传

客户端发送一个PUT 请求并携带deigest参数来告诉服务器上传完成。如果不发送该接口,上传将会被视为没有完成:

PUT /v2/<name>/blobs/uploads/<uuid>?digest=<digest>
Content-Length: <size of chunk>
Content-Range: <start of range>-<end of range>
Content-Type: application/octet-stream

<Last Layer Chunk Binary Data>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果对应的layer 已经存在,可以发送一个Content-Length 为0 的请求。当最后的chunk被接收并验证通过将会返回302 Created 状态:


201 Created
Location: /v2/<name>/blobs/<digest>
Content-Length: 0
Docker-Content-Digest: <digest>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

HEADER 的Location 将会携带一个可以验证当前接收的layer存在的地址。Docker-Content-Digest 将会提供当前blob的digest,它可能会和提供的digest不同

PUT /v2/hello-world/blobs/uploads/83f294f5-2886-4d3b-bc9a-cc6c1ab93101?_state=EMZiJZM0HP1kElDmi_IuN3GbJ1JfEnn6XcYp_3FYlLl7Ik5hbWUiOiJoZWxsby13b3JsZCIsIlVVSUQiOiI4M2YyOTRmNS0yODg2LTRkM2ItYmM5YS1jYzZjMWFiOTMxMDEiLCJPZmZzZXQiOjE1MTAsIlN0YXJ0ZWRBdCI6IjIwMTktMTEtMThUMTY6MzA6MzNaIn0%3D&digest=sha256%3Afce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e HTTP/1.1
Host: 
User-Agent: docker/17.12.0-ce go/go1.9.2 git-commit/c97c6d6 kernel/3.10.0-693.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.12.0-ce \(linux\))
Content-Length: 0
Accept-Encoding: gzip
X-Forwarded-For: 172.17.0.1, 192.168.79.136
X-Forwarded-Proto: https
X-Real-Ip: 172.17.0.1

HTTP/1.1 201 Created
Content-Length: 0
Docker-Content-Digest: sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e
Docker-Distribution-Api-Version: registry/2.0
Location: /v2/hello-world/blobs/sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e
X-Content-Type-Options: nosniff
Date: Mon, 18 Nov 2019 16:30:33 GMT

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

step 6. push 镜像的manifest

当所有的layer都上传完成了,客户端需要发送一个PUT请求并提供镜像的manifest:

PUT /v2/<name>/manifests/<reference>
Content-Type: <manifest media type>
{
   "name": <name>,
   "tag": <tag>,
   "fsLayers": [
      {
         "blobSum": <digest>
      },
      ...
    ]
   ],
   "history": <v1 images>,
   "signature": <JWS>,
   ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

url中的namereference必须和下面的body中的字段对应。reference 可以是tag 或者digest

PUT /v2/hello-world/manifests/1.0 HTTP/1.1
Host: 
User-Agent: docker/17.12.0-ce go/go1.9.2 git-commit/c97c6d6 kernel/3.10.0-693.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/17.12.0-ce \(linux\))
Content-Length: 524
Accept-Encoding: gzip
Content-Type: application/.v2+json
X-Forwarded-For: 172.17.0.1, 192.168.79.136
X-Forwarded-Proto: https
X-Real-Ip: 172.17.0.1

{
   "schemaVersion": 2,
   "mediaType": "application/.v2+json",
   "config": {
      "mediaType": "application/.v1+json",
      "size": 1510,
      "digest": "sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e"
   },
   "layers": [
      {
         "mediaType": "application/",
         "size": 977,
         "digest": "sha256:1b930d010525941c1d56ec53b97bd057a67ae1865eebf042686d2a2d18271ced"
      }
   ]
}

HTTP/1.1 201 Created
Docker-Content-Digest: sha256:92c7f9c92844bbbb5d0a101b22f7c2a7949e40f8ea90c8b3bc396879d95e899a
Docker-Distribution-Api-Version: registry/2.0
Location: /v2/hello-world/manifests/sha256:92c7f9c92844bbbb5d0a101b22f7c2a7949e40f8ea90c8b3bc396879d95e899a
X-Content-Type-Options: nosniff
Date: Mon, 18 Nov 2019 16:30:33 GMT
Content-Length: 0

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

这个接口返回201后整个push流程就结束了~