- 文档
- 教程
- 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
在无法访问镜像仓库(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 个递进目标,覆盖"打包 → 对比 → 传输 → 导入 → 验证"的完整闭环:
- 选一个镜像并创建归档:使用
podman save把镜像导出为 tar 归档文件; - 对比归档大小与镜像大小:观察两者是否有差异,并解释原因——这是本练习的"思考题",也是理解镜像存储模型的关键;
- 把归档拷贝到远程主机:使用
rsync(或scp等)完成传输; - 在远程主机加载镜像:使用
podman load把归档导入本机镜像存储; - 验证镜像已加载并存在:用
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 ls3.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-archive | Docker 兼容的 tar 归档(默认,Podman 与 Docker 可互导) |
oci-archive | OCI 镜像规范归档 |
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" 练习系列中,与下列仓库资源互为补充,建议按顺序练习:
- Working with Images:镜像的列出、拉取、运行与删除,答案见 solutions/working_with_images.md;
- Layer by Layer(镜像分层):亲手构建镜像并确认哪些指令产生新层、哪些只写元数据,理解层与大小的关系,答案见 solutions/image_layers.md;
- Create Images on The Fly(commit):从运行中的容器固化出新镜像,答案见 solutions/commit_image.md;
- 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
相关推荐
Docling 版本演进技术全景解读:从 v0.2 到 v2.124 的文档解析能力图谱
Docling 版本演进技术全景解读:从 v0.2 到 v2.124 的文档解析能力图谱 本文以仓库根目录 CHANGELOG.md https://link.
文档教程DevOps运维Podman镜像管理实战:从零开始掌握容器镜像全流程
Podman镜像管理实战:从零开始掌握容器镜像全流程 想象一下,你刚刚完成了一个精彩的Web应用开发,现在需要把它打包成容器镜像,就像给应用程序准备一个"旅行箱
容器运行时云原生CLIPodman 镜像传输压缩实战:`--compress` 选项在 dir 传输中的应用(podman push / podman save)
Podman 镜像传输压缩实战: compress 选项在 dir 传输中的应用(podman push / podman save) 导读 compress
容器运行时云原生CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考