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中的name
和reference
必须和下面的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流程就结束了~