这是《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 责任:
- 初始化进程;
- 转发信号;
- 回收子进程;
- 处理退出。
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 信号处理和可写层持久化是生产高频问题。
思考题
- PID namespace 为什么会让容器内进程显示为 PID 1?
- cgroup v1 和 v2 的组织方式有什么差异?
cpu.max=200000 100000表示多少 CPU?- memory.max 和 memory.high 有什么区别?
- 为什么容器入口脚本要转发信号?