备考RH134的朋友拿到第13章“运行容器”时,很多人的第一反应是:这不就几个podman命令嘛,看着比LVM、iscsi简单多了。但等到实机练习过一轮,或者做过几套模拟问答题就会明白,这一章的失分率一点都不低。原因是它表面考命令,实际考的是容器与虚拟机的本质区别、镜像分层原理、rootless运行机制、SELinux标签、systemd集成这一整条链路。只要一个概念没理顺,选择题和操作题都会连环踩坑。
这篇文章把所有核心考点整理成问答题形式,命令全部基于RHEL 9环境验证过,涉及端口映射、持久化、systemd服务配置的部分,建议你边读边在终端里跟着敲一遍。考前两天再拿它做复盘,效率比翻一遍教材高很多。
1. 先厘清底层概念:容器、镜像与Podman
1.1 容器和虚拟机到底差在哪(选择题高频考点)
问:容器和虚拟机在隔离层面有什么本质区别?
这是第13章几乎必考的一道概念题。虚拟机依赖hypervisor模拟出完整硬件,每个虚拟机上跑一个完整的GuestOS,有独立内核、独立系统服务,启动时要经历完整的引导流程。容器则完全不同,它直接共享宿主机的内核,只对用户空间做隔离,隔离手段是内核的namespace(名称空间)和cgroups(控制组)。
具体拆开说:
- namespace负责隔离进程、网络、文件系统挂载点等资源,让容器里的进程以为自己是“独立机器”上的进程。
- cgroups负责限制CPU、内存、I/O等资源用量,避免某个容器吃光宿主机资源。
所以容器启动只需要毫秒级到秒级,虚拟机从冷启动到服务可用往往要几十秒甚至更久;容器镜像常见是几十到几百MB,虚拟机镜像经常好几个GB;一台宿主机上可以同时跑几十上百个容器,但虚拟机受硬件资源限制只能跑个位数到十多个。
答法建议:先把“共享宿主机内核”这个关键词放在最前面,再说“namespace做资源隔离、cgroups做资源限制”,最后补一句“容器具备轻量、快速、可移植的优势,但隔离强度弱于虚拟机”。
这里要特别提醒一个高频陷阱:题目如果给你“容器比虚拟机更安全,因为隔离更彻底”这种选项,直接排除。共享内核意味着宿主机和容器之间、容器和容器之间的攻击面更大,容器安全的核心卖点是进程隔离和权限最小化,不是完全隔离。RH134的知识点问答题喜欢在这里埋雷。
1.2 镜像、容器和仓库三者是什么关系
问:镜像、容器、仓库的概念以及它们之间的区别是什么?
镜像是一个只读的静态模板,由多层只读layer组成,层之间可以复用。比如你在一个基础镜像上安装了nginx,生成的新镜像会复用基础镜像的所有层,只把新增的软件层追加在上面。容器则是镜像运行后的实例,在镜像最上层加了一个可写层,容器内对文件系统的修改都落在可写层里,不会动镜像本身。
仓库就是管理镜像的地方,用来存储和分发镜像。日常说的“拉镜像”就是从仓库把镜像下载到本机,“推镜像”是把本地镜像上传到仓库。
用一个生活化类比:镜像好比一个装好的电脑系统的克隆U盘,容器是拿着这个U盘装好系统、正在使用的电脑;仓库则是存放各种U盘镜像的网盘。容器删掉后,镜像还在,可以再创建新容器,但容器里的临时修改会全部消失——这也是引出数据持久化那一节的前提。
1.3 为什么RHEL默认使用Podman而不是Docker
问:RHEL上为什么要用Podman替代Docker环境?
这道题在问答题里经常会变着花样出现,核心答案围绕三点。
第一,命令兼容。podman run、podman ps、podman pull这些命令和Docker几乎一一对应,从Docker迁移过来几乎不需要改操作习惯。第二,无守护进程。Docker需要一个常驻后台的daemon来管理容器,这个daemon一旦以root权限运行,就会成为宿主机上的一个高价值攻击目标。Podman没有常驻daemon,每个podman命令都是独立进程,通过fork/exec直接和容器运行时通信,攻击面和故障点都更小。第三,rootless支持更彻底。普通用户不需要sudo也能运行容器,这是RHEL主打的安全性设计。
你再记一个附加知识点:RHEL目前通过container-tools模块统一提供Podman、Buildah、Skopeo三件套。Buildah用来构建镜像,Skopeo用来检查、复制远程镜像,Podman负责运行容器。考试选择题如果问“哪条命令能在不启动容器的情况下查看远程镜像信息”,答案优先考虑skopeo inspect,不是podman inspect。
2. 镜像管理:考试前必须掌握的几条命令
2.1 怎么搜索并下载一个合适的镜像
问:如何从仓库搜索并下载一个镜像到本地?
常规三步操作:
# 搜索镜像 podman search nginx # 拉取镜像,建议写明完整的仓库路径和tag podman pull registry.access.redhat.com/ubi9/nginx-118 # 查看本地镜像 podman imagespodman search返回的结果一般包含镜像名、描述等信息,实际练习时可以先搜索确认镜像是否存在,再决定pull。生产环境里不建议只写nginx:latest这种模糊tag,latest的含义在不同时间点可能不同,考试一般不深究,但我会建议你养成给镜像加明确tag的习惯。
阅读podman images输出时,重点关注REPOSITORY、TAG和IMAGE ID这三列。IMAGE ID是镜像的短ID,镜像完整ID要用podman inspect查看。
提示:实验环境如果无法访问公网,先检查题目里面给定的registry路径,通常是一个内网地址;pull之前可以用curl快速确认仓库端口通不通,避免盲目等待超时。
2.2 查看镜像细节、打标签、删除镜像
问:给镜像重新打标签、删除镜像用什么命令?
打标签:
podman tag registry.access.redhat.com/ubi9/nginx-118:latest mynginx:1.0打标签只是给同一个镜像多了一个引用名,不会复制镜像数据。执行完podman images会看到两个名字指向同一个IMAGE ID。
删除镜像:
podman rmi mynginx:1.0如果镜像正被某个容器引用,rmi会报错并拒绝删除,需要先把引用它的容器删掉,再删镜像。另一个容易混淆的点是:同一个镜像存在多个tag时,podman rmi 某个tag只会删除这个tag引用,镜像本身在还有别的tag时不会被真正删除。
避坑:我不建议在练习时用podman rmi -f强删镜像。-f会强制把所有悬空镜像一并清理,误删的镜像后面想找回来很麻烦,考试操作题里也没必要冒着误删风险使用它。
2.3 登录私有仓库需要注意什么
问:拉取私有仓库里的镜像需要执行什么操作?
podman login 仓库地址 podman logout 仓库地址登录后凭据默认保存在${XDG_RUNTIME_DIR}/containers/auth.json,不是大家习惯的~/.docker/config.json,这一点在判断题里偶尔会出现。选择题还常考一个句子:“不登录就无法从私有仓库拉取镜像”。这个说法是对的,公共仓库比如仓库广场里的公开镜像可以直接pull,私有仓库必须登录认证后才能访问。
3. 运行容器:核心参数与生命周期管理
3.1 怎样启动一个交互式容器
问:运行一个容器并进入它的shell,命令行怎么写?
podman run -it --name demo registry.access.redhat.com/ubi9/ubi:latest /bin/bash-i表示保持标准输入打开,-t表示分配一个伪终端,两个参数组合起来才能获得一个可以正常交互的shell。--name demo给容器取一个固定名字,后面操作起来比记一长串随机ID方便得多。命令行最后的/bin/bash就是容器要执行的主进程。
进入容器后你可以正常执行命令,输入exit退出。注意这个细节:这个容器的主进程是/bin/bash,shell退出,主进程结束,容器状态就会变成Exited。如果你希望容器退出后继续存在而不是自动删除,不要给这个命令加--rm,先让容器保留下来,方便后续练习podman start。
3.2 后台运行容器并映射端口
问:让容器在后台运行,并把宿主机8080端口映射到容器80端口?
podman run -d --name web -p 8080:80 镜像名 curl http://127.0.0.1:8080-d让容器在后台运行,不会占用当前终端。-p的完整格式是“宿主机IP:宿主机端口:容器端口”,省略IP时默认监听所有接口。
这里可以补充一下端口映射的原理:容器默认有自己的网络命名空间,通过Podman创建的网桥接入容器网络,桥接模式下容器地址通常是172.x的一个私有地址。-p 8080:80实际上是在宿主机上建立一条DNAT规则,把到达宿主机8080端口的流量转发到容器IP的80端口。所以外部客户端访问的是“宿主机IP:8080”,容器内部服务监听的是80端口,两者不是一回事。
常见报错:如果宿主机8080端口已经被占用,podman run会直接报端口bind失败。解决方法是换一个宿主机端口,比如-p 8081:80,或者先用ss -tlnp | grep 8080确认占用进程。
3.3 容器的启动、停止、重启和删除
问:容器停止、重启、删除以及查看状态分别用什么命令?
# 查看运行中的容器 podman ps # 查看所有容器,包括已退出的 podman ps -a # 启动一个已存在但未运行的容器 podman start 容器名 # 停止容器,默认等待10秒 podman stop 容器名 # 重启容器 podman restart 容器名 # 暂停与恢复 podman pause 容器名 podman unpause 容器名 # 删除已停止的容器 podman rm 容器名 # 强制删除运行中的容器 podman rm -f 容器名podman ps的STATUS列是重点,常见状态包括Created(已创建但没启动)、Up(运行中)、Exited(已退出)、Paused(已暂停)。选择题喜欢给一段场景让你判断容器处于什么状态,比如“容器主进程被杀,容器进入什么状态”,答案就是Exited。
3.4 进入运行中的容器执行命令
问:容器已经在后台运行,如何进入容器内部执行命令?
podman exec -it web /bin/bashexec非常重要,它是在容器内额外启动一个进程,不会影响容器主进程的运行。在容器里执行exit退出时,只是退出这个新开的shell,容器本身继续运行。
这里要强调一下attach和exec的区别。podman attach web是连接到容器主进程的输入输出上,如果操作不当,按Ctrl+D或Ctrl+C可能把主进程终止,导致容器退出。因此需要进容器操作时,优先用exec,attach看一眼即可。考试操作题要求“进入容器并修改配置文件”,正确动作就是exec。
问:容器日志怎么查看?容器异常退出怎么排查?
podman logs web podman logs -f web podman logs --tail 50 web-f缩写是follow,实时跟踪日志;--tail 50只看最近50行。容器反复异常退出时,先看日志,再看podman inspect里的退出码和错误描述:
podman inspect web | grep -A10 StateState字段里的ExitCode、Error、OOMKilled都要会读。OOMKilled为true说明容器被系统杀掉,一般是内存超限,不是代码问题。
3.5 易错参数:--rm和--restart
问:--rm和--restart分别解决什么问题?
--rm表示容器退出时自动删除,适合一次性任务,避免容器堆积。比如临时跑一个脚本,跑完就自动清理。但它有一个坑:和-d一起使用时,容器一旦停止就悄悄被删掉,考生如果用podman ps -a找不到刚才的容器,会以为是命令执行失败,其实是--rm生效了。
--restart设置重启策略,常见值有no、on-failure、always、unless-stopped。RHEL 9里如果容器交给systemd管理,一般不依赖--restart,而是用systemd的Restart=指令,这一点在后续systemd小节会展开。
4. 数据持久化:卷与绑定挂载
4.1 为什么容器删除后数据会丢
问:容器重启或删除后,里面产生的数据为什么不见了?
因为容器对文件系统的修改都写在容器可写层,可写层和容器生命周期绑定,删除容器时这一层数据就一并删除了。想要数据保留下来,必须把数据放到宿主机上由外部存储承载,这就是卷(volume)和绑定挂载(bind mount)存在的意义。
4.2 volume和bind mount有什么区别、怎么选
问:创建数据卷和使用绑定挂载分别怎么写?各自适合什么场景?
使用数据卷:
podman volume create mydata podman run -d --name db -v mydata:/var/lib/mysql 镜像名使用绑定挂载:
podman run -d --name web -v /srv/web:/usr/share/nginx/html 镜像名两种方式的区别用表格看更清楚:
| 对比项 | 数据卷(volume) | 绑定挂载(bind mount) |
|---|---|---|
| 数据位置 | Podman管理的目录,路径自动分配 | 宿主机任意指定目录 |
| 是否好备份 | 需要podman volume系列命令维护 | 直接备份宿主机目录即可 |
| 适合场景 | 数据库、应用数据等持久化存储 | 配置文件、代码目录、日志目录 |
| 命名管理 | 有独立名字,多个容器可复用 | 没有独立名字,靠路径识别 |
数据卷更“干净”,因为它不会打乱宿主机目录结构,适合存数据库文件;绑定挂载更直观,适合把宿主机的nginx配置目录或应用代码直接塞进容器,改宿主机文件容器内马上生效。
4.3 SELinux标签:Z和:z——最容易翻车的考点
问:为什么绑定挂载后容器内经常报Permission denied?加了:Z和:z有什么不同?
RHEL默认开启SELinux强制模式。宿主机普通目录通常带的是home等类型的SELinux标签,而容器进程运行时的域是container_t,默认策略下容器进程访问不带container_file_t标签的目录会被拒绝。这就是很多同学明明挂载成功、路径没错,容器里却无法写文件的原因。
解决办法是在挂载参数后面追加SELinux重打标签选项:
podman run -d -v /srv/web:/usr/share/nginx/html:Z 镜像名:Z表示私有,系统会为当前容器生成一个唯一的SELinux标签,只有这个容器能访问;:z表示共享,多个容器可以共用同一个标签来访问该目录。一个共享目录如果被多个容器挂载,必须用:z,否则容器之间互相访问不了。
避坑:加了:Z之后,宿主机上其他非容器服务再访问这个目录,反而可能被SELinux拦截,因为标签类型被改变了。所以生产环境里要慎重决定是否把某个重要目录直接给容器打标签,更好的做法是规划专门的容器数据目录。
5. 把容器注册成systemd服务:第13章的压轴考点
5.1 为什么要用systemd管理容器
问:宿主机重启后,容器会自己恢复运行吗?
默认不会。容器只是一个用户空间进程,宿主重启后进程全部结束,Podman不会自动恢复它们。要让容器实现开机自启,标准做法是把容器注册为systemd服务,由systemd负责拉起、守护和日志管理。这也是RH134第13章操作题最常见的出题方向之一。
5.2 用generate systemd生成服务单元
问:如何把一个已有的容器转换为systemd服务?
分两步走。先正常启动一个容器:
podman run -d --name web -p 8080:80 镜像名然后生成服务单元文件,强烈建议加上--new:
podman generate systemd --new --name web > /etc/systemd/system/container-web.service--new的作用非常关键,它让systemd在每次服务启动时根据镜像重新创建容器,而不是去复用那个指定的已存在容器。这样即使原来的容器被误删,服务依然能正常拉起。如果省略--new,服务启动时会去找一个叫web的容器,容器不存在服务就直接失败。
生成后执行:
systemctl daemon-reload systemctl enable --now container-web.service5.3 部署服务单元后常见的两个错误
第一个错误是忘记daemon-reload,直接执行systemctl enable,系统提示找不到unit文件。第二个错误是原容器还在运行,端口被占用,systemd启动新容器时端口bind失败。处理方法是先把原容器停止并删除,再启动systemd服务:
podman stop web && podman rm web systemctl restart container-web.service日志查看也换成systemd方式:
journalctl -u container-web.service5.4 rootless容器怎么实现开机自启
问:普通用户(非root)运行的容器,如何配置开机自启?
rootless容器不能用/etc/systemd/system下的root级unit,正确做法是把服务单元放到用户级目录。先开启用户驻留功能:
loginctl enable-linger 用户名再把生成的unit文件放到~/.config/systemd/user/目录,然后:
systemctl --user daemon-reload systemctl --user enable --now container-web.servicelinger在这里的含义是:允许该用户在没有登录会话的情况下,用户级systemd进程依然运行,这样rootless容器才能真正做到开机自启。不开启linger的话,用户一注销,用户级systemd退出,容器全部跟着停。
6. 网络、端口与防火墙避坑
6.1 容器有哪几种常见网络模式
问:Podman容器有哪些网络模式,分别怎么用?
- 默认桥接模式:容器接入Podman创建的网桥,获得172.x私有地址,外部访问需要
-p做端口映射。 - host模式:容器直接使用宿主机网络栈,容器内看到的就是宿主机IP和端口,不需要也不存在DNAT转发。适合对网络性能要求高、或端口经常变动的场景。
- none模式:容器没有网络,适合离线任务。
- 自定义网络:
podman network create mynet创建,podman run --network mynet接入。自定义网络的好处是容器之间可以通过容器名互相访问,重启容器IP变了也不影响。
问:容器重启后IP发生变化,是不是故障?
不是。默认桥接模式下,容器IP是动态分配的,重启后可能变化。需要固定访问入口时,用自定义网络和容器名解析,或直接用host模式。
6.2 端口映射后外部访问不了怎么排查
问:-p 8080:80配了,curl反正不通,怎么定位?
按下面的顺序排查:
# 第一步:确认容器在跑 podman ps # 第二步:确认端口映射生效 podman port web # 第三步:检查宿主机监听端口 ss -tlnp | grep 8080 # 第四步:检查防火墙 firewall-cmd --list-allRHEL 9的Podman和firewalld有集成,端口映射一般会自动生成放行规则,但firewalld重启后规则偶尔会丢失。干脆把端口显式加入白名单更稳:
firewall-cmd --permanent --add-port=8080/tcp firewall-cmd --reload6.3 host网络和桥接网络选择上的提示
考试选择题有时会把host和桥接模式混在一起考区分点。记住两句话:host模式性能好、直接暴露端口,但容器内端口和宿主机端口直接冲突;桥接模式隔离性好、需要显式映射端口,但容器间通信默认也要走网桥或自定义网络。做题看到“要求容器直接使用宿主机IP端口”选host,看到“多个容器通过网络隔离”选桥接或自定义网络。
7. 高频问答题速查清单
下表是我按RH134第13章的问题风格整理出的速查表,适合考前一小时快速过一遍:
| 问题 | 答题要点 |
|---|---|
| 容器与虚拟机有什么区别 | 共享宿主机内核,namespace+cgroups隔离用户空间;镜像小、启动快;隔离强度弱于虚拟机 |
| 如何搜索并下载镜像 | podman search、podman pull 仓库路径:tag、podman images验证 |
| 如何运行并进入容器 | podman run -it --name 容器名 镜像名 /bin/bash |
| 如何让容器后台运行 | podman run -d |
| 如何映射端口 | podman run -p 宿主机端口:容器端口 |
| 如何进入运行中的容器 | podman exec -it 容器名 /bin/bash |
| 如何查看容器日志 | podman logs -f 容器名 |
| 如何持久化容器数据 | volume或bind mount,注意SELinux标签:Z/:z |
| 如何让容器开机自启 | podman generate systemd --new生成unit,放到systemd目录并enable --now |
| rootless容器怎么开机自启 | 用户级systemd unit +loginctl enable-linger |
| 镜像删除失败怎么办 | 有容器正在引用镜像,先删容器再删镜像 |
| 容器IP重启变了 | 默认桥接网络动态分配,使用自定义网络或host模式 |
这组问题要练到“看到题目就能条件反射说关键词”的程度。第13章的操作题不会考冷门参数,但一定考命令的准确组合,比如-it漏一个,exec写成attach,generate systemd漏了--new,这些都是扣分点。
8. 我自己复习时踩过的几个坑
8.1 别把问答题背成命令填空题
我第一次复习时只盯着命令记,run、exec、logs背得滚瓜烂熟,结果被一道“容器和虚拟机隔离原理的区别”问答题打得措手不及。后来把重点调整成“先背动作动词”:题目说“运行”就答run,说“进入”就答exec,说“持久化”就答“卷或绑定挂载”,说“开机自启”就答“systemd”。每个动作再往下想一层,比如run要配哪几个参数、为什么配这些参数,知识点就能串成线。
8.2 实验镜像尽量用UBI基础镜像
复习时不要纠结于必须拉某个特定镜像。RHEL环境直接拉UBI基础镜像,体积小、下载快、和在考试环境里操作没有区别。我见过同学在实验环境里反复尝试拉一个很大的镜像,卡在下载上浪费时间,完全没有必要。
8.3 问答题的答案要先说关键词再说展开
无论模拟题还是自测,回答格式都建议采用“关键词一句话 + 命令/参数 + 一到两句理由”。比如“如何让容器后台运行”:-d参数让容器脱离当前终端在后台运行,需要停止时用podman stop。先给结论再补细节,阅卷和自查都更容易踩中得分点。
8.4 多敲podman --help和man页
最后分享一个小技巧:RHEL 9环境里podman --help、man podman-run、man podman-generate-systemd这些帮助信息都可以现场查询。做题时卡住了,与其硬想不如敲一下帮助页。我第二次复习时就是靠着这个习惯,把generate systemd --new的确切用法重新校准了一遍,避免了一个很大的记忆偏差。这一章知识点看着多,真正需要死记的也就十来条命令和它们背后的原理,把逻辑理清楚,考试基本不会丢分。