LinuxNotes

第 19 章:Namespace 与 Cgroup

zjc 于 2026-01-19 发布

这是《Linux 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Namespace 解决“能看到什么”,cgroup 解决“能用多少”。容器、systemd slice 和云原生资源隔离都建立在这两个机制上。理解它们,才能定位容器看不到宿主机进程、CPU 限流、内存被杀和存储视图差异。

19.1 Namespace

常见 namespace:

Namespace 隔离对象
PID 进程编号
NET 网卡、路由、iptables、socket
MNT 挂载点
UTS 主机名
IPC System V IPC、POSIX 消息队列
USER UID / GID 映射
TIME 系统时钟,较新内核支持

查看:

lsns
lsns -t pid
readlink /proc/<pid>/ns/pid
ls -l /proc/<pid>/ns

19.2 unshare 示例

创建新的 UTS namespace:

sudo unshare --uts bash
hostname demo
hostname
exit

创建新的 mount namespace:

sudo unshare --mount bash
mount --bind /tmp /mnt
exit

这些实验说明隔离边界,但生产容器由容器运行时组合 namespace、cgroup、capabilities 和文件系统。

19.3 PID namespace

容器内常见 PID 1 视角:

Host PID 12345
  -> Container PID 1
     -> Container app process

查看:

ps -ef
docker top <container>
sudo nsenter -t <host-pid> -p -m ps -ef

PID 1 责任:

  1. 初始化进程;
  2. 转发信号;
  3. 回收子进程;
  4. 处理退出。

Shell 入口脚本如果不转发信号,容器可能无法优雅停止。

19.4 Network namespace

查看:

sudo ip netns list
sudo ip netns exec web ip addr
sudo ip netns exec web ss -lntp

创建并连接两个 namespace:

sudo ip netns add web
sudo ip netns add db
sudo ip link add veth-web type veth peer name veth-db
sudo ip link set veth-web netns web
sudo ip link set veth-db netns db
sudo ip netns exec web ip addr add 10.200.1.1/24 dev veth-web
sudo ip netns exec db ip addr add 10.200.1.2/24 dev veth-db
sudo ip netns exec web ip link set veth-web up
sudo ip netns exec db ip link set veth-db up

清理:

sudo ip netns del web
sudo ip netns del db

19.5 cgroup 版本

cgroup v1:

/sys/fs/cgroup/cpu
/sys/fs/cgroup/memory

cgroup v2:

/sys/fs/cgroup
  cpu.max
  memory.max
  io.max

查看版本:

stat -fc %T /sys/fs/cgroup
mount | grep cgroup

v2 输出常见为 cgroup2fs,v1 常见为 tmpfs

19.6 CPU cgroup

v1 常用:

cpu.cfs_period_us
cpu.cfs_quota_us
cpu.shares

v2:

cat /sys/fs/cgroup/cpu.max
cat /sys/fs/cgroup/cpu.stat

示例:

200000 100000

表示每 100000 微秒可使用 200000 微秒 CPU,即 2 核。

限流指标:

指标 含义
nr_throttled 被限流次数
throttled_usec 被限流时长
nr_periods 周期数

19.7 Memory cgroup

v2:

cat /sys/fs/cgroup/memory.current
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.stat

限制:

文件 作用
memory.max hard limit
memory.high soft limit,触发回收
memory.swap.max swap 限制
memory.events OOM 等事件

hard limit 达到时可能触发 OOM Killer。页缓存也会计入 cgroup memory current,需要结合 memory.stat 分析。

19.8 IO cgroup

v2:

cat /sys/fs/cgroup/io.stat
cat /sys/fs/cgroup/io.max

io.max 示例:

259:0 rbps=104857600 wbps=max riops=max wiops=1000

设备号可用:

lsblk
cat /proc/partitions

IO 限制效果与存储类型和队列深度有关,需压测验证。

19.9 容器视角

查看进程 cgroup:

cat /proc/<pid>/cgroup

查看容器资源:

docker stats
docker inspect <container> --format '{{json .HostConfig.Resources}}'
kubectl describe pod <pod>

常见差异:

现象 解释
容器内看不到宿主机进程 PID namespace
容器内 CPU 核数不同 cgroup 或 runtime 暴露方式
容器内 hostname 变化 UTS namespace
容器写入重启后消失 可写层未持久化
宿主机内存正常但容器被杀 memory limit

19.10 systemd 与 cgroup

查看服务层级:

systemctl status app
systemd-cgls
systemd-cgtop

资源限制:

[Service]
CPUQuota=200%
MemoryHigh=4G
MemoryMax=6G
TasksMax=1024
IOWeight=100

systemd 会把服务放入 cgroup,方便聚合观察和限制资源。

19.11 常见案例

容器内存 OOMKilled

kubectl describe pod <pod>
cat /sys/fs/cgroup/<path>/memory.events
cat /sys/fs/cgroup/<path>/memory.stat

分析 anon、file、slab 和 workingset。

CPU 延迟高但使用率不满

cat /sys/fs/cgroup/<path>/cpu.stat
kubectl get pod <pod> -o jsonpath='{.spec.containers[0].resources.limits.cpu}'

如果 throttled_usec 增长,说明限流。

容器无法看到挂载盘

docker inspect <container> --format '{{json .Mounts}}' | jq .
kubectl describe pod <pod>

检查 volume、bind mount、subPath 和挂载 namespace。

本章小结

Namespace 提供视图隔离,cgroup 提供资源限制。容器排障要同时看宿主机全局视角和容器内部视角。CPU throttling、memory events、PID 1 信号处理和可写层持久化是生产高频问题。

思考题

  1. PID namespace 为什么会让容器内进程显示为 PID 1?
  2. cgroup v1 和 v2 的组织方式有什么差异?
  3. cpu.max=200000 100000 表示多少 CPU?
  4. memory.max 和 memory.high 有什么区别?
  5. 为什么容器入口脚本要转发信号?