使用 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,但本地设备可以访问且未安装 Docker | crane | 本地设备可以直接把指定平台的远程镜像复制到目标可用区 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 架构匹配的软件包:
brew install crane
crane version在 Linux AMD64 中安装
以下命令以本文验证的 v0.21.7 为例,将二进制文件安装到当前用户目录。更新版本前,请先查看 crane Releases中的发布说明。
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:
$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 ls、digest、manifest、copy 等命令在 Windows 与 macOS/Linux 中的参数一致。本文未标注 PowerShell 的 shell 代码块使用 macOS/Linux 的 POSIX shell 语法;PowerShell 中设置和读取环境变量时使用 $env:NAME。
按运行位置选择 Registry 地址和凭证
crane 使用与 Docker 兼容的认证配置。命令在平台外部运行,或目标为其他可用区时,必须使用镜像中心提供的目标可用区 Registry 公网访问地址和登录凭证;cr.infini-ai.com 只用于从平台实例访问该实例所在可用区的 Registry。
登录公网 Registry 时,先从镜像中心复制实际地址和用户名,再通过标准输入传入密码:
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 可以复用该凭证:
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 凭证:
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 中迁移
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,再复制该平台:
crane digest --platform=linux/amd64 "$SRC_IMAGE"
crane copy \
--platform=linux/amd64 \
"$SRC_IMAGE" \
"$DST_IMAGE"复制完成后,比较源端和目标端的 linux/amd64 平台摘要,并快速验证目标镜像:
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:
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 的进程参数:
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 平台:
$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"
}比较两端摘要并验证目标镜像:
$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 用户名和访问令牌再次调用登录函数:
$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 和镜像:
export REPOSITORY="<Registry>/<Namespace>/<Repository>"
export IMAGE="$REPOSITORY:<tag>"列出 Repository 中可见的 tag:
crane ls "$REPOSITORY"查看镜像引用当前指向的摘要:
crane digest "$IMAGE"查看原始 manifest。多架构镜像会在镜像索引的 manifests 数组中列出各平台及其摘要:
crane manifest "$IMAGE"如果本地已安装 jq,可以只查看平台和摘要:
crane manifest "$IMAGE" |
jq '.manifests[]? | {digest, platform}'确认镜像可以选择 linux/amd64,并查看该平台的配置和摘要:
crane config --platform=linux/amd64 "$IMAGE"
crane digest --platform=linux/amd64 "$IMAGE"如果镜像不包含 linux/amd64,平台选择会失败。运行 crane 的本地机器是 ARM64 还是 AMD64,不会改变 --platform=linux/amd64 指定的远程检查目标。
完整远程验证会读取并校验镜像层,可能产生接近镜像大小的下载流量:
crane validate --remote "$IMAGE"如果只需要快速检查清单和配置结构,可以跳过镜像层下载和摘要校验:
crane validate --fast --remote "$IMAGE"为远程镜像添加标签
crane tag 可以在同一个 Repository 中为现有远程镜像添加 tag,不需要下载镜像层。先记录原摘要,再添加目标 tag:
crane digest "$IMAGE"
crane tag "$IMAGE" release-2026-07第二个参数只填写新 tag,不填写完整镜像地址。该操作需要对 Repository 的推送权限。tag 可以被后续操作覆盖;需要固定生产版本时,应同时记录或直接使用镜像摘要。
如果源和目标不在同一个 Repository,使用 crane copy,不要使用 crane tag。
在两个 Registry 之间传输镜像
同一平台、同一租户的可用区之间进行一次迁移时,优先使用镜像中心的 同步 功能。以下情况更适合使用 crane:
- 源或目标是外部 Registry;
- 需要按脚本重复迁移;
- 需要批量迁移多个 tag;
- 执行环境没有 Docker Engine;
- 需要在迁移后自动比较摘要。
分别使用源、目标 Registry 的真实地址定义完整镜像引用:
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,固定镜像索引摘要:
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 命令。成功后,确认目标摘要仍与迁移开始前固定的源摘要一致:
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:
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 平台摘要与同平台摘要比较。
比较完整镜像引用的摘要:
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 平台摘要:
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:
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 直接加载:
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,使缺少授权时立即失败。
导入后,确认远程镜像的目标平台:
ssh "$SSH_HOST" \
"docker image inspect --format '{{.Os}}/{{.Architecture}}' '$SRC_IMAGE'"预期输出为 linux/amd64。仍应在目标主机上启动容器,验证镜像的实际运行行为。
通过可恢复的归档传输大型镜像
目标主机无法访问任何可用 Registry,并且镜像较大或连接不稳定时,先让 crane 缓存已下载完成的镜像层并生成本地归档,再使用支持断点续传的工具发送归档。以下示例适用于安装了 rsync 的 macOS 或 Linux:
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 提供的 scp 和 ssh 传输并导入,避免通过文本管道传输二进制镜像数据:
$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、隔离的认证目录和环境变量中的凭证。以下模板登录两端、复制一个镜像并验证摘要:
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 地址,并检查当前账号是否具有所需权限。检查类命令通常需要拉取权限;tag 和 copy 的目标端需要推送权限。能够拉取镜像不表示能够推送、删除镜像或列出整个 Registry 的 Repository。
无法解析平台内部 Registry 域名
cr.infini-ai.com 不是公网访问入口。本地设备、AICoder以及访问其他可用区的实例应改用镜像中心提供的目标可用区 Registry 公网访问地址。
找不到 linux/amd64 平台
镜像清单中没有目标平台。返回构建流程,为镜像添加 linux/amd64,或构建同时包含所需平台的多架构镜像。不要通过修改 tag 解决架构缺失。
两端摘要不同
先确认两端比较的是同一个 tag、同一种摘要层级和同一个平台。若使用了 --no-clobber,目标 tag 可能在复制前已经存在;此时应检查目标 tag 当前指向的镜像,而不是直接覆盖。
大镜像复制反复中断
先按照迁移大镜像并在失败后重试固定源摘要,再重新执行相同的 crane copy 命令。添加全局 --verbose 选项可以查看详细请求和重试信息:
crane --verbose copy "$SRC_IMAGE" "$DST_IMAGE"如果同一个镜像层反复失败,请检查链路中的代理或网关超时、上传大小限制、Registry 限流、网络重置以及目标端存储空间。--all-tags 和 --jobs 用于批量 tag 复制,不能让单个未完成的镜像层从中断位置继续上传。单个镜像层过大且无法稳定传输时,应在可行的情况下调整 Dockerfile,避免把大量数据集中到一个镜像层中。
AICoder 系统盘空间不足
ls、digest、manifest、config、tag 和 copy 不要求把完整镜像保存到本地文件。避免在 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 中的镜像内容一致,不能代替运行验证。