很多人看到RH134第13章“运行容器”时,第一反应往往是“容器我早就会了啊,docker run不就行了吗”,可真到模拟环境里一跑,立刻就卡住:普通用户跑容器为什么监听不了80端口?挂载目录明明有文件,容器里怎么读不到?为什么systemd里配了Restart=always还是起不来?这些问题,恰恰就是这一章真正要考的东西。
简单说,RH134的容器章节围绕的是“在单机企业Linux环境下,用rootless方式把容器跑起来并纳入系统管理”,它考的不是Kubernetes那套编排,也不是docker compose,而是Podman、SELinux、systemd这三样东西和容器之间如何协同。你如果只是背了一堆命令,却不理解背后机制,遇到排查题就会很被动。这篇文章就按问答题的思路,把这章的知识点拆开讲透,顺带把我实际练习过程中踩过的坑、总结的判断链路一起写出来。适合正在备考RH134的人,也适合那些想搞清楚rootless容器到底怎么回事的人。
1. 这章到底考什么:先框定RH134里“运行容器”的边界
1.1 考的是单机rootless,不是编排
RH134的容器章节有个很明确的范围,它不会让你搭集群,也不会让你写复杂的编排文件。考核核心就是你在一台已经装好企业Linux系统的机器上,以普通用户身份完成容器的拉取、运行、查看、管理、日志、卷、网络和systemd托管。换句话说,它考的是“把这一个容器当成一个Linux进程去管理”的能力,而不是“把一堆容器编排成服务”的能力。
这个定位特别重要。因为很多人在备考时会陷入一个误区:花大量时间去研究podman-compose、研究Kubernetes,结果真正上机的时候,却在podman exec交互、podman generate systemd这类基础操作上卡壳。我备考时的经验是,把关注点放在“容器和宿主机的边界”上,反而更容易拿分。什么叫边界?就是用户、进程、文件系统、网络、日志、自启策略这几个维度上,容器和宿主机各自管什么、谁在约束谁。
1.2 底层工具链:Podman、SELinux、systemd三件套
这一章的技术栈是三位一体的:
- Podman:负责容器本身的生命周期管理,拉镜像、起容器、看状态、删容器。它兼容Docker的命令行习惯,但没有守护进程,普通用户也能直接用。
- SELinux:负责给容器进程和容器内文件打标签。很多诡异的“权限不足”问题,根子不在Linux权限位,而在SELinux上下文。
- systemd:负责让容器像原生服务一样托管,包括开机自启、异常重启、日志归集。RH134很喜欢考systemd与容器的交互。
这三者缺一不可。实际考试时,题目不会直接说“请用SELinux排查问题”,但会在场景题里描述一个“映射目录无法访问”的现象,你要能主动想到跟SELinux有关。这就是知识点问答题和机械操作题的最大差别。
1.3 备考心态:命令好背,机制难懂
命令本身并不多,run、ps、exec、logs、stop、rm、rmi,加上volume、network、generate systemd,十几个动词就能覆盖大部分操作。真正需要花时间的是理解“为什么”。
举个例子,podman run -d -p 8080:80 nginx这条命令很多人会打,但换成-p 80:80就报错,为什么?因为rootless容器里的root并不是宿主机上的真实root,它映射的是一个普通UID,普通用户没有绑定低端口的权限。这个机制层面的理解,比多记十来个参数都有用。
所以我的建议是:不要先刷题,先把Podman的rootless模型想明白,再动手做实验。理解了模型之后,考试里那些“看似考操作、实则考机制”的题,基本都能一眼看穿。
2. 容器生命周期管理:镜像、运行、exec、清理的完整动作链
2.1 镜像获取:search、pull、inspect与digest
容器的起点是镜像。考试最基础的部分就是怎么把镜像弄到本地。常用动作是这样一条链路:
# 搜索镜像 podman search nginx # 拉取镜像 podman pull nginx # 查看本地镜像 podman images # 查看镜像详细信息(包括分层、架构、入口命令) podman inspect nginx这里有个值得展开的知识点:pull命令拉下来的镜像,在本地会有一个唯一标识,叫digest,它是镜像内容的SHA256校验值。使用镜像时,最好用registry/用户名/镜像名:标签再加上digest来锁定版本,比如nginx:1.26@sha256:xxx。为什么要这么麻烦?是因为同名标签可能被更新,而digest一旦变更,就说明镜像内容变了。这在企业环境里是安全性的基本盘,考试中很可能问“如何确认一个镜像来源可信”,答案就是看签名策略和digest。
podman inspect是个很容易被忽略但考试很爱考的命令。它能输出容器的完整元数据,包括挂载来源、网络模式、PID、SELinux标签、重启策略等。排查问题的时候,inspect的输出往往是最直接的证据。建议养成习惯:凡是容器表现异常,先用podman inspect <容器名>看看它的运行配置,再去看日志。
2.2 核心运行参数:第一次跑容器前必须看懂的选项
启动容器是这一章的主战场。最核心的命令长这样:
podman run -d --name web -p 8080:80 -v /srv/webdata:/usr/share/nginx/html:Z nginx逐个拆开说:
-d:后台运行。不加-d时容器会占据当前终端,日志直接打到屏幕上。考试一般要求后台运行。--name web:给容器起名字。不起名的话Podman会随机生成一个名字,后面所有操作都要先查名字,非常被动。-p 8080:80:端口映射,宿主8080端口转到容器的80端口。-v /srv/webdata:/usr/share/nginx/html:Z:绑定挂载目录,还带了一个SELinux重标记选项Z。
还有一些参数很常见:
-it:分配交互式终端,主要用于run一个需要现场操作的容器,比如podman run -it --name test ubuntu bash。--rm:容器退出时自动删除容器本身。适合临时跑一次性任务,比如测试配置、跑个脚本。--restart=always:设置重启策略。容器挂了之后,由Podman自动拉起。--env或-e:注入环境变量,比如-e MYSQL_ROOT_PASSWORD=xxx。--user:指定容器内运行进程的用户ID,比如--user 1000:1000,配合卷权限使用。
理解容器生命周期有个关键点:容器不是一个常驻的“虚拟机”,它的生命周期等于主进程的生命周期。主进程退出,容器就结束。比如podman run nginx,nginx前台进程一直在,容器就一直在;如果你把nginx命令换成一个echo hello,容器跑完命令就进入Exited状态。很多新手跑完podman run立刻podman ps发现列表是空的,就是因为忘了加-d,容器在前台结束后就退出了。
2.3 容器内部操作:exec、attach、logs的配合方式
容器跑起来之后,最常见的三个操作是进容器、看日志、执行命令。
# 进入运行中容器的shell podman exec -it web bash # 执行单条命令,例如查看环境变量 podman exec web env # 跟随查看日志 podman logs -f web # 只看最后20行 podman logs --tail 20 webexec是考试重点。注意-it的含义:-i是保持标准输入打开,-t是分配伪终端。如果只是执行一条非交互命令,不加-it也行;但如果想进入容器交互式操作,就必须加。
这里有个和我踩过的坑相关的细节:有些镜像里没有bash,只有sh,比如很多精简版镜像。遇到这种情况,podman exec -it web bash会直接报bash: not found,换sh就好了。考试时如果命令报错,别急着怀疑权限问题,先确认镜像里到底装了哪个shell。
logs命令的-f参数很实用,它会像tail -f一样持续输出新日志。排查容器起不来的问题时,顺序应该是:先podman ps -a看状态,再podman logs <容器名>看报错,最后podman inspect看元数据,不要一上来就盲改配置。
2.4 清理的纪律:rm、rmi、prune的边界感
容器和镜像的删除是考点,也是很容易混淆的地方。核心区分如下:
podman rm <容器>:删除已经停止的容器。podman rmi <镜像>:删除本地镜像。podman rm -f <容器>:强制删除正在运行的容器。podman container prune:清理所有已停止的容器。podman image prune:清理悬挂镜像(没有标签且不被容器引用的镜像)。podman system prune:按依赖关系清理一切无用资源,使用前要谨慎。
我见过不少人把rm和rmi搞混,结果发现容器还在,镜像却没了一大堆。记住一句口诀:先删容器,再删镜像。因为一个镜像只要还被某个容器引用,rmi就会拒绝删除并提示冲突。
另外,--rm参数是个双刃剑。它的好处是临时容器跑完自动清,比如用容器执行一次性脚本;坏处是你如果给一个需要长期运行的容器加了--rm,一旦容器异常退出,容器连同它产生的临时层都会被删除,调试现场就没了。考试如果遇到“重启后容器不见了”的题目,优先检查是不是加了--rm,或者重启策略配置有误。
3. rootless容器与SELinux:几乎所有现场事故都出在这一节
3.1 rootless容器的用户映射和网络实现原理
RH134默认要求用普通用户直接操作Podman,也就是rootless容器。这意味着容器进程本身运行在宿主机普通用户的权限范围内,而不是在root权限下。Podman会通过用户命名空间把容器内的UID映射到宿主机的普通UID上,容器内部的root在宿主机看来就是一个普通用户。
这样设计的好处是安全隔离:即使攻击者拿到了容器内root权限,在宿主机层面也只是个普通用户,破坏范围被控制在初始目录和自身进程内。坏处就是一系列限制,最典型的就是端口、挂载和SELinux。
网络方面,rootless容器默认使用slirp4netns这样的用户态网络栈,容器有一个内部的虚拟IP,通过NAT方式访问外网。外部如果要用宿主机地址访问容器服务,必须做端口映射。这个过程中,任何低于1024的端口都会因为胶质权限不足而失败。很多题目的坑就在这里。
3.2 低端口、卷挂载与SELinux标签的三个经典坑
先说低端口。普通用户跑容器时执行:
podman run -d --name web -p 80:80 nginx大概率会报错,原因就是上面提到的CAP_NET_BIND_SERVICE缺失。解决办法很简单,映射到高位端口就好。如果业务确实需要80端口,更规范的做法是让容器监听8080,再用系统的firewalld做端口转发,而不是强行让rootless容器绑低端口。考试默认走高位端口,记住这一点就能避开很多“环境有问题”的假象。
然后是卷挂载。普通用户用-v /srv/data:/container/data nginx这类绑定挂载,经常会遇到容器里对该目录只有只读权限,或者读取时直接Permission denied。大多数情况不是文件权限位的问题,而是SELinux标签不允许容器进程访问这个目录。
企业Linux系统上SELinux默认是Enforcing。容器要访问宿主目录,该目录的标签必须匹配container_file_t这个上下文。普通用户自己创建的目录,标签一般是user_home_t或var_t,和容器要求的标签不一致,于是被SELinux拒绝。解决办法是手动指定:Z或:z:
podman run -d --name web -v /srv/webdata:/usr/share/nginx/html:Z nginx:Z表示必须重新标记目标目录为容器专用私有标签,一般用于专属于某个容器的目录。:z表示共享标签,适用于多个容器共用一个目录的场景。考试很喜欢在这两个选项之间做区分,是高频失分点。
3.3 排查链路:从报错到ausearch/sealert再到修复
遇到容器权限问题,不要瞎试。成熟的排查链路是固定的:
- 先看报错原文。如果挂在文件访问上,大概率是SELinux或文件权限。
- 用
getenforce确认SELinux是否Enforcing。如果是Disabled,那就可以排除SELinux因素。 - 用
ausearch -m avc -ts recent查看最近的AVC拒绝记录,找到被拒绝的进程和目标。 - 用
sealert -a /var/log/audit/audit.log生成可读性更强的修复建议,它会直接提示是加规则还是改标签。 - 确认和容器卷有关后,选择
:Z或:z重标或直接调整文件上下文,再重启容器验证。
这套链路在考试里对应的是“现场排查”题型。我不建议死记硬背规则,而是记住“拒绝记录在audit日志里”这一点。RH134考查的不是你会不会给SELinux写自定义策略,而是你能否快速判断问题方向、使用正确工具导出证据。
另外补充一句:很多人在rootless环境下喜欢有事没事就sudo podman。这不推荐,一是不符合rootless优先的考试要求,二是切换到root后,挂载目录的SELinux上下文、归属用户都变了,反而更容易踩坑。普通用户的问题就留在普通用户层面解决。
4. 镜像签名、私有registry与数据持久化
4.1 镜像分层的现实意义:为什么统一base镜像能省大量空间
镜像由多层只读文件系统组成。每个层对应一条构建指令的结果,拉取时多个层叠加在一起形成完整的根目录。容器运行时,Podman在镜像层之上再叠加一个可写层,所有容器内的修改都在可写层里。
理解了分层,你就能明白两件事。第一,多个容器共用同一个基础镜像时,基础镜像的层在本地只需保存一份,启动新容器只是在它之上新增一个薄薄的可写层,速度快且省空间。第二,为什么考试里建议“自己构建镜像时尽量基于一个统一的基础镜像”,因为这样能最大化借助本地缓存和分层优势。
podman history nginx可以看镜像的每一层内容,考试偶尔会用来验证你是否理解“层”的概念。这个命令虽然基础,但对排查“为什么镜像这么大”“为什么镜像层明明一样却还是拉取了新层”这类问题很有帮助。
4.2 登录与签名校验:pull镜像安全性的两个层次
RH134对镜像源的信任机制有明确要求。拉取镜像之前,系统会检查这个镜像的来源和签名状态。企业环境里一般会配置好自己的私有registry,普通用户需要通过登录才能访问受保护仓库:
podman login registry.example.com podman pull registry.example.com/webapp:1.0 podman logout registry.example.com登录凭证默认保存在用户配置目录下,rootless用户保存在自己的~/.config/containers/auth.json,不同用户的凭证互不干扰。这也是考试的一个小考点:为什么普通用户登录的registry,root用户看不到?因为凭证在各自的用户目录里。
签名校验方面,企业Linux系统默认对某些可信仓库启用签名强制验证。如果拉取不信任来源的镜像,Podman有时候会给出类似“image is not signed”的警告。理解它的意义在于:镜像签名保证镜像在构建之后没有被篡改,digest保证你在运行时引用的确实是拉下来的那一个版本。考试不要求你手动配置复杂的GPG签名,但会要求你知道“为什么不能盲拉陌生来源的镜像”以及“如何锁定镜像版本”。答案就是签名策略加digest。
4.3 卷的三种玩法:绑定挂载、命名卷和卷容器
容器本身的可写层不保证持久性,容器一删,可写层跟着没了。要想让数据活得更久,卷是必考的。
绑定挂载就是把宿主目录直接映射进容器:
podman run -d --name web -v /srv/webdata:/usr/share/nginx/html:Z nginx写作方便,但受宿主目录权限和SELinux标签影响大。
命名卷是Podman管理的卷,存放在Podman的卷目录中:
podman volume create appdata podman volume ls podman run -d --name db -v appdata:/var/lib/mysql mariadb命名卷的好处是,你不必关心数据到底落在宿主机哪个具体目录,Podman帮你管好了目录权限和SELinux标签。容器删除后,命名卷不会自动删除,数据保住了。要删卷时用podman volume rm appdata,注意前提是没有容器正在引用它。
卷容器是早年间常用的一种模式:先起一个只挂载卷、不干事的容器,其他容器通过--volumes-from共享它的卷。在现代Podman环境里,命名卷已经能覆盖大多数场景,但考试题面偶尔会提这种老概念,知道它存在、能看懂--volumes-from的含义就够了。
我的建议是:日常练习就用命名卷,遇到明确要求把数据放在特定目录的场景才用绑定挂载。卷的SELinux标签由Podman自动处理,能少掉一堆权限报错。
5. 网络、日志和systemd集成:让容器像原生服务一样工作
5.1 网络模式的选择:默认NAT、host和自定义网络
rootless容器的默认网络是用户态NAT。容器内部有一个独立IP,可以主动访问外网,但外部要访问容器内服务,必须通过-p做端口映射。这种模式的优点是隔离性好,缺点是不利于容器间直接通信,尤其是跨多个容器时,每个容器都要暴露一堆映射端口,既不安全也不优雅。
--network=host模式让容器直接共享宿主机网络栈,不再有独立的网络命名空间。容器监听端口时用的就是宿主机IP和端口。好处是性能好、无需端口映射;坏处是隔离性下降,低端口限制也可能依旧存在。考试题目会让你判断“哪个网络模式能让容器直接访问宿主机的回环地址”,那基本就是host模式。
更多容器协同的场景,建议创建自定义网络:
podman network create appnet podman run -d --name db --network appnet mariadb podman run -d --name web --network appnet -p 8080:80 myweb同一个自定义网络里的容器可以直接通过容器名互相访问,比如web容器只要把数据库连接地址写成db就能连上。自定义网络还有DNS解析功能,这是RH134容器章节进阶考点。熟练掌握后,解“多容器互通”的题会轻松很多。
5.2 日志查看与日志清理
容器日志是排查最主要的抓手。常用命令:
podman logs web podman logs -f web podman logs --tail 50 web podman logs --since 10m web podman logs --timestamps web--since能只查看最近十分钟的日志,--timestamps能显示每一行的精确时间,配合排查系统性问题的时候特别好用。如果容器没写标准输出,logs就看不到东西,这是很多新手疑惑“为什么日志为空”的原因。有些镜像会把日志写到文件而非stdout,这时候就得用exec进去看文件了。
日志量的管理也值得留意。容器长时间运行,日志文件会越来越大。虽然RH134考试不会深入考察日志轮转配置,但至少要知道/var/lib/containers或用户目录下存放容器数据的空间是有限的,日常实验环境里定期清理无用容器和镜像,比单纯清日志更重要,podman system prune -a能一次性清理大部分闲置资源。
5.3 systemd生成与开机自启:RH134高频考点
容器要成为系统服务,最标准的方式就是交给systemd托管。RH134对此的考法包括“为现有容器生成systemd unit”和“配置容器随系统开机自启”。
podman generate systemd --new --name web > ~/.config/systemd/user/web.service systemctl --user daemon-reload systemctl --user enable --now web.service这里有几个关键点:
--new参数很关键。加上它之后,生成的unit会包含标准的podman run命令,也就是说服务启动时会重新创建并启动一个同名新容器。不加--new,unit只会对已有容器做start/stop,一旦容器被意外删除,服务就会失效。考试如果问“如何让容器在宿主机重启后自动拉起并保持配置”,一定要带上--new。- systemd服务默认是root用户执行的,如果当前操作者是普通用户,要么用
systemctl --user启用用户级服务管理,要么把unit文件放到root的/etc/systemd/system下再启用。 - 容器设置
--restart=always和systemd unit的Restart=always是两层机制。前者是Podman自己拉起,后者是宿主机systemd检测到服务退出后拉起。RH134里经常出现“容器退出了但一直没被拉起”的描述,通常就是因为重启策略没有配,或者配在了错误的层级上。
实际干活时,我会先用podman create --restart=always这类命令确认容器能正常工作,再生成systemd unit,最后用systemctl status验证服务状态。考试也一样:生成unit、enable、然后主动kill容器进程看systemd是否自动拉回,这一步能验证你整个链条是否真的通了。
6. 考前冲刺:高频易错点清单和模拟问答
6.1 我见过的最容易丢分的六个操作误区
- 忘记
--name:后续命令全都靠猜容器名,非常浪费时间。养成每次run都带--name的习惯。 -d和-it混用不清晰:-d之后容器在后台,exec进去加-it才能交互,两者并不冲突。rm和rmi混着用:先删容器再删镜像,顺序反了镜像删不掉。-p 80:80在普通用户下失败:高频考题,本质是rootless权限问题,低端口需要系统级转发。- 卷挂载不加
:Z或:z:数据目录访问不了,第一反应不是SELinux而是chmod,方向跑偏。 - systemd生成时漏了
--new:服务看起来正常,但容器被删后起不来,排查半天才发现unit创建逻辑错了。
这些误区在模拟做题时几乎每条都能对应到一个实际报错。把每个误区自己动手复现一遍,印象会深刻很多。
6.2 模拟问答题十连测
下面是我自己备考时整理的十道典型的问答题,覆盖这一章绝大部分考点,答案是浓缩后的要点,先自己列完思路再对照。
问:普通用户使用
podman run -p 80:80失败,原因是什么?
答:rootless容器没有绑定低端口的能力,默认不能监听1024以下端口,改用高位端口后映射,或用系统防火墙做转发。问:
podman run -d --rm nginx,容器被停止后会发生什么?
答:容器退出时会被自动删除,因为--rm指定了退出即删除。问:绑定挂载目录在容器内显示Permission denied,最可能原因是什么?
答:SELinux标签不允许容器访问该目录,使用:Z或:z重新标记卷后验证。问:
podman ps看不到任何容器,但容器明明启动过,为什么?
答:很可能容器主进程已经退出,需要podman ps -a查看包括已退出容器在内的全部容器。问:如何确认一个容器是否使用了host网络?
答:执行podman inspect <容器名>,查看NetworkMode字段是否为host。问:多个容器要在同网络内通过容器名通信,怎么实现?
答:先podman network create自定义网络,然后所有容器都通过--network加入该网络。问:容器删除后,数据为什么可能还在?
答:如果数据写入的是命名卷,卷的生命周期独立于容器。容器删了卷还在,需单独删除卷。问:
podman generate systemd --new和省略--new有什么区别?
答:带--new时unit会重新运行podman run创建容器,省略则只管理现有容器实例。问:从私有registry拉取镜像报认证错误,如何解决?
答:先podman login registry.example.com,确认用户在配置目录里有对应凭证。问:查看容器日志只显示最近几行,参数怎么用?
答:podman logs --tail N <容器名>,N是行数,也可配合--since限定时间范围。
这些题做熟之后,基本可以把第13章的“知识点问答题”部分稳住了。
6.3 一小时上机练习清单
如果时间紧张,我建议把这一章的练习压缩成下面这个一小时流程,覆盖度和效率都足够高。
- 前10分钟:用自己的普通用户身份,搜索并拉取镜像,练习
images、inspect、history。 - 中间20分钟:启动一个web容器并挂载命名卷,验证日志、
exec、修改容器内文件并重启容器验证持久化。 - 后20分钟:创建自定义网络,再启一个数据库容器,与web容器同网通信,测试容器名互相访问。
- 最后10分钟:为web容器生成systemd unit,enable并验证重启后自动拉起,再手动kill进程看systemd恢复。
每一段结束,顺手把跑过的命令记录到笔记里,标注有什么报错、最后怎么解的。这套练习做下来,比单纯刷一百道题都管用。
说实话,第13章在RH134里难度不算高,但它非常考验“机制理解”。我自己的体会是,只要把rootless模型和SELinux这两个坎迈过去了,剩下的就是反复敲命令,熟到系统重启后能闭着眼把容器重新拉起来。考试前如果能做到这个程度,就不必再担心容器题丢分了。