☰
容器镜像离线共享实战:基于 devops-exercises 的 podman save、rsync 与 podman load 全流程指南
2026/10/1 9:48:41 网站建设 项目流程
  • 文档
  • 教程
  • DevOps
  • 运维

【免费下载链接】devops-exercises

Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions

项目地址:https://gitcode.com/GitHub_Trending/de/devops-exercises
点击查看免费下载

在无法访问镜像仓库(Registry)的内网、隔离网络或需要快速把镜像交付给同事的场景中,把镜像打包成归档文件再拷贝到目标主机,是最实用也最常用的做法。本文以 devops-exercises 仓库中 "Sharing Images (without a registry)" 练习为骨架,完整还原从podman save打包、rsync传输到podman load导入并验证的五个步骤,并深入解释"归档为什么比镜像小"这一核心问题,同时结合仓库内 Containers 主题 README 中的镜像层、压缩层哈希(distribution hash)、Registry 对比等问答内容,帮你掌握一套不依赖 Registry 的镜像分发实战方案。

1. 练习背景:什么时候需要"无 Registry 共享镜像"

容器镜像的常规分发路径是"推送到 Registry,再从 Registry 拉取"(podman push/podman pull)。但实际运维中常有以下情况无法走这条路径:

  • 目标主机处于内网、气隙(air-gapped)网络,无法访问外网 Registry;
  • 公司安全策略禁止镜像外流到公共 Registry,而自建私有 Registry 尚未就绪;
  • 只需要临时把某个镜像交给同事调试,不值得为此搭建一套 Registry 基础设施。

此时"打包归档 + 文件传输 + 本地导入"就成了最直接的替代方案。本练习正是围绕这个场景设计的,它在 Containers 主题 README 的 "Images" 练习表中被明确标注为Sharing Images (without a registry)。

1.1 前置条件

练习要求先确认本地已存在至少一个镜像:

podman image ls

如果本地没有镜像,直接拉取一个用于实验:

podman pull httpd

仓库内 Working with Images 练习 的参考答案还演示了镜像生命周期的基础操作,例如用podman image ls列出镜像、podman image pull ubuntu:latest拉取镜像、podman rm <container_id> && podman image rm ubuntu先删容器再删镜像。这些操作可作为理解"镜像从哪来、如何管理"的前置知识。

2. 练习目标拆解

原练习文件 sharing_images.md 定义了 5 个递进目标,覆盖"打包 → 对比 → 传输 → 导入 → 验证"的完整闭环:

  1. 选一个镜像并创建归档:使用podman save把镜像导出为 tar 归档文件;
  2. 对比归档大小与镜像大小:观察两者是否有差异,并解释原因——这是本练习的"思考题",也是理解镜像存储模型的关键;
  3. 把归档拷贝到远程主机:使用rsync(或scp等)完成传输;
  4. 在远程主机加载镜像:使用podman load把归档导入本机镜像存储;
  5. 验证镜像已加载并存在:用podman image ls确认远程主机上镜像可用。

3. 完整解决方案(可直接复制运行)

仓库内的 参考答案 给出了干净利落的五条命令,下面逐条展开并补充说明:

# ① 把 httpd 镜像保存为归档文件 podman save -o httpd.tar httpd # ② 对比归档与镜像的大小 du -sh httpd.tar # 输出:143MB podman image ls | grep httpd # 输出:149MB # 归档明显小于镜像本身(约 6MB 差异) # ③ 把归档传输到远程主机(将 USER@REMOTE_HOST_FQDN 替换为实际主机) rsync -azc httpd.tar USER@REMOTE_HOST_FQDN:/tmp/ # ④ 在远程主机上加载镜像 podman load -i /tmp/httpd.tar # ⑤ 验证镜像已存在于远程主机 podman image ls

3.1 第 1 步:podman save打包镜像

podman save -o httpd.tar httpd
  • -o, --output指定归档输出文件路径;
  • 参数中的httpd是镜像名(可加 tag,如httpd:latest);
  • Podman 也提供等价别名podman image save;
  • 生成的httpd.tar内含该镜像的完整元数据(manifest)与各层内容,是自包含的,可以被任何装有 Podman/Docker 的主机导入。

3.2 第 2 步:对比归档大小与镜像大小

du -sh httpd.tar # 磁盘占用:143MB podman image ls | grep httpd # 镜像"虚拟大小":149MB

参考答案明确记录了两者存在约 6MB 的差异,并提示"归档显然比镜像本身小"。这一差异的成因会在第 4 节详细剖析。

3.3 第 3 步:rsync传输归档到远程主机

rsync -azc httpd.tar USER@REMOTE_HOST_FQDN:/tmp/

rsync三个常用选项的含义:

选项作用
-a(archive)归档模式,递归拷贝并保留文件属性(权限、时间戳等),对二进制归档文件非常合适
-z(compress)传输时压缩数据,降低带宽占用;tar 归档中已压缩的层会被跳过重复压缩
-c(checksum)基于文件校验和而非文件大小/时间戳判断是否需要同步,保证两边文件一致

也可以换用scp httpd.tar USER@REMOTE_HOST_FQDN:/tmp/完成同样目的;在气隙网络中,甚至可以用 U 盘等离线介质搬运归档。

3.4 第 4 步:podman load导入镜像

podman load -i /tmp/httpd.tar
  • -i, --input指定要导入的归档文件;
  • 等价别名是podman image load;
  • 导入时 Podman 会把归档中的各层解压并写入本地镜像存储,重新构建镜像的 tag 与 ID 索引。

3.5 第 5 步:验证镜像已加载

podman image ls

在输出中应能看到docker.io/library/httpd及其 tag,说明镜像已在远程主机上就绪,可以继续用podman run启动容器。更精细的验证可以用podman inspect httpd查看镜像的完整配置与层信息。

4. 深入原理:为什么归档比镜像小?

这是本练习最有价值的"思考题"。结合仓库 Containers 主题 README 中关于镜像层(image layers)与 distribution hash 的问答,可以给出三层解释:

4.1 镜像大小是"未压缩虚拟大小",归档里是"压缩层"

README 在解答"什么是 distribution hash"时明确指出:

Layers are compressed when pushed or pulled;distribution hash 是压缩后层的哈希,用于拉取/推送时校验,防止镜像或层被篡改,并避免 ID 冲突。

这意味着镜像分发格式天然就是"层以压缩形式存储"。podman image ls展示的 149MB 是镜像各层内容未压缩时的累加(通常被称为"虚拟大小");而podman save生成的 tar 归档内保存的是符合分发格式的压缩层,因此整体体积更小。这一点与"push/pull 时层被压缩"是同一套存储模型,只是save把同样的压缩层写进了本地文件。

4.2du -sh与image ls度量口径不同

  • du -sh httpd.tar统计的是归档文件在磁盘上实际占用的块大小;
  • podman image ls的 SIZE 列反映的是镜像逻辑上的层内容总和,并不等于单文件在磁盘上的字节数。

两种度量口径不同,也会造成观感上的差异,但主导因素仍是上述"未压缩虚拟大小 vs 压缩层"的区别。

4.3 归档不包含"冗余的运行时数据"

镜像层是只读且内容寻址的(每层 ID 是基于内容的哈希),README 指出"改变任意层的内容都会导致哈希变化"。save只导出该镜像的 manifest 与层 blob,不含任何运行中的容器可写层或日志等运行时数据,这也是归档更"干净"的原因之一。相比之下,用podman commit从运行中的容器生成的新镜像会带上额外的日志与进程开销,通常更大(README 中podman commit相关问答有明确说明)。

5.podman save/podman load的更多参数与边界行为

5.1 保存与导入的格式选项

podman save支持通过--format指定归档格式,常见取值包括:

格式说明
docker-archiveDocker 兼容的 tar 归档(默认,Podman 与 Docker 可互导)
oci-archiveOCI 镜像规范归档
oci-dir/docker-dir输出为目录结构而非单个 tar

默认docker-archive保证了跨引擎的互操作性:Docker 侧用docker save/docker load与 Podman 的save/load可以互相读取归档。

5.2 一次保存多个镜像、带 tag 保存

podman save支持同时指定多个镜像名(例如podman save -o bundle.tar httpd nginx),归档内会包含多个镜像的 manifest;导入时可用podman load一次性恢复。若本地镜像存在多个 tag,save会保留镜像的 tag 信息。

5.3 目标主机已有同名镜像时的行为

在远程主机上podman load时,如果目标主机已存在相同镜像:

  • 若层内容与 ID 完全相同,Podman 会识别层已存在(对应 README 中"拉取镜像时看到already exists"的层复用机制),不会重复写入数据;
  • 若同名 tag 指向不同内容,导入可能覆盖该 tag 的指向,导入前建议先podman image ls检查目标主机现状,必要时先清理旧镜像(podman rmi <image>)。

5.4 删除归档的注意事项

镜像删除(podman rmi IMAGE)只会移除本地镜像存储中的镜像,不会自动删除已生成的 tar 归档文件;反之,删除归档也不影响已加载的镜像。两者是独立的资源,清理时需分别处理。

6. 传输环节:rsync参数详解与替代方案

6.1 为什么参考答案选rsync -azc

  • -a保留归档元数据,避免因时间戳差异导致目标文件"看起来不一致";
  • -z对网络传输友好,尤其是跨广域网拷贝大镜像时收益明显;
  • -c强制基于校验和判断同步状态,确保传输后目标端与源端内容逐字节一致——这对镜像这种"内容寻址、哈希敏感"的数据尤为重要。

6.2 传输后完整性校验

镜像层的内容由哈希标识(distribution hash 是压缩层的哈希),任何一位数据被篡改都会导致加载失败或校验错误。传输后可先做一步校验再load:

# 源主机 sha256sum httpd.tar # 远程主机 sha256sum /tmp/httpd.tar # 两次输出应完全一致

6.3 无网络场景

在彻底断网的场景下,可把归档拷入 U 盘、移动硬盘等介质带到目标主机,再执行podman load -i。归档的自包含特性(manifest + 全部层 blob 在一个文件中)使这种离线搬运完全可行,这也是"无 Registry 共享"方案在隔离环境中的价值所在。

7. 常见问题与排查

现象可能原因处理建议
podman save报错找不到镜像本地镜像不存在或 tag 写错先podman image ls确认镜像名与 tag
传输后podman load失败/校验失败归档在传输中损坏或不完整用sha256sum对比两端校验和,重新传输
加载后podman image ls看不到加载的是无 tag 的镜像(dangling image)用podman images -a查看全部镜像,必要时用podman tag补 tag
目标主机空间不足层解压所需空间大于归档体积检查磁盘空间,或先清理目标主机无用镜像

关于"如何删除镜像"与"dangling image"等概念,仓库 README 中有专门问答:podman rmi IMAGE删除镜像,若镜像正被容器使用会失败,可用--force但更推荐先检查使用它的容器。

8. 何时用 save/load,何时用 Registry 或 commit

仓库 README 的场景问答提供了一条清晰的决策边界:

8.1 运行中的容器有 Bug 需要共享给团队——用podman commit

There is a running container that has a certain issue...podman commitcan be a good choice... 应避免用podman save/load,因为它作用于镜像而非运行中的容器;也要避免直接改 Containerfile 加入调试环境变量。

即:要复现的是"运行中容器的状态",就用 commit_image 练习 中演示的podman commit把容器固化为新镜像再分发;要分发的是"某个已构建好的镜像",才用save/load。

8.2 有网络条件时优先用 Registry

README 指出 Registry 是集中存储镜像的服务,包含一个或多个 repository,每个 repository 又包含一个或多个镜像。Registry 分发有几个save/load不具备的优势:

  • 层复用:拉取时若某层已存在会显示already exists,不会重复下载;而多个镜像各自save成独立归档时,相同层会被重复打包,归档总大小更大;
  • 按需拉取:不需要把整个归档传输到每台主机,目标主机只拉取缺失的层;
  • 版本管理:tag 与 digest(内容寻址标识,标签可变而 digest 不可变)配合,便于多版本管理。

因此正确的取舍是:内网/离线/临时场景用podman save+rsync+podman load;常规分发场景优先 Registry。

9. 扩展练习:用仓库内其他资源加深理解

本练习位于 Containers 主题 的 "Images" 练习系列中,与下列仓库资源互为补充,建议按顺序练习:

  1. Working with Images:镜像的列出、拉取、运行与删除,答案见 solutions/working_with_images.md;
  2. Layer by Layer(镜像分层):亲手构建镜像并确认哪些指令产生新层、哪些只写元数据,理解层与大小的关系,答案见 solutions/image_layers.md;
  3. Create Images on The Fly(commit):从运行中的容器固化出新镜像,答案见 solutions/commit_image.md;
  4. Sharing Images 练习原文 与 参考答案。

其中 "Layer by Layer" 的答案还给出了一条与本练习直接呼应的结论:FROM、RUN会创建新层,EXPOSE、ENV、WORKDIR只写元数据;把多条RUN用&&合并成一条可以减少层数、缩小镜像。层数越少、内容越精简,最终save出的归档也越小——这为"如何把镜像和归档做得更小"提供了实操指引。

结语

通过本练习,你掌握了一条完整且可复用的离线镜像分发链路:podman save打包 → 对比大小理解压缩层模型 →rsync安全传输 →podman load导入 →podman image ls验证。它适用于内网、气隙与临时交付场景,并与仓库中镜像分层、commit、Registry 等知识点形成完整知识闭环。在需要长期、多主机分发镜像时,再切换回 Registry 方案即可。

  • 文档
  • 教程
  • DevOps
  • 运维

【免费下载链接】devops-exercises

Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions

项目地址:https://gitcode.com/GitHub_Trending/de/devops-exercises
点击查看免费下载

相关推荐

上一篇:Filament 分页组件(Pagination)完全指南:在 Livewire 视图中实现页码、页大小与极端链接分页
下一篇:JSZip ZipObject.async() 实战指南:将压缩包内文件导出为 8 种格式并监听进度

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询