Linux Namespace:容器隔离的底层原理,PID、网络、挂载隔离实战

Linux Namespace:容器隔离的底层原理,PID、网络、挂载隔离实战

当你执行 docker run 启动一个容器时,容器内的进程以为自己是系统上唯一运行的程序,它看到的网络接口是独立的,挂载点是独立的,主机名也是独立的。这一切魔法的背后,是 Linux 内核的 Namespace 机制。本文将深入讲解 Linux Namespace 的原理,并通过实际命令演示每种 Namespace 的效果。


服务器配置

Namespace 实验需要 Linux 内核 3.8+ 以上(现代发行版均满足),建议使用有 root 权限的服务器。推荐雨云服务器 rainyun-com的 2 核 4GB 机型,注册填 2026off 领 5 折,性能充足且支持内核级操作。


Namespace 是什么

Namespace 是 Linux 内核提供的一种资源隔离机制。每个 Namespace 都是系统资源的一个独立视图——进程只能看到属于自己 Namespace 中的资源,对同一系统资源的其他 Namespace 中的内容一无所知。

Linux 目前支持 7 种 Namespace 类型:

Namespace系统调用标志隔离内容引入版本
PIDCLONE_NEWPID进程 ID 空间Linux 3.8
NetworkCLONE_NEWNET网络接口、路由、端口Linux 2.6.24
MountCLONE_NEWNS挂载点Linux 2.4.19
IPCCLONE_NEWIPCSystem V IPC、POSIX 消息队列Linux 2.6.19
UTSCLONE_NEWUTS主机名、NIS 域名Linux 2.6.19
UserCLONE_NEWUSER用户 ID、组 ID 映射Linux 3.8
TimeCLONE_NEWTIME系统时钟偏移Linux 5.6

实验工具安装

apt update
apt install util-linux iproute2 procps -y

主要工具说明:

  • unshare — 以新 Namespace 启动程序
  • nsenter — 进入已有进程的 Namespace
  • lsns — 列出系统中所有 Namespace
  • ip netns — 专门管理 Network Namespace

PID Namespace 实战

PID Namespace 让进程看到一个独立的进程 ID 空间,容器内的进程 1 不是系统真正的 init。

创建 PID 隔离环境

# 创建新 PID Namespace,同时挂载独立的 /proc
unshare --pid --fork --mount-proc /bin/bash

进入后执行:

# 在新 Namespace 中,只能看到自己的进程
ps aux

输出类似:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4624  3720 pts/0    S    10:00   0:00 /bin/bash
root        12  0.0  0.0   7480  3200 pts/0    R+   10:00   0:00 ps aux

当前 bash 进程在 Namespace 内的 PID 是 1,而在宿主机上它有一个完全不同的 PID。退出后在宿主机验证:

# 宿主机可以看到所有进程
ps aux | wc -l

关键点

  • --fork 是必须的:让 unshare 先 fork 子进程再 exec,确保子进程是新 Namespace 中的 PID 1
  • --mount-proc 在新 Namespace 中重新挂载 /proc,否则 ps 仍会读宿主机数据

UTS Namespace 实战

UTS (UNIX Time-sharing System) Namespace 隔离主机名和 NIS 域名,是最简单的 Namespace 类型。

# 创建新 UTS Namespace
unshare --uts /bin/bash

# 在新 Namespace 中修改主机名
hostname container-node-1
hostname
# 输出:container-node-1

# 退出后查看宿主机主机名(不受影响)
exit
hostname
# 输出:your-original-hostname

Docker 正是利用 UTS Namespace 给每个容器设置独立的 hostname(默认使用容器 ID 的前 12 位)。


Network Namespace 实战

Network Namespace 是最复杂也最强大的 Namespace 类型,它隔离网络接口、IP 地址、路由表、iptables 规则、端口空间。

创建独立网络 Namespace

# 创建名为 myns 的 Network Namespace
ip netns add myns

# 列出已有的 Network Namespace
ip netns list

# 在 myns 中执行命令
ip netns exec myns ip link list
# 只能看到 lo 接口,且默认是 DOWN 状态

用 veth pair 连通两个 Namespace

veth (virtual ethernet) 是一对虚拟网卡,数据从一端进必从另一端出,常用于连接两个 Namespace。

# 创建 veth pair:veth0 和 veth1
ip link add veth0 type veth peer name veth1

# 将 veth1 移入 myns
ip link set veth1 netns myns

# 为宿主机端的 veth0 配置 IP
ip addr add 10.0.0.1/24 dev veth0
ip link set veth0 up

# 为 myns 内的 veth1 配置 IP
ip netns exec myns ip addr add 10.0.0.2/24 dev veth1
ip netns exec myns ip link set veth1 up
ip netns exec myns ip link set lo up

# 验证连通性
ping -c 3 10.0.0.2

# 从 myns ping 宿主机
ip netns exec myns ping -c 3 10.0.0.1

清理实验环境

ip netns del myns
ip link del veth0

Mount Namespace 实战

Mount Namespace 让每个容器拥有独立的挂载点树,容器内的挂载操作不会影响宿主机。

# 创建新 Mount Namespace
unshare --mount /bin/bash

# 在新 Namespace 中创建挂载
mkdir -p /tmp/testmount
mount -t tmpfs tmpfs /tmp/testmount
df -h /tmp/testmount
# 可以看到 tmpfs 挂载

# 退出后在宿主机验证
exit
df -h /tmp/testmount
# mount: /tmp/testmount: not mounted  —— 宿主机不受影响

bind mount 隔离

unshare --mount /bin/bash

# bind mount 一个目录
mkdir /tmp/hidden
mount --bind /etc /tmp/hidden
ls /tmp/hidden  # 可以看到 /etc 的内容

exit
ls /tmp/hidden  # 宿主机 /tmp/hidden 是空的

User Namespace 实战

User Namespace 允许在 Namespace 内部"成为 root",但在宿主机上仍是普通用户,是实现无需特权运行容器的关键。

# 以普通用户身份(不需要 sudo)创建 User Namespace
unshare --user --map-root-user /bin/bash

# 在 Namespace 内查看当前用户
id
# uid=0(root) gid=0(root) groups=0(root) —— 看起来是 root

# 但实际上在宿主机看,这只是普通用户
# 在另一个终端查看
ps aux | grep unshare
# 进程所属用户仍然是你的普通用户

查看 UID/GID 映射关系

# 在 User Namespace 内查看映射
cat /proc/self/uid_map
# 0    1000    1
# Namespace内UID 0 对应宿主机UID 1000

使用 lsns 查看所有 Namespace

# 列出系统中所有 Namespace
lsns

# 输出示例
NS TYPE   NPROCS   PID USER  COMMAND
4026531835 cgroup    120     1 root  /sbin/init
4026531836 pid       120     1 root  /sbin/init
4026531837 user      120     1 root  /sbin/init
4026531838 uts       120     1 root  /sbin/init
4026531839 ipc       120     1 root  /sbin/init
4026531840 mnt       118     1 root  /sbin/init
4026531992 net       120     1 root  /sbin/init
4026532198 mnt         1   823 root  /lib/systemd/systemd-udevd

每个 Namespace 都有一个唯一的 inode 编号,可以通过 /proc/<PID>/ns/ 目录查看:

ls -la /proc/self/ns/
# lrwxrwxrwx 1 root root 0 net -> 'net:[4026531992]'
# lrwxrwxrwx 1 root root 0 pid -> 'pid:[4026531836]'
# ...

nsenter:进入容器 Namespace 调试

nsenter 是运维的利器,可以进入正在运行的容器的 Namespace 进行调试,无需通过 docker exec

进入 Docker 容器的 Namespace

# 获取容器 PID
docker inspect --format '{{.State.Pid}}' my-container

# 假设 PID 是 12345
# 进入容器的网络和 PID Namespace
nsenter -t 12345 -n -p -- ip addr

# 进入容器的所有 Namespace(相当于 docker exec -it)
nsenter -t 12345 --mount --uts --ipc --net --pid -- /bin/bash

# 只进入网络 Namespace(宿主机文件系统依然可用)
nsenter -t 12345 -n -- ss -tlnp

nsenter vs docker exec 的区别

  • docker exec 进入容器的所有 Namespace,且受 Docker 安全策略限制
  • nsenter 可以选择性地进入特定 Namespace,且不受 Docker 策略限制,适合深度调试

例如,容器内没有安装 tcpdump,但你想抓包:

# 只进入容器的网络 Namespace,使用宿主机的 tcpdump
nsenter -t 12345 -n -- tcpdump -i eth0 -w /tmp/container.pcap

Docker 与 Namespace 的对应关系

Docker 功能Namespace 类型作用
容器进程隔离PID容器内看不到宿主机进程
容器网络Network独立 IP、独立端口空间
容器文件系统Mount独立的 rootfs
容器主机名UTS独立的 hostname
容器 IPCIPC独立的 SHM/消息队列
普通用户运行(rootless)User非 root 用户运行容器
# 验证:查看一个运行中容器使用的 Namespace
docker run -d --name test nginx
PID=$(docker inspect --format '{{.State.Pid}}' test)
ls -la /proc/$PID/ns/

安全意义

Namespace 提供了视图隔离,但不是完整的安全边界:

  1. Namespace 不限制资源使用(CPU、内存需要 cgroup)
  2. 内核漏洞可能突破 Namespace(需要 seccomp、AppArmor 加固)
  3. PID 1 问题:容器内 PID 1 需要正确处理信号(推荐使用 tini)
  4. User Namespace + 其他 Namespace:非 root 用户可以安全地创建完整隔离环境(Podman rootless 的基础)

总结

Linux Namespace 是理解容器技术的基础。掌握了 Namespace,你就理解了为什么 Docker 里的进程 PID 是 1、为什么容器有独立的 IP、为什么修改容器内的主机名不影响宿主机。更进一步,结合 cgroup(资源限制)和 seccomp(系统调用过滤),就构成了完整的容器隔离体系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值