3. 容器镜像获取与仓库管理

在容器交付链路中,镜像仓库是镜像的“中转站”和“分发中心”。RHEL 默认使用 Podman 管理容器,但 Podman 的强项是运行容器,而不是精细地操作远程镜像。要检查、复制、删除远程镜像,以及处理多仓库认证,红帽提供的 Skopeo 是更合适的工具。它不依赖本地容器存储,可以直接与远程 registry 的 Registry HTTP API 交互,因此非常适合做镜像搬运、仓库巡检和离线交付。

Skopeo:面向远程镜像的搬运工具

Skopeo 的核心价值在于“不落地即可操作”。它支持 docker://、dir:、oci:、containers-storage: 等多种传输格式。日常最常用的四个子命令是:

  • skopeo inspect:读取远程镜像的 manifest、标签、架构、层摘要和创建时间,不下载镜像层。
  • skopeo copy:在 registry、本地目录、OCI 布局之间复制镜像,可跨仓库、跨认证域。
  • skopeo delete:删除远程仓库中的指定标签或摘要,需要仓库开启删除权限。
  • skopeo list-tags:列出某个仓库中所有可用标签,便于确认版本。

例如,检查红帽 UBI 镜像的元数据:

skopeo inspect docker://registry.access.redhat.com/ubi9/ubi:latest
skopeo list-tags docker://registry.access.redhat.com/ubi9/ubi

输出中会包含 Digest、Architecture、Labels 等字段。其中 Digest 是镜像内容的唯一指纹,比标签更可靠。生产环境建议用摘要固定版本,例如 ubi9/ubi@sha256:...,避免 latest 漂移导致不同节点拉取到不同内容。

registries.conf:定义仓库的信任与搜索顺序

RHEL 中容器仓库配置位于 /etc/containers/registries.conf,采用 TOML 格式。它决定三件事:未写全域名时去哪些仓库搜索、哪些仓库允许不安全连接、以及各仓库的镜像重写规则。典型配置如下:

unqualified-search-registries = ["registry.access.redhat.com", "quay.io", "registry.internal.example.com"]

[[registry]]
prefix = "registry.internal.example.com"
location = "registry.internal.example.com:5000"
insecure = true

[[registry.mirror]]
location = "mirror.internal.example.com"
insecure = false

这里 unqualified-search-registries 定义了短名称的搜索顺序;insecure = true 表示允许通过 HTTP 或跳过 TLS 校验访问内部仓库,仅建议在隔离内网使用;registry.mirror 则让 Podman/Skopeo 优先从镜像站拉取,失败后再回源,适合大规模节点加速。修改后无需重启服务,新发起的拉取即生效。

auth.json:多仓库认证的集中管理

当仓库需要登录时,凭据保存在 ${XDG_RUNTIME_DIR}/containers/auth.json(普通用户)或 /run/containers/0/auth.json(root)。推荐用 podman login 或 skopeo login 写入,而不是手工编辑:

podman login quay.io -u myuser --password-stdin
skopeo login registry.internal.example.com:5000 -u admin --password-stdin

auth.json 的结构是 auths 下按仓库域名分组,每组保存 base64 编码的 用户名:密码。同一文件可同时保存 quay.io、红帽 registry 和内部仓库的凭据,Skopeo 在复制时会自动按目标域名选取对应认证。需要注意:该文件是明文凭据,权限应保持 600,且不要提交到 Git。

Skopeo 镜像搬运与仓库认证链路 源仓库 quay.io registry.access.redhat.com ubi9/ubi:latest Skopeo inspect / copy / delete list-tags / login 不依赖本地容器存储 直接调用 Registry HTTP API 内部私有仓库 registry.internal .example.com:5000 ubi9/ubi:internal 拉取/检查 推送/复制 registries.conf unqualified-search-registries insecure / mirror 重写 /etc/containers/registries.conf auth.json auths: quay.io / 内部仓库 base64(user:password) 权限 600,勿入版本库 关键判断:inspect 看 Digest 与 Architecture → copy 跨仓库搬运 → 目标仓库 list-tags 验证标签是否出现 认证失败优先检查 auth.json 域名是否与 registry 地址完全一致(含端口)

实践:从 quay.io 拉取 UBI 并推送到内部 registry

下面以“把 quay.io 上的 UBI 镜像搬运到内部仓库”为例,串联本节知识点。假设内部仓库地址为 registry.internal.example.com:5000,已开启删除权限,且已通过 skopeo login 完成认证。

第一步,确认源镜像信息,记录摘要:

skopeo inspect docker://quay.io/ubi9/ubi:latest | grep -E 'Digest|Architecture'
# 示例输出(合理假设):
# "Digest": "sha256:9f3a...c1d2",
# "Architecture": "amd64"

第二步,直接跨仓库复制,不经过本地存储:

skopeo copy \
  --src-creds myuser:myquaytoken \
  --dest-creds admin:internalpass \
  docker://quay.io/ubi9/ubi:latest \
  docker://registry.internal.example.com:5000/ubi9/ubi:internal

如果内部仓库使用 HTTP,需要在 registries.conf 中把该仓库标记为 insecure = true,否则 Skopeo 会因 TLS 校验失败而报 x509: certificate signed by unknown authority。复制完成后,用 skopeo list-tags docker://registry.internal.example.com:5000/ubi9/ubi 确认 internal 标签已出现,再用 skopeo inspect 比对源与目标的 Digest 是否一致——一致即说明内容完整搬运。

若只想搬运单个架构,可加 --override-arch amd64;若要保留多架构 manifest list,则不要加该参数,Skopeo 会整体复制索引。

删除远程镜像与常见排障

删除远程标签使用 skopeo delete docker://registry.internal.example.com:5000/ubi9/ubi:internal。注意:多数 registry 默认关闭删除接口,需要服务端启用 delete.enabled=true,否则会返回 405。删除标签不会立即回收存储,还需在仓库侧执行垃圾回收。

常见问题与判断依据:

  • unauthorized:auth.json 中域名与目标地址不一致,或凭据过期。用 skopeo login 重新写入。
  • manifest unknown:标签不存在或拼写错误,先用 list-tags 核对。
  • connection refused:内部仓库端口未监听,或 registries.conf 中 location 端口写错。
  • digest mismatch:复制中断或镜像被覆盖,重新执行 copy 并再次比对摘要。

把 Skopeo、registries.conf 与 auth.json 三者配合使用,就能在不启动容器、不占用本地存储的前提下完成镜像的检查、搬运、删除与认证管理,为后续在 RHEL 上部署容器应用准备好可靠的镜像来源。