在Fedora上运行容器时,如果不显式声明资源限制,Podman默认不会给容器设置CPU或内存上限。这意味着某个容器内的进程可能会耗尽宿主机的全部内存,触发内核OOM Killer,甚至影响宿主机上的其他服务。Fedora从较新的版本开始使用cgroups v2作为默认控制组层级,Podman也针对cgroups v2做了适配,因此设置资源配额的方式和旧版本有一些差异。

资源配额的基础:cgroups v2与Podman的参数体系
Fedora的容器资源限制依赖内核的cgroups子系统。cgroups v2采用统一的层级结构,不再像v1那样为CPU、内存、I/O分别挂载多个控制器。检查系统是否运行在cgroups v2下,可以查看/sys/fs/cgroup/cgroup.controllers文件,里面列出了当前可用的控制器,比如cpu memory pids。Podman在启动容器时会把对应的cgroup限制写入到systemd创建的scope或slice中,所以理解这一点有助于排查配额不生效的问题。
资源配额主要分为几类:CPU时间、内存使用、进程数量、块设备I/O等。日常使用中最常关注的是CPU和内存。CPU限制又分为绝对值和相对权重两类。绝对值使用--cpus参数,表示容器最多可以使用的CPU核心数,例如--cpus=1.5表示最多使用1.5个核心的计算时间。相对权重用--cpu-shares或者--cpu-weight(cgroups v2下对应)控制,它只在多个容器争抢CPU时起作用。内存限制用--memory,同时还需要注意--memory-swap的配合,否则某些情况下容器仍可能占用大量swap空间。
进程数限制使用--pids-limit,可以防止容器内进程无限fork导致宿主机PID耗尽。这个参数在面向公共平台的容器中尤其重要,因为它能挡住fork炸弹类的简单攻击。I/O限制可以用--blkio-weight等参数,不过目前在cgroups v2下的支持程度不如CPU和内存直观。本文重点放在CPU、内存和进程数三个维度。
用Podman命令直接设置CPU与内存配额
启动一个Fedora容器时,最基本的内存限制写法如下:
podman run -d --name web \ --memory=512m \ --memory-swap=1g \ --cpus=1.0 \ --pids-limit=128 \ nginx:latest
这里--memory=512m表示容器最多使用512MB物理内存。--memory-swap=1g表示物理内存加swap的总上限为1GB,也就是说容器可以使用512MB内存和488MB左右的swap。如果不设置--memory-swap,Podman的默认行为在不同版本中可能不同,但通常建议显式设置,避免只限制物理内存却放任swap使用。要彻底禁止容器使用swap,可以把--memory-swap的值设置成和--memory一样,例如--memory=512m --memory-swap=512m。
CPU限制方面,--cpus=1.0会限制容器最多使用一个CPU核心的算力。如果宿主机有4个核心,容器内的多线程程序即使想跑满也不会超过25%的总CPU。对于需要突发性能的场景,可以结合--cpu-period和--cpu-quota做更细粒度的控制。例如设置--cpu-period=100000 --cpu-quota=200000表示每100毫秒内容器最多获得200毫秒的CPU时间,相当于2个核心。实际上--cpus参数就是这两个值的快捷方式,计算关系是quota除以period等于cpus数量。
进程数限制--pids-limit=128会限制容器内同时存在的进程和线程总数。如果容器内进程数超过这个值,内核会返回Resource temporarily unavailable错误,新进程无法创建。运行数据库或需要大量线程的应用时,要确保这个值设置得足够高,否则服务无法启动。验证当前容器的资源限制可以使用podman inspect命令,在输出中查找HostConfig下的相关字段。
通过systemd单元固化资源配额
直接使用podman run命令虽然方便,但容器重启或由systemd管理时,命令行参数容易丢失。Fedora推荐用systemd单元来管理容器,把资源配额写进单元的[Service]段或[Container]段。如果使用Quadlet(Podman较新版本支持的方式),可以在.container文件中直接声明资源限制,systemd生成对应的服务单元。
下面是一个使用传统systemd服务单元管理容器的例子:
[Unit] Description=Web Container with Resource Limits After=network-online.target Wants=network-online.target [Service] ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid ExecStart=/usr/bin/podman run \ --cidfile=%t/%n-cid \ --pidfile=%t/%n-pid \ --cpus=1.5 \ --memory=768m \ --memory-swap=1g \ --pids-limit=256 \ --name=web \ nginx:latest ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n-cid ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile=%t/%n-cid Restart=on-failure [Install] WantedBy=multi-user.target
在这个单元中,--cpus=1.5、--memory=768m、--memory-swap=1g和--pids-limit=256都固化了下来。systemd启动该服务时会调用Podman,Podman再根据这些参数创建cgroup限制。需要注意的是,systemd单元自己的资源控制指令也可以限制整个服务的资源使用,比如在[Service]段写CPUQuota=150%或MemoryMax=768M。这两套机制叠加时,实际限制会取更严格的哪一个。对容器来说,推荐优先使用Podman参数,因为这样更贴近容器生命周期管理,cgroup层级也更清晰。
如果使用Quadlet,可以创建一个名为web.container的文件放在/etc/containers/systemd/目录下,内容如下:
[Container] Image=nginx:latest ContainerName=web PublishPort=8080:80 Cpus=1.5 Memory=768M MemorySwap=1G PidsLimit=256 [Service] Restart=on-failure [Install] WantedBy=multi-user.target
Quadlet会自动把这个文件转换成systemd服务单元,你只需要执行systemctl daemon-reload然后systemctl start web.service即可。这种方式把容器配置和systemd管理解耦,更符合Fedora的系统集成思路。
验证配额是否真正生效的几种方法
设置完配额后,必须实际验证,否则可能会被表面参数迷惑。首先用podman stats观察运行中容器的资源使用情况。这个命令会实时显示CPU百分比、内存使用量、内存限制值和PIDs数量。如果内存限制生效,MEM LIMIT列会显示你设置的值,比如512MB。如果显示--或者空值,说明限制没有应用到该容器上。
其次可以在容器内部用压力测试工具主动触发限制。例如拉取一个带busybox或stress的镜像,执行podman run --rm --memory=128m alpine sh -c 'tail /dev/zero',观察容器是否在达到128MB后被杀死。或者用stress-ng镜像测试CPU和进程数限制。注意不要把压力测试跑在重要的生产容器里。
还可以直接查看cgroup文件确认。使用podman inspect --format '{{.State.CgroupPath}}' web拿到cgroup路径,然后在/sys/fs/cgroup/对应的子目录下查看memory.max、cpu.max和pids.max文件。例如cat /sys/fs/cgroup/.../memory.max应该输出你设置的内存字节数。cgroups v2下这些文件的位置和v1不同,所以排查时不要沿用旧路径。理解了这一层,后续遇到容器资源限制不生效的问题,就能快速定位是参数没传对还是cgroup层级没匹配上。
Fedora容器资源配额cgroups v2修改时间:2026-10-03 14:53:40