在RHEL系统中,Skopeo作为一款无守护进程的容器镜像管理工具,可以直接在镜像仓库之间完成镜像复制、检查与删除等操作,无需安装Docker或Podman即可运行。它使用OCI标准与容器镜像仓库API通信,特别适合自动化脚本、CI/CD流水线以及服务器上无法运行容器引擎的场景。以下从实用角度拆解Skopeo的搬运能力。

一、为什么需要 Skopeo 而不是 docker pull/push
传统的 Docker 镜像搬运流程通常分为三步:docker pull 拉取镜像到本地、docker tag 修改标签、docker push 推送到目标仓库。这一过程要求 Docker 守护进程处于运行状态,并且镜像在本地磁盘中存在完整解包副本。对于单个大镜像,这会产生数GB的临时文件,同时 pull/push 还会改变镜像的部分元数据,尤其在处理多架构清单时容易丢失架构信息。
Skopeo 则直接借助 registry HTTP API 进行层与清单的复制。它不需要本地存储镜像的每一层,也不启动容器运行时,因此可以在没有 root 权限、没有 Docker daemon 的环境中运行。例如在 RHEL 最小化安装的服务器上,仅安装 skopeo 包即可实现从 quay.io 到内部 Harbor 的镜像搬运。skopeo copy 命令会读取源仓库的 manifest,并按需复制各层 blob,最终在目标仓库生成新的 digest,保持镜像内容的完整性和可追溯性。
此外,Skopeo 支持直接复制 OCI 与 Docker v2 两种格式的镜像,并且可以同步签名信息。对于一个包含 amd64 与 arm64 两种架构的多架构清单,skopeo copy --all 参数能够完整保留 manifest list,而不是像 docker pull/push 那样默认只处理当前平台架构。这个细节在生产环境多架构迁移中非常关键。
二、安装与环境准备
在 RHEL 8 或 9 中,skopeo 位于标准仓库,直接使用 dnf 即可安装:sudo dnf install -y skopeo。安装完成后可以通过 skopeo --version 查看版本。Skopeo 本身不依赖 Podman 或 Docker,但如果需要对本地容器存储进行管理,则需要配合 containers/storage 库。对于纯仓库间复制,仅凭 skopeo 二进制即可工作。
默认情况下,Skopeo 会读取 /etc/containers/policy.json 作为签名策略文件,读取 /etc/containers/registries.conf 作为仓库访问配置。在 RHEL 中,这些文件通常随 containers-common 包提供。若目标仓库使用自签名证书,需要将CA证书放置到 /etc/pki/ca-trust/source/anchors/ 并执行 update-ca-trust,否则 skopeo copy 会因 TLS 校验失败而报错。
也可以通过命令行参数临时指定认证信息:--src-creds=username:password 与 --dest-creds=username:password。但生产环境建议将凭证放入文件,例如 --src-authfile=/root/.docker/config.json,或使用环境变量控制,避免密码出现在 shell 历史记录中。
三、核心命令:copy、inspect 与 sync 实战
1. 复制镜像。最基本的用法是将一个镜像从源仓库复制到目标仓库:
skopeo copy \ docker://registry.access.redhat.com/ubi9/ubi:latest \ docker://harbor.ippipp.com/library/ubi9:latest \ --dest-creds=admin:Harbor12345 \ --dest-tls-verify=false
该命令中,docker:// 前缀表示使用 Docker Registry V2 API。如果源仓库需要认证,则添加 --src-creds;若需要复制所有架构镜像,则加上 --all。当目标仓库使用自签名证书时,可以设置 --dest-tls-verify=false,但仅建议在内网可信环境中使用。
2. 查看镜像信息。skopeo inspect 可以直接从远程仓库获取镜像的元数据,而不需要拉取镜像:
skopeo inspect docker://registry.access.redhat.com/ubi9/ubi:latest
输出包含 digest、架构、层信息、标签以及 labels 等字段。与 docker inspect 不同,Skopeo 的 inspect 针对的是仓库中的镜像而不是本地容器,因此可以快速判断远程仓库中当前版本是否更新。若只关注 digest,可以使用 skopeo inspect --format '{{.Digest}}' 来提取。
3. 批量同步。当需要将一个命名空间或整个仓库中的多个镜像同步到另一个仓库时,使用 skopeo sync 比循环执行 copy 更加可靠。例如:
skopeo sync \ --src docker \ --dest docker \ --src-creds=user:pass \ --dest-creds=admin:pass \ registry.ippipp.com/source-ns \ harbor.ippipp.com/dest-ns
上述命令会遍历源命名空间下的所有仓库,并在目标创建同名仓库。若不希望全量同步,还可以结合 --scoped 参数保持完整路径,或使用 --dry-run 先打印将要同步的内容。sync 在离线镜像站建设中非常实用,可以定期通过 systemd timer 触发。
四、认证配置与 TLS 疑难排查
Skopeo 的认证优先级依次为命令行参数、authfile,再到默认的认证文件。RHEL 中 podman login 写入的认证通常保存在 /run/user/1000/containers/auth.json 或 /root/.config/containers/auth.json。Skopeo 不会自动读取 Docker daemon 的配置,需要显式指定:--authfile=/root/.config/containers/auth.json。如果复制时遇到 authentication required,首先确认 authfile 路径是否正确,其次检查用户名密码是否能直接登录目标仓库。
TLS 方面,常见错误是 x509: certificate signed by unknown authority。这说明目标仓库使用了自签名证书或企业内部CA,但系统未信任。正确做法是把 CA 证书添加到系统信任库,而不是长期使用 --tls-verify=false。对于 Harbor、Quay 等私有仓库,可以在其设置中下载 CA 或服务器证书,然后执行:
cp harbor-ca.crt /etc/pki/ca-trust/source/anchors/ update-ca-trust
之后无需 --tls-verify=false 即可安全复制。如果必须关闭验证,建议将其限制在命令行选项内,不要写入配置文件,以免影响其他 Podman、Buildah 等容器的安全校验。
五、签名策略与自动化集成
RHEL 的容器签名机制通过 /etc/containers/policy.json 来控制哪些镜像可以被拉取或运行。Skopeo 在执行 copy 时会读取该策略,若策略要求签名而源仓库未提供签名,copy 会失败。例如 RHEL 默认策略对红帽官方镜像要求签名验证。对于内部仓库,可以在策略文件中增加 insecureAcceptAnything 或指向内部 GPG 签名服务器,但这样会降低安全性,需要根据企业合规要求调整。
在日常运维中,Skopeo 很适合嵌入 systemd 服务或 cron 任务。例如每天凌晨从上游仓库同步到本地 Harbor 作为缓存,可以编写 /usr/local/bin/mirror-images.sh 脚本,并在 systemd timer 中调用。脚本内部使用 skopeo sync --all --delete 保持目标仓库与源一致。注意 --delete 会删除目标仓库中源仓库已不存在的标签,需谨慎测试。
对于 CI/CD 场景,Skopeo 可以作为免 Docker 的镜像复制工具直接运行在 GitLab Runner、Jenkins agent 上。例如发布流水线中的制品上传步骤可以使用:
skopeo copy \ --src-authfile "$CI_REGISTRY_AUTH_FILE" \ --dest-authfile "$DEST_AUTH_FILE" \ docker://registry.gitlab.com/project/app:$CI_COMMIT_TAG \ docker://harbor.ippipp.com/prod/app:$CI_COMMIT_TAG
这里通过环境变量传递 Authfile 路径,避免泄露明文密码。由于 Skopeo 不依赖守护进程,部署和资源占用都极为轻量,这使得它在容器平台之外成为镜像流转的可靠齿轮。