Skip to content

使用 crane 检查、管理和传输容器镜像

本文中的 crane 特指 go-containerregistry 项目提供的命令行工具,用于直接访问远程容器镜像和 Registry。它可以在不启动 Docker Engine、也不把镜像导入本地 Docker 镜像存储的情况下,检查镜像摘要和架构、添加远程标签以及在 Registry 之间传输镜像。

crane 以 Apache 2.0 许可证发布,使用工具本身不需要购买软件许可。运行命令仍会消耗执行设备的网络流量和计算资源,Registry 服务也可能产生存储或流量费用。

信息

第三方可选工具

crane 不是平台内置工具,也不属于平台服务等级协议的保障范围。平台功能能够直接完成任务时,优先使用平台功能;需要远程检查、Dockerless 操作或自动化时,再安装 crane。crane 的版本、安装包和命令行为由上游项目维护。

先选择平台功能、Docker 还是 crane

先按镜像当前所在位置和下一步动作选择工具,可以避免为一次简单迁移引入不必要的命令行流程。

要完成的任务优先选择选择依据
在同一平台、同一租户的可用区之间进行一次镜像迁移镜像中心的 同步 功能平台能够识别源可用区和目标可用区,不需要自行维护两端登录状态
从公网 HTTP/HTTPS 地址导入镜像镜像中心的 URL 上传 功能平台直接抓取目标文件,操作步骤更少
构建镜像、运行容器或调试容器启动行为Docker 和平台镜像构建功能crane 不构建或运行容器
检查远程镜像的 tag、摘要、清单和 linux/amd64 架构crane不需要拉取完整镜像,也不需要 Docker Engine
平台无法访问 Docker Hub,但本地设备可以访问且未安装 Dockercrane本地设备可以直接把指定平台的远程镜像复制到目标可用区 Registry
目标 SSH 主机无法访问源 Registry 或任何可用的中转 Registry,但本地设备可以访问源端和该主机crane 与 SSH仅适合向单台主机临时导入镜像;目标主机能够访问 Registry 时,仍优先复制到 Registry
在外部 Registry 与平台 Registry 之间传输镜像crane可以直接读取源 Registry 并写入目标 Registry
重复迁移、批量复制或在 CI 中校验镜像crane命令适合脚本化,并可以通过摘要判断结果

平台的 Registry 按可用区相互独立。选择地址和凭证前,先阅读镜像仓库地址与可用区。需要了解平台内置导入和迁移方式时,参见导入或跨可用区迁移镜像

安装并确认 crane 版本

从官方 Release 安装时,crane 是一个独立二进制文件,运行时不需要 Go 工具链或 Docker Engine。生产环境和 CI 应固定经过验证的版本,避免每次运行时自动切换到未知的新版本。

在 macOS Apple Silicon 中安装

Homebrew 会选择与当前 Mac 架构匹配的软件包:

language-shell
brew install crane
crane version

在 Linux AMD64 中安装

以下命令以本文验证的 v0.21.7 为例,将二进制文件安装到当前用户目录。更新版本前,请先查看 crane Releases中的发布说明。

language-shell
VERSION=v0.21.7
OS=Linux
ARCH=x86_64

curl -fL \
  "https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_${OS}_${ARCH}.tar.gz" \
  -o /tmp/go-containerregistry.tar.gz

mkdir -p "$HOME/.local/bin"
tar -xzf /tmp/go-containerregistry.tar.gz -C "$HOME/.local/bin" crane
export PATH="$HOME/.local/bin:$PATH"
crane version

如果执行设备为 Linux ARM64,将 ARCH 改为 arm64。官方安装说明还提供 Release 产物的 SLSA provenance 验证方法,详见 crane 安装文档

在 Windows PowerShell 中安装

官方 Release 提供 Windows x86-64 和 ARM64 二进制文件。以下 PowerShell 命令以本文验证的 v0.21.7 为例,根据当前 PowerShell 进程架构选择安装包,并使用 Windows 自带的 tar.exe 解压 crane.exe

language-powershell
$Version = "v0.21.7"
$Architecture = switch ($env:PROCESSOR_ARCHITECTURE) {
  "AMD64" { "x86_64" }
  "ARM64" { "arm64" }
  default { throw "Unsupported Windows architecture: $env:PROCESSOR_ARCHITECTURE" }
}

$InstallDir = Join-Path $HOME ".local\bin"
$Archive = Join-Path $env:TEMP "go-containerregistry.tar.gz"
$DownloadUrl = "https://github.com/google/go-containerregistry/releases/download/$Version/go-containerregistry_Windows_$Architecture.tar.gz"

New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
Invoke-WebRequest -Uri $DownloadUrl -OutFile $Archive
tar.exe -xzf $Archive -C $InstallDir crane.exe

$env:Path = "$InstallDir;$env:Path"
crane version

PATH 设置只对当前 PowerShell 窗口生效。需要在后续新终端中直接运行 crane 时,将 $HOME\.local\bin 添加到当前用户的 PATH。Windows Subsystem for Linux(WSL)用户应按 Linux 步骤安装 Linux 二进制文件;Git Bash 用户可以把 Windows crane.exe 加入 PATH 后使用本文的 POSIX shell 命令。

除环境变量、引号和换行语法外,crane lsdigestmanifestcopy 等命令在 Windows 与 macOS/Linux 中的参数一致。本文未标注 PowerShell 的 shell 代码块使用 macOS/Linux 的 POSIX shell 语法;PowerShell 中设置和读取环境变量时使用 $env:NAME

按运行位置选择 Registry 地址和凭证

crane 使用与 Docker 兼容的认证配置。命令在平台外部运行,或目标为其他可用区时,必须使用镜像中心提供的目标可用区 Registry 公网访问地址和登录凭证;cr.infini-ai.com 只用于从平台实例访问该实例所在可用区的 Registry。

登录公网 Registry 时,先从镜像中心复制实际地址和用户名,再通过标准输入传入密码:

language-shell
export REGISTRY="<目标可用区 Registry 公网访问地址>"
export REGISTRY_USERNAME="<镜像中心提供的用户名>"

read -rsp 'Registry password: ' REGISTRY_PASSWORD
printf '%s' "$REGISTRY_PASSWORD" |
  crane auth login "$REGISTRY" \
    --username "$REGISTRY_USERNAME" \
    --password-stdin
unset REGISTRY_PASSWORD

使用 --password-stdin 可以避免密码出现在 shell 历史和进程参数中。crane 会将登录信息写入 Docker 兼容的配置文件,后续命令会自动查找匹配的 Registry 凭证。

在本地设备中使用公网地址

本地 macOS、Linux、Windows 或平台外部服务器无法依赖平台内部 Registry 域名。无论目标是哪个可用区,都要在镜像中心选择该可用区,然后使用页面提供的 Registry 公网访问地址和凭证。

如果源镜像和目标镜像位于不同 Registry,需要分别登录两端。源端账号至少需要拉取权限,目标端账号至少需要推送权限。

在 AICoder 中使用公网地址

AICoder 不提供 Docker Engine,但可以直接运行 crane 二进制文件。使用方式与本地设备相同:通过目标可用区的 Registry 公网访问地址和平台提供的凭证登录。

AICoder 适合执行摘要、清单、tag 检查和远程复制。由于系统盘空间有限,不建议在 AICoder 中使用会生成大型本地文件的镜像拉取或文件系统导出流程。

在 AIStudio 开发机中复用当前可用区凭证

以下凭证只适用于当前实例所在可用区。访问其他可用区时,仍需改用目标可用区的 Registry 公网地址和凭证。

直接复用平台预置凭证

AIStudio 开发机开启 Docker 容器 功能后,平台会把当前可用区 Registry 的凭证写入 /tmp/docker-config/config.json。设置 DOCKER_CONFIG 后,crane 可以复用该凭证:

language-shell
test -s /tmp/docker-config/config.json
export DOCKER_CONFIG=/tmp/docker-config

crane ls "cr.infini-ai.com/<租户 ID>/<Repository>"

如果已经执行过 docker logout cr.infini-ai.com,请重启开发机,等待平台重新写入凭证后再运行 crane。

在极值算力实例中复用当前可用区凭证

以下凭证只适用于当前实例所在可用区。如果目标 Registry 不在当前实例所在可用区,请使用目标可用区的公网访问地址和凭证。

直接复用平台预置凭证

极值算力实例开启 Docker 容器 功能后,也可以通过 DOCKER_CONFIG 复用当前可用区 Registry 凭证:

language-shell
test -s /tmp/docker-config/config.json
export DOCKER_CONFIG=/tmp/docker-config

crane ls "cr.infini-ai.com/<租户 ID>/<Repository>"

如果已经执行过 docker logout cr.infini-ai.com,请重启极值算力实例,等待平台重新写入凭证后再运行 crane。

将 Docker Hub 的 Linux AMD64 镜像迁移到平台

如果平台受网络限制无法访问 Docker Hub,但本地 macOS、Linux 或 Windows 设备能够访问 Docker Hub,可以让 crane 在本地设备上读取 Docker Hub 镜像,并写入平台目标可用区的 Registry。该流程不要求本地设备安装 Docker 客户端或 Docker Engine,也不要求把镜像导入本地 Docker 镜像存储。

本地设备是 ARM64 还是 AMD64 不会改变目标镜像架构。以下示例显式选择 Docker Hub 镜像中的 linux/amd64 平台,并将其复制到平台:

在 macOS 或 Linux 中迁移

language-shell
export SRC_IMAGE="docker.io/library/ubuntu:22.04"

# 在镜像中心选择目标可用区后,复制页面显示的实际公网地址。
# 只填写主机名,不包含 https://。
export DST_REGISTRY="<目标可用区 Registry 公网访问地址>"
export DST_IMAGE="$DST_REGISTRY/<租户 ID>/ubuntu:22.04"
export DST_USERNAME="<镜像中心提供的用户名>"

read -rsp 'Registry password: ' DST_PASSWORD
printf '%s' "$DST_PASSWORD" |
  crane auth login "$DST_REGISTRY" \
    --username "$DST_USERNAME" \
    --password-stdin
unset DST_PASSWORD

先确认源 tag 包含 linux/amd64,再复制该平台:

language-shell
crane digest --platform=linux/amd64 "$SRC_IMAGE"

crane copy \
  --platform=linux/amd64 \
  "$SRC_IMAGE" \
  "$DST_IMAGE"

复制完成后,比较源端和目标端的 linux/amd64 平台摘要,并快速验证目标镜像:

language-shell
SRC_DIGEST="$(crane digest --platform=linux/amd64 "$SRC_IMAGE")"
DST_DIGEST="$(crane digest --platform=linux/amd64 "$DST_IMAGE")"

printf 'source:      %s\ndestination: %s\n' "$SRC_DIGEST" "$DST_DIGEST"
test "$SRC_DIGEST" = "$DST_DIGEST"

crane validate --fast --remote "$DST_IMAGE"

Docker Hub 公有镜像通常可以匿名读取。源镜像为私有镜像,或需要避免匿名拉取限额时,使用 Docker Hub 用户名和访问令牌登录 index.docker.io

language-shell
export DOCKERHUB_USERNAME="<Docker Hub 用户名>"
read -rsp 'Docker Hub access token: ' DOCKERHUB_TOKEN
printf '%s' "$DOCKERHUB_TOKEN" |
  crane auth login index.docker.io \
    --username "$DOCKERHUB_USERNAME" \
    --password-stdin
unset DOCKERHUB_TOKEN

在 Windows PowerShell 中迁移

以下函数通过 PowerShell 安全字符串读取密码或访问令牌,再通过标准输入传给 crane。明文不会写入 PowerShell 命令历史或 crane 的进程参数:

language-powershell
function Invoke-CraneLogin {
  param(
    [Parameter(Mandatory)] [string] $Registry,
    [Parameter(Mandatory)] [string] $Username,
    [string] $Prompt = "Registry password"
  )

  $SecurePassword = Read-Host $Prompt -AsSecureString
  $PasswordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)

  try {
    $PlainPassword = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($PasswordPointer)
    $PlainPassword | crane auth login $Registry `
      --username $Username `
      --password-stdin

    if ($LASTEXITCODE -ne 0) {
      throw "crane login failed for $Registry"
    }
  }
  finally {
    $PlainPassword = $null
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($PasswordPointer)
    Remove-Variable SecurePassword -ErrorAction SilentlyContinue
  }
}

定义源镜像和目标镜像,登录目标可用区 Registry,然后复制 linux/amd64 平台:

language-powershell
$env:SRC_IMAGE = "docker.io/library/ubuntu:22.04"

# 在镜像中心选择目标可用区后,复制页面显示的实际公网地址。
# 只填写主机名,不包含 https://。
$env:DST_REGISTRY = "<目标可用区 Registry 公网访问地址>"
$env:DST_IMAGE = "$($env:DST_REGISTRY)/<租户 ID>/ubuntu:22.04"
$env:DST_USERNAME = "<镜像中心提供的用户名>"

Invoke-CraneLogin `
  -Registry $env:DST_REGISTRY `
  -Username $env:DST_USERNAME

crane digest --platform=linux/amd64 $env:SRC_IMAGE
if ($LASTEXITCODE -ne 0) {
  throw "The source image does not provide linux/amd64"
}

crane copy `
  --platform=linux/amd64 `
  $env:SRC_IMAGE `
  $env:DST_IMAGE
if ($LASTEXITCODE -ne 0) {
  throw "Image copy failed"
}

比较两端摘要并验证目标镜像:

language-powershell
$SourceDigest = crane digest --platform=linux/amd64 $env:SRC_IMAGE
if ($LASTEXITCODE -ne 0) {
  throw "Unable to read the source digest"
}

$DestinationDigest = crane digest --platform=linux/amd64 $env:DST_IMAGE
if ($LASTEXITCODE -ne 0) {
  throw "Unable to read the destination digest"
}

Write-Host "source:      $SourceDigest"
Write-Host "destination: $DestinationDigest"
if ($SourceDigest -ne $DestinationDigest) {
  throw "Source and destination digests do not match"
}

crane validate --fast --remote $env:DST_IMAGE
if ($LASTEXITCODE -ne 0) {
  throw "Destination image validation failed"
}

源镜像为 Docker Hub 私有镜像,或需要避免匿名拉取限额时,使用 Docker Hub 用户名和访问令牌再次调用登录函数:

language-powershell
$env:DOCKERHUB_USERNAME = "<Docker Hub 用户名>"
Invoke-CraneLogin `
  -Registry "index.docker.io" `
  -Username $env:DOCKERHUB_USERNAME `
  -Prompt "Docker Hub access token"

警告

镜像数据仍会经过本地设备

该流程不是 Docker Hub 与平台 Registry 之间的服务端直接复制。对于目标 Registry 中尚不存在、也无法通过 Registry 挂载复用的镜像数据,本地设备会从 Docker Hub 接收后再上传到平台。因此,本地设备必须同时能够访问 Docker Hub 和目标可用区 Registry 公网地址。

如果需要保留源镜像的全部平台,不要添加 --platform=linux/amd64。镜像复制成功后,仍需确认镜像满足目标平台服务的基础组件要求;用于 AIStudio 开发机前,请参见为导入的镜像安装开发机基础组件

检查远程镜像是否适合目标算力

平台的 Kubernetes 算力节点通常运行 Linux AMD64 镜像。镜像来自 Apple Silicon Mac 或多架构构建流程时,应在迁移或使用前确认远程镜像确实包含 linux/amd64。关于平台侧兼容性检查,参见使用现有容器镜像

先定义待检查的 Repository 和镜像:

language-shell
export REPOSITORY="<Registry>/<Namespace>/<Repository>"
export IMAGE="$REPOSITORY:<tag>"

列出 Repository 中可见的 tag:

language-shell
crane ls "$REPOSITORY"

查看镜像引用当前指向的摘要:

language-shell
crane digest "$IMAGE"

查看原始 manifest。多架构镜像会在镜像索引的 manifests 数组中列出各平台及其摘要:

language-shell
crane manifest "$IMAGE"

如果本地已安装 jq,可以只查看平台和摘要:

language-shell
crane manifest "$IMAGE" |
  jq '.manifests[]? | {digest, platform}'

确认镜像可以选择 linux/amd64,并查看该平台的配置和摘要:

language-shell
crane config --platform=linux/amd64 "$IMAGE"
crane digest --platform=linux/amd64 "$IMAGE"

如果镜像不包含 linux/amd64,平台选择会失败。运行 crane 的本地机器是 ARM64 还是 AMD64,不会改变 --platform=linux/amd64 指定的远程检查目标。

完整远程验证会读取并校验镜像层,可能产生接近镜像大小的下载流量:

language-shell
crane validate --remote "$IMAGE"

如果只需要快速检查清单和配置结构,可以跳过镜像层下载和摘要校验:

language-shell
crane validate --fast --remote "$IMAGE"

为远程镜像添加标签

crane tag 可以在同一个 Repository 中为现有远程镜像添加 tag,不需要下载镜像层。先记录原摘要,再添加目标 tag:

language-shell
crane digest "$IMAGE"
crane tag "$IMAGE" release-2026-07

第二个参数只填写新 tag,不填写完整镜像地址。该操作需要对 Repository 的推送权限。tag 可以被后续操作覆盖;需要固定生产版本时,应同时记录或直接使用镜像摘要。

如果源和目标不在同一个 Repository,使用 crane copy,不要使用 crane tag

在两个 Registry 之间传输镜像

同一平台、同一租户的可用区之间进行一次迁移时,优先使用镜像中心的 同步 功能。以下情况更适合使用 crane:

  • 源或目标是外部 Registry;
  • 需要按脚本重复迁移;
  • 需要批量迁移多个 tag;
  • 执行环境没有 Docker Engine;
  • 需要在迁移后自动比较摘要。

分别使用源、目标 Registry 的真实地址定义完整镜像引用:

language-shell
export SRC_IMAGE="<源 Registry>/<Namespace>/<Repository>:<tag>"
export DST_IMAGE="<目标 Registry>/<Namespace>/<Repository>:<tag>"

crane copy "$SRC_IMAGE" "$DST_IMAGE"

crane 默认处理镜像引用中的全部平台。上游命令参考将 crane copy 定义为在保留摘要值的情况下复制远程镜像。对于多架构镜像,不要添加 --platform=linux/amd64,除非目标 Repository 确实只需要单个平台。

迁移大镜像并在失败后重试

crane 按内容摘要检查和上传镜像配置及各个镜像层。复制失败后,不需要手动逐层执行命令:使用相同的源摘要和目标引用重新运行 crane copy,目标端已经完整提交的镜像层通常会被检查并跳过。

长时间迁移前,先把可能变化的源 tag 固定为摘要。以下示例只迁移 linux/amd64 镜像;如果需要保留完整多架构镜像,从 crane digest 命令中删除 --platform=linux/amd64,固定镜像索引摘要:

language-shell
export SRC_REPOSITORY="<源 Registry>/<Namespace>/<Repository>"
export SRC_TAG="<tag>"
export DST_IMAGE="<目标 Registry>/<Namespace>/<Repository>:<tag>"

SRC_DIGEST="$(
  crane digest \
    --platform=linux/amd64 \
    "$SRC_REPOSITORY:$SRC_TAG"
)"
export SRC_IMAGE="$SRC_REPOSITORY@$SRC_DIGEST"

crane copy "$SRC_IMAGE" "$DST_IMAGE"

如果命令中断,重新执行最后一条 crane copy 命令。成功后,确认目标摘要仍与迁移开始前固定的源摘要一致:

language-shell
DST_DIGEST="$(crane digest "$DST_IMAGE")"

printf 'source:      %s\ndestination: %s\n' "$SRC_DIGEST" "$DST_DIGEST"
test "$SRC_DIGEST" = "$DST_DIGEST"
crane validate --fast --remote "$DST_IMAGE"

对于预计运行数小时的命令,使用不会随本地设备休眠或终端关闭而结束的执行环境,例如 CI Runner 或持久服务器中的 tmux 会话。

警告

重试不是单个镜像层内的断点续传

crane 会重试临时网络错误,并在重新执行复制命令时复用目标端已经完整提交的镜像层。但是,正在上传且尚未提交的单个镜像层可能需要从头上传。重试间隔过长时,目标 Registry 的垃圾回收也可能删除尚未被镜像清单引用的数据。单个镜像层特别大且反复失败时,应检查网络和 Registry 限制;如果能够控制镜像构建流程,还应考虑减小该镜像层。

需要迁移整个 Repository 的 tag 时,可以限制并发量,并避免覆盖目标端已经存在的 tag:

language-shell
export SRC_REPOSITORY="<源 Registry>/<Namespace>/<Repository>"
export DST_REPOSITORY="<目标 Registry>/<Namespace>/<Repository>"

crane copy \
  --all-tags \
  --no-clobber \
  --jobs 4 \
  "$SRC_REPOSITORY" \
  "$DST_REPOSITORY"

--jobs 控制多个 tag 的并发复制数量,不会突破执行设备、源 Registry 或目标 Registry 的网络和服务限制。批量复制前,先用 crane ls 确认源端 tag 数量,避免意外迁移整个大型 Repository。

警告

crane copy 不保证服务端直接复制

crane copy 使用标准 Registry API,不调用 Harbor 的复制或同步任务 API。复制时,crane 会按摘要检查目标端是否已经存在数据,并在 Registry 支持时请求挂载复用;这些数据不需要再次传输。对于目标端缺失且无法挂载的数据,运行 crane 的设备仍需从源 Registry 接收并发送到目标 Registry。因此,该命令不是有保证的服务端直接复制,速度仍会受到源端下载带宽、目标端上传带宽、网络时延和 Registry 限流影响。

验证传输结果

不要只根据目标 tag 出现来判断迁移完成。分别读取两端摘要,并确保比较的是同一层级:镜像索引摘要与镜像索引摘要比较,linux/amd64 平台摘要与同平台摘要比较。

比较完整镜像引用的摘要:

language-shell
SRC_DIGEST="$(crane digest "$SRC_IMAGE")"
DST_DIGEST="$(crane digest "$DST_IMAGE")"

printf 'source:      %s\ndestination: %s\n' "$SRC_DIGEST" "$DST_DIGEST"
test "$SRC_DIGEST" = "$DST_DIGEST"

比较 Kubernetes 节点使用的 linux/amd64 平台摘要:

language-shell
SRC_AMD64_DIGEST="$(crane digest --platform=linux/amd64 "$SRC_IMAGE")"
DST_AMD64_DIGEST="$(crane digest --platform=linux/amd64 "$DST_IMAGE")"

test "$SRC_AMD64_DIGEST" = "$DST_AMD64_DIGEST"
crane validate --fast --remote "$DST_IMAGE"

比较两个平台可用区中的同名 tag 时,应分别使用两个可用区的 Registry 公网访问地址。不要在两个不同可用区的实例中都使用 cr.infini-ai.com 后,仅根据相同的地址字符串判断镜像一致。

在无可达 Registry 时通过 SSH 临时导入镜像

目标 SSH 主机无法访问源 Registry、也无法访问任何可用的中转 Registry,而本地设备能够访问源端并连接该主机时,可以让 crane 在本地读取镜像,再通过 SSH 将镜像导入远程容器运行时。这是面向单台主机的后备路径;只要目标主机能够访问平台或其他 Registry,仍应优先使用在两个 Registry 之间传输镜像中的流程。

执行前确认以下条件:

  • 本地设备已安装 crane,并具有源镜像的拉取权限;
  • 目标主机已经安装并启动 Docker Engine,且 SSH 用户能够执行 docker load
  • 目标主机具有保存镜像层所需的磁盘空间;
  • 镜像只需要在该主机中临时使用,不需要供其他节点拉取。

本文只演示向 Docker Engine 导入镜像。Podman、containerd 以及 Kubernetes 发行版使用的导入命令和镜像命名空间可能不同,应遵循对应运行时的文档,不要直接套用以下命令。

从 macOS 或 Linux 流式导入

先定义源镜像和 SSH 主机,并确认源镜像包含 linux/amd64

language-shell
export SRC_IMAGE="docker.io/library/ubuntu:22.04"
export SSH_HOST="<用户名>@<SSH 主机>"

crane digest --platform=linux/amd64 "$SRC_IMAGE"

启用管道错误检查后,让 crane 把 Docker 兼容的镜像 tar 包写入标准输出,并由远程 Docker Engine 直接加载:

language-shell
set -o pipefail

crane pull \
  --platform=linux/amd64 \
  "$SRC_IMAGE" \
  /dev/stdout |
  ssh "$SSH_HOST" 'docker load'

镜像数据仍会按“源 Registry → 本地设备 → SSH 主机”的路径传输,但本地设备不需要 Docker Engine,也不需要保存完整的镜像 tar 文件。该管道不支持断点续传;如果 SSH 连接中断,需要从头重新传输镜像。不要对大型镜像或不稳定连接使用该方式。

如果远程 Docker 命令需要 sudo,不要让密码提示读取镜像数据流。应先在独立的交互式 SSH 会话中完成授权,再把远程命令改为 sudo -n docker load,使缺少授权时立即失败。

导入后,确认远程镜像的目标平台:

language-shell
ssh "$SSH_HOST" \
  "docker image inspect --format '{{.Os}}/{{.Architecture}}' '$SRC_IMAGE'"

预期输出为 linux/amd64。仍应在目标主机上启动容器,验证镜像的实际运行行为。

通过可恢复的归档传输大型镜像

目标主机无法访问任何可用 Registry,并且镜像较大或连接不稳定时,先让 crane 缓存已下载完成的镜像层并生成本地归档,再使用支持断点续传的工具发送归档。以下示例适用于安装了 rsync 的 macOS 或 Linux:

language-shell
export SRC_IMAGE="docker.io/library/ubuntu:22.04"
export SSH_HOST="<用户名>@<SSH 主机>"
export CACHE_DIR="$HOME/.cache/crane/layers"
export ARCHIVE="$HOME/ubuntu-22.04-linux-amd64.tar"
export REMOTE_ARCHIVE="/tmp/ubuntu-22.04-linux-amd64.tar"

mkdir -p "$CACHE_DIR"
crane pull \
  --cache_path "$CACHE_DIR" \
  --platform=linux/amd64 \
  "$SRC_IMAGE" \
  "$ARCHIVE"

rsync \
  --partial \
  --progress \
  "$ARCHIVE" \
  "$SSH_HOST:$REMOTE_ARCHIVE"

ssh "$SSH_HOST" "docker load -i '$REMOTE_ARCHIVE'"

如果 crane pull 中断,重新执行同一命令可以复用缓存中已经完整下载的镜像层;当前未完成的单个镜像层仍可能从头下载。如果 rsync 中断,重新执行同一命令可以继续传输已有的归档部分。

该方式会同时占用镜像层缓存、本地归档、远程归档和远程容器运行时存储。执行前分别检查本地和远程磁盘空间;确认远程镜像能够运行后,再删除归档和不再需要的缓存。

从 Windows PowerShell 传输镜像归档

原生 PowerShell 中,先让 crane 生成镜像归档,再使用 Windows OpenSSH 提供的 scpssh 传输并导入,避免通过文本管道传输二进制镜像数据:

language-powershell
$env:SRC_IMAGE = "docker.io/library/ubuntu:22.04"
$env:SSH_HOST = "<用户名>@<SSH 主机>"
$Archive = Join-Path $env:TEMP "ubuntu-22.04-linux-amd64.tar"
$RemoteArchive = "/tmp/ubuntu-22.04-linux-amd64.tar"

crane pull --platform=linux/amd64 $env:SRC_IMAGE $Archive
if ($LASTEXITCODE -ne 0) {
  throw "crane pull failed"
}

scp $Archive "${env:SSH_HOST}:$RemoteArchive"
if ($LASTEXITCODE -ne 0) {
  throw "scp failed"
}

ssh $env:SSH_HOST "docker load -i '$RemoteArchive'"
if ($LASTEXITCODE -ne 0) {
  throw "docker load failed"
}

ssh $env:SSH_HOST "docker image inspect --format '{{.Os}}/{{.Architecture}}' '$env:SRC_IMAGE'"

确认输出为 linux/amd64 并完成运行验证后,删除本地和远程归档。该方式会在两端临时占用接近镜像归档大小的磁盘空间。scp 本身不支持断点续传;连接不稳定时,应改用支持断点续传的文件传输工具。WSL 用户可以使用上一节的 rsync 归档传输流程;Git Bash 用户只应在连接稳定且镜像较小时使用流式命令。

警告

镜像只存在于一台主机的运行时中

通过 SSH 导入不会把镜像上传到 Registry,也不会让其他 Kubernetes 节点获得该镜像。多节点工作负载可能被调度到没有该镜像的节点,imagePullPolicy: Always 也可能继续访问不可达的 Registry。需要持久使用、跨节点调度或重复部署时,应把镜像复制到目标环境能够访问的 Registry。

将检查和复制放进自动化

CI 或批处理脚本应使用固定版本的 crane、隔离的认证目录和环境变量中的凭证。以下模板登录两端、复制一个镜像并验证摘要:

language-shell
set -euo pipefail

: "${SRC_REGISTRY:?}"
: "${SRC_USERNAME:?}"
: "${SRC_PASSWORD:?}"
: "${SRC_IMAGE:?}"
: "${DST_REGISTRY:?}"
: "${DST_USERNAME:?}"
: "${DST_PASSWORD:?}"
: "${DST_IMAGE:?}"

export DOCKER_CONFIG="$(mktemp -d)"
trap 'rm -rf "$DOCKER_CONFIG"' EXIT

printf '%s' "$SRC_PASSWORD" |
  crane auth login "$SRC_REGISTRY" \
    --username "$SRC_USERNAME" \
    --password-stdin

printf '%s' "$DST_PASSWORD" |
  crane auth login "$DST_REGISTRY" \
    --username "$DST_USERNAME" \
    --password-stdin

crane copy --no-clobber "$SRC_IMAGE" "$DST_IMAGE"

SRC_DIGEST="$(crane digest "$SRC_IMAGE")"
DST_DIGEST="$(crane digest "$DST_IMAGE")"
test "$SRC_DIGEST" = "$DST_DIGEST"

不要把密码直接写入脚本、命令参数或 CI 日志。将 Registry 用户名和密码保存在 CI 的密钥变量中,并按源端只读、目标端可写的最小权限原则配置账号。

排查登录、架构和复制失败

先根据错误出现的阶段缩小范围,避免在认证失败时反复修改镜像本身。

返回 401 或 403

确认命令使用的是目标可用区的正确 Registry 地址,并检查当前账号是否具有所需权限。检查类命令通常需要拉取权限;tagcopy 的目标端需要推送权限。能够拉取镜像不表示能够推送、删除镜像或列出整个 Registry 的 Repository。

无法解析平台内部 Registry 域名

cr.infini-ai.com 不是公网访问入口。本地设备、AICoder以及访问其他可用区的实例应改用镜像中心提供的目标可用区 Registry 公网访问地址。

找不到 linux/amd64 平台

镜像清单中没有目标平台。返回构建流程,为镜像添加 linux/amd64,或构建同时包含所需平台的多架构镜像。不要通过修改 tag 解决架构缺失。

两端摘要不同

先确认两端比较的是同一个 tag、同一种摘要层级和同一个平台。若使用了 --no-clobber,目标 tag 可能在复制前已经存在;此时应检查目标 tag 当前指向的镜像,而不是直接覆盖。

大镜像复制反复中断

先按照迁移大镜像并在失败后重试固定源摘要,再重新执行相同的 crane copy 命令。添加全局 --verbose 选项可以查看详细请求和重试信息:

language-shell
crane --verbose copy "$SRC_IMAGE" "$DST_IMAGE"

如果同一个镜像层反复失败,请检查链路中的代理或网关超时、上传大小限制、Registry 限流、网络重置以及目标端存储空间。--all-tags--jobs 用于批量 tag 复制,不能让单个未完成的镜像层从中断位置继续上传。单个镜像层过大且无法稳定传输时,应在可行的情况下调整 Dockerfile,避免把大量数据集中到一个镜像层中。

AICoder 系统盘空间不足

lsdigestmanifestconfigtagcopy 不要求把完整镜像保存到本地文件。避免在 AICoder 中执行会生成大型 tar 包或导出文件系统的操作,并使用 df -h / 检查空间。

了解 crane 的使用边界

crane 适合操作远程镜像和标准 Registry API,但不能替代完整的容器工具链:

  • crane 不负责执行 Dockerfile、构建应用镜像或运行容器;
  • crane 不提供漏洞扫描或运行时兼容性测试;
  • 本文不使用 Registry 级 catalog 和镜像删除操作,平台凭证的拉取或推送权限不表示拥有这些权限;
  • crane copy 复制指定的镜像或镜像索引,不应假设签名、SBOM、证明材料和其他 OCI referrer 一定随镜像一起迁移;
  • 本文仅在目标主机无可达 Registry 时使用 crane pull 生成或传输镜像归档;其他本地归档流程应先评估磁盘空间和传输成本;
  • crane export 导出的是容器文件系统,不是可以由 docker load 加载的镜像归档;本文不介绍文件系统导出或镜像层修改操作,应先评估摘要变化和供应链影响,再查阅对应的上游命令。

迁移后仍应创建测试实例,确认平台能够拉取、启动并正常运行目标镜像。远程摘要一致只能证明 Registry 中的镜像内容一致,不能代替运行验证。

查看官方参考和平台文档