You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编程获取Docker manifest?Node.js AWS SDK ECR推送镜像遇难题

获取Docker镜像Manifest用于AWS ECR PutImage接口

我之前也遇到过类似的困扰——实验性的docker manifest CLI确实不太靠谱,而且dockerode默认没有封装获取manifest的方法,但其实我们可以直接调用Docker的v2 Registry API来拿到需要的JSON格式manifest,这个API是稳定可用的,只是CLI层面把相关命令标成了实验性而已。下面是两种可行的方案:

方案一:通过Dockerode调用Docker V2 API获取Manifest

Docker后台daemon本身就实现了Registry V2接口,不管是本地镜像还是已登录的远程仓库镜像,都可以通过这个接口获取manifest。我们可以用dockerode的modem模块发送自定义请求来实现:

const Docker = require('dockerode');
const docker = new Docker();

async function getImageManifest(imageRef) {
  // 拆分镜像名和标签,比如 "my-image:latest" 拆成 ["my-image", "latest"]
  const [repoName, tag = 'latest'] = imageRef.split(':');

  try {
    // 发送GET请求到Docker V2 API的manifest端点,指定Accept头部获取V2格式的manifest
    const response = await docker.modem.get(`/v2/${repoName}/manifests/${tag}`, {
      headers: {
        'Accept': 'application/vnd.docker.distribution.manifest.v2+json'
      }
    });

    // 解析响应流为JSON
    const manifestBuffer = await new Promise((resolve, reject) => {
      const chunks = [];
      response.on('data', chunk => chunks.push(chunk));
      response.on('end', () => resolve(Buffer.concat(chunks)));
      response.on('error', reject);
    });

    return JSON.parse(manifestBuffer.toString());
  } catch (err) {
    console.error('获取Manifest失败:', err);
    throw err;
  }
}

// 使用示例
getImageManifest('my-local-image:dev')
  .then(manifest => {
    console.log('拿到的Manifest:', manifest);
    // 这里就可以把manifest传给AWS SDK的PutImage接口了
  })
  .catch(err => process.exit(1));

说明:

  • 这个方法适用于本地镜像和已登录的远程仓库镜像,Docker daemon会自动处理本地存储或远程请求。
  • Accept头部指定application/vnd.docker.distribution.manifest.v2+json,确保拿到的是ECR兼容的V2 Schema 2格式manifest。

方案二:从本地镜像信息构建Manifest(适合离线场景)

如果你的镜像完全在本地、无法通过API获取(比如离线环境),可以通过解析dockerode的镜像检查信息手动构建manifest:

const Docker = require('dockerode');
const docker = new Docker();

async function getLayerSize(layerDigest) {
  // 通过Docker API的blob HEAD请求获取layer大小
  const [alg, digest] = layerDigest.split(':');
  const response = await docker.modem.head(`/v2/_blobs/${alg}/${digest}`);
  return parseInt(response.headers['content-length'], 10);
}

async function buildLocalManifest(imageRef) {
  const image = await docker.getImage(imageRef).inspect();
  
  // 获取每个layer的大小
  const layers = await Promise.all(
    image.RootFS.Layers.map(async layer => {
      return {
        mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip',
        digest: layer,
        size: await getLayerSize(layer)
      };
    })
  );

  // 构建符合V2 Schema 2的manifest
  return {
    schemaVersion: 2,
    mediaType: 'application/vnd.docker.distribution.manifest.v2+json',
    config: {
      mediaType: 'application/vnd.docker.container.image.v1+json',
      size: image.Size,
      digest: image.Id
    },
    layers: layers
  };
}

// 使用示例
buildLocalManifest('my-offline-image:v1')
  .then(manifest => console.log('构建的Manifest:', manifest))
  .catch(err => console.error(err));

说明:

  • 这个方法需要Docker daemon运行,依赖blob HEAD请求获取layer大小。
  • 构建出的manifest完全符合ECR PutImage接口的要求,可以直接使用。

额外提示

当你用PutImage接口推送manifest到ECR时,要确保所有的layer blobs已经上传到ECR(可以用AWS SDK的PutImageLayer接口),否则ECR会返回错误。如果你的镜像已经通过docker push推过,或者手动上传了所有layer,只需要提交manifest即可。

内容的提问来源于stack exchange,提问作者Gerben Rampaart

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:31:22