简体   繁体   English

调用 Docker 的 /v2/{name}/manifest/{tag} API 时出现问题

[英]Problems calling Docker's /v2/{name}/manifest/{tag} API

I am using the docker registry api to complete the push of the docker image.我正在使用docker registry api来完成docker镜像的推送。 I have completed the push of all the previous blobs, but I encountered a problem when pushing the manifest at the end.我已经完成了之前所有blob的推送,但是最后推送manifest的时候遇到了问题。 I hope to get help here.我希望在这里得到帮助。

I checked the container log through the docker logs command and found that the error message was: err.code="manifest invalid" err.detail="invalid format length" err.message="manifest invalid"我通过docker logs命令查看了容器日志,发现报错信息为:err.code="manifest invalid" err.detail="invalid format length" err.message="manifest invalid"

I haven't found a solution so far.到目前为止我还没有找到解决方案。 I will describe my development environment and pseudo code below.我将在下面描述我的开发环境和伪代码。

My docker registry image is of amd architecture, the machine is a cloud server of x86 architecture, and I access it locally through code I get the mirror manifest file through GET /v2/{name}/manifests/{tag}, After traversing all the layers according to the layers field in the list and uploading, this step is completed successfully我的docker注册镜像是amd架构的,机器是x86架构的云服务器,通过代码在本地访问我通过GET /v2/{name}/manifests/{tag}获取镜像清单文件,遍历所有根据列表中layers字段的layers并上传,此步骤成功完成

After all blobs are uploaded,上传所有 Blob 后,

I use PUT /v2/{name}/manifests/{tag} to upload the final image manifest file My request body comes from the manifest file that I got from the source warehouse at the beginning, and my request header Content-Type is also consistent with it, but it fails in the end.我使用 PUT /v2/{name}/manifests/{tag} 上传最终的图片清单文件 我的请求体来自我一开始从源仓库得到的清单文件,我的请求头 Content-Type 也是与它一致,但最终失败了。

How can I modify my code to make it work properly如何修改我的代码以使其正常工作

The issue is synchronized on github https://github.com/distribution/distribution/issues/3503问题在github上同步https://github.com/distribution/distribution/issues/3503

shell:壳:

#! /bin/bash

getManifestsUrl="http://localhost:5001/v2/registry/manifests/1"
putManifestsUrl="http://localhost:5002/v2/registry/manifests/1"

manifests=$(curl -s -X GET "$getManifestsUrl")

echo "$manifests"

result=$(curl -X PUT -H "Content-Type:application/vnd.docker.distribution.manifest.v2+json" -d '"$manifests"' "$putManifestsUrl") 
echo "$result"

java:爪哇:

     String name = "registry";
        String tag = "1";

        // get /v2/{name}/manifests/{tag} impl
        String url = String.format(fromHost + "/v2/%s/manifests/%s", name, tag);
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.put(HttpHeaders.CONTENT_TYPE,
                Arrays.asList("application/vnd.docker.distribution.manifest.v2+json"));
        HttpEntity<String> httpEntity = new HttpEntity<>(null, httpHeaders);
        ResponseEntity<String> resp = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        // print "[application/vnd.docker.distribution.manifest.v1+prettyjws]"
        System.out.println(resp.getHeaders().get(HttpHeaders.CONTENT_TYPE));
        String manifests = resp.getBody();

        // put /v2/{name}/manifests/{tag} impl
        httpHeaders.clear();
        httpHeaders.put(HttpHeaders.CONTENT_TYPE,
                Arrays.asList("application/vnd.docker.distribution.manifest.v2+json"));
        httpEntity = new HttpEntity<>(manifests, httpHeaders);
        resp = restTemplate.exchange(toHost + "/v2/registry/manifests/1",
                HttpMethod.PUT, httpEntity, String.class);
        System.out.println(resp);

The problem here has to do with content-type header.这里的问题与内容类型标头有关。 When you do a GET, you tell the registry which content-type values you are able to accept with an Accept header, and the registry responds with one of those as the Content-Type header in the response.当您执行 GET 时,您通过Accept标头告诉注册表您可以接受哪些内容类型值,并且注册表以其中之一作为响应中的Content-Type标头进行响应。 When you have no Accept header, the registry falls back to a compatibility mode with very old docker versions that are expecting a schema version 1 manifest, which is why you saw application/vnd.docker.distribution.manifest.v1+prettyjws .当您没有 Accept 标头时,注册表会退回到与非常旧的 docker 版本的兼容模式,这些版本需要模式版本 1 清单,这就是您看到application/vnd.docker.distribution.manifest.v1+prettyjws的原因。

When you push a manifest in the schema v1 format with a different content-type header, the registry is rejecting it since the manifest doesn't match content-type.当您推送具有不同内容类型标头的架构 v1 格式的清单时,注册表将拒绝它,因为清单与内容类型不匹配。

The fix should be the following in the GET: GET 中的修复应如下所示:

httpHeaders.put(HttpHeaders.ACCEPT,
                Arrays.asList("application/vnd.docker.distribution.manifest.v2+json",
                "application/vnd.docker.distribution.manifest.list.v2+json",
                "application/vnd.oci.image.manifest.v1+json",
                "application/vnd.oci.image.index.v1+json"));

And then in your PUT request, you should pass the same content-type that you've received:然后在您的 PUT 请求中,您应该传递您收到的相同内容类型:

httpHeaders.put(HttpHeaders.CONTENT_TYPE,
                Arrays.asList(resp.getHeaders().get(HttpHeaders.CONTENT_TYPE)));

This handles the three main image types you'll see on registries today: docker schema 2 manifest, the docker manifest list, and the same pair for OCI.这将处理您今天将在注册表中看到的三种主要图像类型:docker schema 2 manifest、docker manifest list 和 OCI 的同一对。

Note that if you are copying images across repositories (and not simply retagging an image or modifying a manifest in the same repository), you'll need to recursively copy any manifests within the manifest list or OCI index before pushing that parent manifest list/index.请注意,如果您要跨存储库复制图像(而不是简单地重新标记图像或修改同一存储库中的清单),则需要在推送该父清单列表/索引之前递归复制清单列表或 OCI 索引中的任何清单. And similarly copy any blobs before copying the manifest itself.同样,在复制清单本身之前复制任何 blob。

I've also got implementations of manifest get and put in Go which may help working through the logic in my regclient project .我还实现了 manifest get 和 put in Go,这可能有助于处理我的 regclient 项目中的逻辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM