ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Podman镜像管理与清理实战:从删除到加速配置

2026/9/17 5:53:29 拓冰建站 浏览量
Podman镜像管理与清理实战:从删除到加速配置 1. 先搞清楚podman的镜像管理逻辑很多人第一次接触podman是冲着“无守护进程”“rootless安全容器”这些卖点去的但真用起来第一个绕不开的操作就是镜像管理。毕竟不管是跑nginx、redis还是自研应用第一步都是把镜像拉下来、跑起来等环境乱了再一趟一趟清理。而这其中删除镜像、清理镜像的命令和镜像加速的配置是日常最高频的动作。先说个基础认知podman的CLI命令设计上和docker高度兼容你用惯了docker的话podman基本是零成本上手。但两者的底层架构完全不同docker依赖一个常驻的dockerd守护进程来管理镜像和容器而podman采用fork-exec模型每一个命令直接由podman进程处理不依赖守护进程。这个差异直接带来了一个关键结论podman的镜像管理命令更轻、更直接出了问题也更容易定位到具体的镜像层。另一个重要区别是镜像存储位置。docker默认把镜像放在/var/lib/docker下而rootless模式下podman的存储路径是$HOME/.local/share/containers/storageroot模式是/var/lib/containers/storage。这意味着你清理podman镜像时磁盘空间的变化看的是这些目录别去盯着docker的老路径看。下面我用一个日常场景来拆解你拉了一堆镜像做测试有几十个悬挂镜像dangling images还有一堆不用的容器没删磁盘眼看要满了。这时候该怎么一步步把空间收回来每一步背后是什么原理这就是这篇文章想给你讲清楚的事。2. 镜像查询与删除命令从列出到精准删除2.1 摸清家底先看有哪些镜像删除之前必须先看清现状这一步很多人会忽略直接用rmi -f一把梭结果把还在用的镜像tag删了容器运行直接报错。我建议的流程是先执行三个查询命令podman images podman ps -a podman system dfpodman images列出本地所有镜像默认只显示顶层镜像如果想看中间层构建过程中产生的层可以加-a参数但这个输出会很杂日常不需要。podman ps -a列出所有容器包括已经退出的如果不加-a只能看到正在运行的容器。podman system df非常关键它像磁盘分析工具一样展示镜像、容器、卷各自占用多少空间以及可回收的空间量执行后输出大概长这样TYPE TOTAL ACTIVE UNUSED RECLAIMABLE Images 12 4 8 3.2GB Containers 23 2 21 4.1GB Local Volumes 5 1 4 800MB看这个输出你就知道真正能腾出空间的是“Unused”和“RECLAIMABLE”两列。不要看到总量大就慌先确认哪些是可清理的。2.2 删除镜像的三种命令姿势删除镜像的命令主要有podman rmi和podman image rm两者是等价的rmi是image rm的简写。基本用法如下podman rmi nginx:latest podman rmi 镜像ID镜像ID不一定要写全podman支持前缀匹配只要前几位能唯一识别就行比如podman rmi 3f8。但有个默认规则要知道podman不允许删除正在被容器使用的镜像除非加-f强制删除。这个限制是保护机制防止你删掉镜像后容器启动不了。我的建议是能不用-f就不用因为你迟早会发现强制删除带来的问题比省下的时间更麻烦。删除多个镜像也支持直接把多个标识写在一起podman rmi nginx:latest redis:7.0 mysql:8.0如果要删除本地所有镜像可以用podman rmi -a。注意这个-a会尝试删除所有镜像但同样被容器引用的镜像会删除失败所以通常要和podman rm -a配合使用——先把容器清空再删镜像。2.3 镜像tag与ID的关系为什么删了tag镜像还在这里有一个非常容易踩坑的知识点同一个镜像可以打多个tag镜像的底层数据由镜像ID唯一标识。比如你先pull ubuntu:22.04然后tag ubuntu:22.04 ubuntu:my-test这两个tag指向同一个镜像ID。当你执行podman rmi ubuntu:my-test时podman只是删掉了这个tag引用镜像的层数据依旧保留因为还有ubuntu:22.04这个tag在引用它。只有当某个镜像ID没有任何tag引用时podman rmi才会真正删除层数据。这个机制和docker完全一致理解它你就不会困惑“为什么我删了tag磁盘空间没变化”。如果你确实想把一个镜像连同它所有tag和层数据都删干净先列出该镜像ID的所有repo tagpodman images --format {{.ID}} {{.Repository}}:{{.Tag}} | grep 镜像ID前几位确认没有其他tag引用后再用podman rmi 镜像ID。如果你足够确定要彻底删除也可以直接podman rmi -f 镜像ID但风险自负后续需要时就得重新拉取。3. 清理镜像与系统垃圾把空间真正收回来3.1 悬挂镜像的处理悬挂镜像也就是dangling images指没有tag、也不被任何容器引用的镜像层数据。它多出现在重复build或pull时旧镜像被新镜像替换了tag引用旧层就成了悬挂状态。累积多了磁盘上会白白躺着几个GB的“尸体”。查看悬挂镜像podman images --filter danglingtrue清理悬挂镜像最安全的命令是podman image prune执行后会列出将被删除的悬挂镜像并询问是否继续。加-f跳过确认。这个命令只删悬挂镜像不会误伤正常镜像属于日常清理的“安全选项”。3.2 更狠的system prune除了悬挂镜像还有退出状态的容器、不再使用的网络、build cache等都是占空间的黑洞。podman system prune是清理这些垃圾的代表性命令。基础版podman system prune它默认会清理已停止的容器悬挂镜像没有被容器使用的网络构建缓存如果你想把没有被任何容器引用的镜像也一并删掉加-apodman system prune -a这里的-a和rmi -a的语义类似但略有不同它删除的是“没有被容器使用”的镜像而不只是悬挂镜像。举个例子你拉了个postgres:15做了测试后来容器删了镜像还在但没有任何容器引用它system prune -a会把它删除。这个威力很大执行前我会建议先用podman system df看看可回收空间再决定要不要上-a。3.3 删除所有容器和镜像的完整组合拳如果环境已经乱到不想一个个排查可以用一套“组合拳”快速重置。注意这套操作默认删除所有容器和镜像不可恢复仅在测试环境或确认不需要时使用podman stop -a # 停止所有运行中的容器 podman rm -a # 删除所有容器包括已停止的 podman rmi -a # 删除所有镜像如果遇到占用资源无法删除的容器再加-t指定超时时间比如podman stop -t 5 -a给容器5秒优雅退出时间超时后强制停止。这一步我经常在持续集成节点或本地开发环境混乱时用执行完基本回到初始状态。3.4 清理后的磁盘检查清理完别急着走验证一下空间是否真的释放了。用两个命令交叉确认podman system df df -h $HOME/.local/share/containers/storagepodman system df看的是podman视角的释放量而df -h看的是文件系统的真实变化。这里有个细节有时候即使podman报告删除了几个GB的镜像磁盘空间也没有立即释放可能是因为文件系统延迟回收也可能是podman的存储驱动如overlayfs的层目录还在被某个进程占用。稍等一会儿再执行一次df -h确认。如果依然没释放检查是否有容器处于Created状态——有些容器创建了但没跑过也会占用镜像层引用。4. 镜像加速配置拉镜像不再干等4.1 为什么要配置加速默认情况下podman从docker.io拉取镜像在国内网络环境下速度经常让人崩溃一个几百MB的镜像能拉半小时。这不是podman本身的问题而是默认仓库的访问链路。解决办法就是给podman配置国内可用的镜像加速源。podman的镜像加速配置和docker有个本质区别docker改的是/etc/docker/daemon.jsonpodman没有守护进程改的是registries.conf。所以你在网上搜“podman 镜像加速”时如果看到让你改daemon.json的教程那基本是拿docker思路套的可以直接跳过。4.2 registries.conf的配置位置与结构registries.conf文件有两个层级系统级/etc/containers/registries.conf用户级~/.config/containers/registries.conf系统级对所有用户生效用户级只对当前用户生效用户级优先级更高。我自己更推荐改用户级原因有两个一是避免sudo权限问题二是不同用户可以有不同配置互不干扰。实际测试下来podman会优先读取用户级配置如果该文件不存在则读取系统级文件。配置文件的结构是基于TOML语法的核心配置项是unqualified-search-registries和[[registry]]。前者定义“不带仓库前缀时默认去哪找镜像”后者定义某个特定仓库的镜像源配置。我的用户级配置示例unqualified-search-registries [docker.io] [[registry]] prefix docker.io location docker.io [[registry.mirror]] location docker.m.daocloud.io [[registry.mirror]] location dockerproxy.cn这个配置的含义是当你不带前缀执行podman pull nginx:latest时podman会去docker.io找镜像如果发现docker.io访问慢或不稳定它会依次尝试配置的mirror源也就是docker.m.daocloud.io和dockerproxy.cn。mirror和prefix的关系是mirror是目标仓库这里是docker.io的加速镜像而不是替代品。这个设计和Maven的中央仓库镜像、npm的registry镜像原理一样都是“原仓库 → 镜像源”的转发模式。4.3 加速源的选择与验证加速源的可用性变化很快今天能用的明天可能就挂了。选源我有几个标准支持https不需要额外改配置拉取速度稳定不能一会儿快一会儿断同步延迟可控拉下来的镜像不能比官方旧太多我个人常用的几个加速源按优先级排序加速源地址备注DaoClouddocker.m.daocloud.io速度稳定免费额度足够日常用中科大docker.mirrors.ustc.edu.cn高校源稳定性较好腾讯mirror.ccs.tencentyun.com内网环境下很快网易hub-mirror.c.163.com老牌源偶尔有同步延迟验证加速源是否生效可以执行podman info | grep -A 10 registries输出里会显示查询到的镜像仓库列表。接着拉一个小镜像测速比如podman pull alpine:latest观察下载用时和速度。如果速度依旧慢就逐个更换mirror项直到找到一个能稳定跑满带宽的源。4.4 配置代理与加速的取舍有些场景下光靠国内加速源还不够比如你需要拉取某些仅存在于quay.io或ghcr.io的镜像这些仓库通常没有国内镜像。这时可以考虑给podman配置网络代理。注意这里的“配置代理”是指给podman的拉取进程设置HTTP代理让它通过通用代理访问目标仓库。配置方法和大多数命令行工具类似在当前shell环境设置环境变量export HTTP_PROXYhttp://127.0.0.1:7890 export HTTPS_PROXYhttp://127.0.0.1:7890 export NO_PROXYlocalhost,127.0.0.1,.localpodman pull会读取这些环境变量走代理去连接远程仓库。这个方案适用于“加速源找不到镜像”的情况但如果你主要是拉docker.io的官方镜像优先用mirror因为mirror在国内访问更快更稳。另外如果你希望代理配置持久生效可以把export语句写入~/.bashrc或~/.zshrc。但要注意这类全局代理会影响所有走HTTP协议的命令如果你不想让git、curl也跟着走代理建议只在执行podman命令的终端里临时设置或者写一个小的shell函数podman_pull() { HTTPS_PROXYhttp://127.0.0.1:7890 podman pull $ }这样可以做到“只有podman走代理其他命令不受影响”。5. 常见问题与排查技巧5.1 镜像删不掉“image is being used by container”这是最典型的报错执行podman rmi nginx:latest大概率遇到。原因很直白有一个容器不管是否在运行引用着这个镜像。解决思路不是强删而是先处理容器podman ps -a | grep nginx podman rm 容器ID podman rmi nginx:latest如果这个容器你以后还要用可以保留容器只把镜像换掉但一般测试场景下直接删容器更快。如果你确认这个容器已经没用了podman rm -f 容器ID可以强制删除不需要先stop。5.2 删除后磁盘空间没变化删除镜像后磁盘空间纹丝不动大概率是下面几种情况之一镜像层数据被多个镜像共享你删的只是其中一个tag。执行podman rmi后podman只是减少了引用计数直到引用计数归零才会真正删除层数据。容器仍然存在。即使容器退出只要它还在podman ps -a里它引用的镜像是不会释放的。所以清理顺序是先删容器再删镜像。overlayfs的upperdir残留。极端情况下可以重启podman的存储驱动或者用podman system reset全面重建存储目录这会清空所有镜像、容器、卷慎用。5.3 加速配置不生效配置了mirror后拉取速度还是老样子先检查配置是否被正确读取podman info --format {{.Registries}}输出应该包含你配置的mirror地址。如果没包含确认配置文件路径是否正确语法是否有误。另外prefix字段必须准确匹配你拉取镜像时使用的仓库名如果你执行的是podman pull docker.io/library/nginx:latest那prefix就要是docker.io而不是nginx。5.4 悬挂镜像清理不干净podman image prune有时会显示“nothing to prune”但podman system df显示RECLAIMABLE很大。这个矛盾的常见来源是build cache和容器日志。build cache可以用podman build --prune或者手动podman system prune --all --volumes最后那个--volumes会把未使用的数据卷也清掉威力极大执行前确认无重要数据。我的习惯是日常用小清理podman image prune -f每周做一次大清理podman system prune -f特殊情况再上-a和--volumes。5.5 命令对比速查表把最常用的清理操作整理成表方便查阅目标命令风险查看镜像podman images无查看磁盘占用podman system df无删除单个镜像podman rmi 镜像低删除悬挂镜像podman image prune -f低停止所有容器podman stop -a低删除所有容器podman rm -a中容器不可恢复删除所有未使用镜像podman system prune -a高无引用镜像全删重置所有数据podman system reset极高测试环境专用6. 关于日常镜像维护的一点心得镜像管理这件事说到底是“增量清理定期复盘”的体力活。podman的命令再强大也顶不住你每天无脑拉镜像、建容器、不清理。我个人维护开发机的经验是每次拉取新镜像前先问一句“这个镜像我有现成的吗”每次用完测试容器后顺手podman rm一把每周末花两分钟跑一次podman system prune -f。这套习惯坚持下来镜像堆积的速度会慢很多。最后分享一个小技巧如果你经常在同一个仓库比如docker.io拉镜像不要把加速源写成一长串选两个稳定的就够。mirror配置多了拉镜像时podman会逐一尝试失败的源像超时重试一样反而拖慢速度。我实测三个mirror以上时首次拉取会因连接超时多等几秒配置两个效果最好。再补充一个排查思路遇到podman任何和镜像相关的诡异问题先看podman info和podman system df这两个命令能把底层存储状态呈现得很清楚再看~/.config/containers/registries.conf确认配置没写错最后再看具体报错。这个排查顺序能解决大部分问题。