07. 命名空间(Namespace)
1. 命名空间概述
命名空间(Namespace)是 Linux 内核实现容器隔离的核心技术。它让每个容器拥有独立的资源视图,容器内的进程看不到宿主机和其他容器的资源。
┌─────────────────────────────────────────────────────────────┐ │ Linux 命名空间类型 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 宿主机内核空间 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────┼───────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ 容器 A │ │ 容器 B │ │ 容器 C │ │ │ │ │ │ │ │ │ │ │ │ PID: 1 │ │ PID: 1 │ │ PID: 1 │ │ │ │ NET: 独立 │ │ NET: 独立 │ │ NET: 独立 │ │ │ │ MNT: 独立 │ │ MNT: 独立 │ │ MNT: 独立 │ │ │ │ UTS: 独立 │ │ UTS: 独立 │ │ UTS: 独立 │ │ │ │ IPC: 独立 │ │ IPC: 独立 │ │ IPC: 独立 │ │ │ │ USER: 独立│ │ USER: 独立│ │ USER: 独立│ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘2. 六种命名空间
| 命名空间 | 功能 | 隔离内容 |
|---|---|---|
| PID | 进程隔离 | 进程 ID 空间 |
| NET | 网络隔离 | 网络设备、IP、端口 |
| MNT | 文件系统隔离 | 挂载点、文件系统视图 |
| UTS | 主机名隔离 | 主机名、域名 |
| IPC | 进程间通信隔离 | 信号量、消息队列 |
| USER | 用户隔离 | 用户和组 ID |
3. PID 命名空间
3.1 原理
PID 命名空间让容器内的进程拥有独立的 PID 编号,每个容器都从 1 开始。
┌─────────────────────────────────────────────────────────────┐ │ PID 命名空间隔离 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 宿主机 PID 空间 容器 PID 空间 │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ PID: 1 systemd│ │ PID: 1 nginx │ │ │ │ PID: 2 kthread│ │ PID: 2 bash │ │ │ │ PID: 100 dockerd│ │ PID: 3 sh │ │ │ │ PID: 150 nginx │◄────────┐│ │ │ │ │ PID: 151 bash │ ││ │ │ │ └─────────────────┘ │└─────────────────┘ │ │ │ │ │ └─ 映射关系 │ │ 宿主机 PID 150 ←→ 容器 PID 1 │ │ │ └─────────────────────────────────────────────────────────────┘3.2 演示与实践
# 查看当前进程的命名空间ls-la/proc/$$/ns/# lrwxrwxrwx 1 user user 0 ... ipc -> ipc:[4026531839]# lrwxrwxrwx 1 user user 0 ... mnt -> mnt:[4026531840]# lrwxrwxrwx 1 user user 0 ... net -> net:[4026531992]# lrwxrwxrwx 1 user user 0 ... pid -> pid:[4026531836]# lrwxrwxrwx 1 user user 0 ... user -> user:[4026531837]# lrwxrwxrwx 1 user user 0 ... uts -> uts:[4026531838]# 启动容器并查看其命名空间CONTAINER_PID=$(dockerinspect-f'{{.State.Pid}}'container_name)ls-la/proc/$CONTAINER_PID/ns/# 验证 PID 隔离dockerrun-it--rmalpinesh# 容器内执行/# ps aux# PID USER TIME COMMAND# 1 root 0:00 sh# 7 root 0:00 ps aux# 宿主机上查看对应进程psaux|grep-E"sh|ps"# 在容器内创建新命名空间# unshare 命令创建隔离环境sudounshare--fork--pid--mount-procbashpsaux# 现在 PID 从 1 开始4. NET 命名空间
4.1 原理
NET 命名空间提供独立的网络栈,包括网络设备、IP 地址、端口、路由表等。
┌─────────────────────────────────────────────────────────────┐ │ NET 命名空间隔离 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 宿主机网络 容器网络 │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ eth0: 192.168.1.10 │ eth0: 172.17.0.2│ │ │ │ lo: 127.0.0.1 │ │ lo: 127.0.0.1 │ │ │ │ docker0: 172.17.0.1 │ │ │ │ │ iptables 规则 │◄────────│ iptables 规则 │ │ │ │ 路由表 │ veth │ 路由表 │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘4.2 演示与实践
# 查看网络命名空间ipnetns list# 创建网络命名空间sudoipnetnsaddtest-nssudoipnetns list# 进入网络命名空间sudoipnetnsexectest-nsbash# 在命名空间内查看网络ipaddr# 1: lo: <LOOPBACK> ...# 只有 lo 接口,没有 eth0# 创建 veth pair 连接命名空间sudoiplinkaddveth0typeveth peer name veth1sudoiplinksetveth1 netns test-ns# 分配 IPsudoipaddradd10.0.0.1/24 dev veth0sudoipnetnsexectest-nsipaddradd10.0.0.2/24 dev veth1# 启动接口sudoiplinksetveth0 upsudoipnetnsexectest-nsiplinksetveth1 up# 测试连通性ping10.0.0.2# Docker 容器网络命名空间dockerrun-d--nameweb nginxCONTAINER_PID=$(dockerinspect-f'{{.State.Pid}}'web)# 进入容器的网络命名空间sudonsenter-t$CONTAINER_PID-nipaddr# 清理sudoipnetns delete test-nsdockerrm-fweb5. MNT 命名空间
5.1 原理
MNT 命名空间提供独立的文件系统挂载点视图,每个容器有独立的根文件系统。
┌─────────────────────────────────────────────────────────────┐ │ MNT 命名空间隔离 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 宿主机挂载点 容器挂载点 │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ / │ │ / (alpine root) │ │ │ │ ├─ bin │ │ ├─ bin │ │ │ │ ├─ etc │ │ ├─ etc │ │ │ │ ├─ home │ │ ├─ home │ │ │ │ ├─ var │ │ └─ var │ │ │ │ └─ docker │ │ │ │ │ │ │ │ 额外挂载: │ │ │ │ /data (bind) │◄────────│ /data (bind) │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘5.2 演示与实践
# 查看挂载命名空间ls-la/proc/$$/ns/mnt# 创建新的 MNT 命名空间sudounshare--mountbash# 在新命名空间中挂载exportPS1="ns-mnt$ "mount-ttmpfs tmpfs /tmpmount|greptmpfs# 退出后,挂载消失exit# Docker 容器文件系统dockerrun-it--rmalpinesh/# mount | head -5# overlay on / type overlay (rw,relatime)# proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)# 查看容器的 rootfsCONTAINER_ID=$(dockercreate alpine)dockerexport$CONTAINER_ID|tar-tv|head-20dockerrm$CONTAINER_ID6. UTS 命名空间
6.1 原理
UTS(UNIX Time-sharing System)命名空间隔离主机名和域名。
# 查看 UTS 命名空间ls-la/proc/$$/ns/uts# 创建新的 UTS 命名空间sudounshare--utsbash# 修改主机名exportPS1="ns-uts$ "hostnamecontainer-hosthostname# container-host# 新开终端查看宿主机主机名(未变化)hostname# Docker 容器演示dockerrun-it--rm--hostnamemycontainer alpinesh/# hostname# mycontainer/# cat /etc/hostname# mycontainer# 自定义主机名dockerrun-it--rm--hostnameweb-server alpinehostname7. IPC 命名空间
7.1 原理
IPC 命名空间隔离进程间通信资源,如信号量、消息队列、共享内存。
# 查看 IPC 命名空间ls-la/proc/$$/ns/ipc# 创建消息队列(宿主机)ipcmk-Qipcs-q# 创建新的 IPC 命名空间sudounshare--ipcbash# 在新命名空间中查看消息队列ipcs-q# 看不到宿主机创建的消息队列# Docker 演示dockerrun-it--rmalpinesh/# ipcmk -Q# 容器内创建的消息队列/# ipcs -q# 不同容器之间的 IPC 隔离dockerrun-d--nameipc1 alpinesleep3600dockerrun-d--nameipc2 alpinesleep3600dockerexecipc1 ipcmk-Qdockerexecipc2 ipcs-q# 看不到 ipc1 创建的消息队列# 共享 IPC 命名空间dockerrun-it--rm--ipccontainer:ipc1 alpine ipcs-q# 可以看到8. USER 命名空间
8.1 原理
USER 命名空间隔离用户 ID 和组 ID,实现非特权用户运行容器。
┌─────────────────────────────────────────────────────────────┐ │ USER 命名空间映射 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 容器内 UID 宿主机 UID │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ UID 0 (root) │ ────────▶│ UID 100000 │ │ │ │ UID 1 (daemon) │ ────────▶│ UID 100001 │ │ │ │ UID 1000 (user) │ ────────▶│ UID 101000 │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ │ 容器内 root 权限被映射为宿主机普通用户权限 │ │ 提升安全性:即使容器被攻破,也无法获取宿主机 root │ │ │ └─────────────────────────────────────────────────────────────┘8.2 演示与实践
# 查看 USER 命名空间ls-la/proc/$$/ns/user# 启用 user namespace(需要配置)# /etc/docker/daemon.json{"userns-remap":"default"}# 重新加载配置sudosystemctl restartdocker# 查看映射cat/proc/self/uid_mapcat/proc/self/setgroups# 容器中查看用户dockerrun-it--rmalpineid# uid=0(root) gid=0(root) groups=0(root)# 宿主机查看对应进程的用户# 如果启用了 userns-remap,容器内 root 对应宿主机普通用户# 手动创建 user namespacesudounshare--user--map-root-userbashid# uid=0(root) gid=0(root)# 查看实际映射cat/proc/self/uid_map# 0 1000 19. 命名空间操作工具
9.1 lsns - 列出命名空间
# 列出所有命名空间lsns# 列出特定类型lsns-tnet lsns-tpid lsns-tmnt# 查看详细信息lsns-oNS,TYPE,NPROCS,PID,COMMAND# 查看特定进程的命名空间lsns-p12349.2 nsenter - 进入命名空间
# 进入容器的网络命名空间dockerinspect-f'{{.State.Pid}}'container_namesudonsenter-t<PID>-nipaddr# 进入容器的所有命名空间sudonsenter-t<PID>-abash# 只进入特定命名空间sudonsenter-t<PID>-n-mbash# 网络 + 挂载9.3 unshare - 创建命名空间
# 创建新的 PID 命名空间sudounshare--fork--pid--mount-procbash# 创建新的网络命名空间sudounshare--netbash# 创建新的 UTS 命名空间并修改主机名sudounshare--utsbashhostnamenewhost# 创建新的所有命名空间sudounshare--user--ipc--pid--net--uts--mount--forkbash10. Docker 中的命名空间
# 查看 Docker 容器的命名空间CONTAINER_PID=$(dockerinspect-f'{{.State.Pid}}'container_name)# 查看命名空间ls-la/proc/$CONTAINER_PID/ns/# 命名空间 ID 格式:类型:[inode号]# ipc:[4026532540]# mnt:[4026532538]# net:[4026532543]# pid:[4026532541]# user:[4026531837]# uts:[4026532539]# 两个容器共享网络命名空间dockerrun-d--namecontainer1 nginxdockerrun-d--namecontainer2--networkcontainer:container1 nginxdockerexeccontainer2ipaddr# 和 container1 相同的网络配置11. 总结对比
| 命名空间 | 隔离内容 | 容器中使用 | 核心命令示例 |
|---|---|---|---|
| PID | 进程 ID | 必须 | unshare --pid |
| NET | 网络栈 | 必须 | unshare --net |
| MNT | 文件系统 | 必须 | unshare --mount |
| UTS | 主机名 | 必须 | unshare --uts |
| IPC | 进程通信 | 必须 | unshare --ipc |
| USER | 用户 ID | 可选 | unshare --user |
12. 常见问题
Q1: 如何判断两个进程在同一命名空间?
# 比较命名空间 IDls-la/proc/PID1/ns/pidls-la/proc/PID2/ns/pid# 如果 inode 号相同,则在同一命名空间Q2: 如何让容器共享宿主机网络?
dockerrun--networkhostnginxQ3: 如何让容器共享其他容器的命名空间?
# 共享网络dockerrun--networkcontainer:other_container# 共享 IPCdockerrun--ipccontainer:other_container# 共享 PIDdockerrun--pidcontainer:other_container13. 小结
- 命名空间是容器隔离的基础
- Linux 提供 6 种命名空间实现资源隔离
- PID:进程隔离,容器内 PID 从 1 开始
- NET:网络隔离,独立网络栈
- MNT:文件系统隔离,独立根文件系统
- UTS:主机名隔离
- IPC:进程间通信隔离
- USER:用户 ID 隔离,提升安全性
- 使用
lsns,nsenter,unshare工具操作命名空间