在Linux 系统中,namespace是在内核级别以一种抽象的形式来封装系统资源的,通过将系统资 源放在不同的namespace中,来实现资源隔离的目的。设置了不同namespace的程序,就可以 享有彼此独立的一份系统资源。 Linux 中当前可用的命名空间如下:

当前系统使用到的命名空间有,cgroup ns:隔离CGroup视图。ipc ns: 隔离进程间通信资源。 mnt ns: 隔离文件系统挂载点。net ns: 隔离网络资源。pid ns: 隔离进程 ID。user ns: 隔离用户 和用户组ID。uts ns: 隔离主机名和域名。pid_for_children: 当前进程的子进程将继承的PID ns
下面通过从非管理账户user1的角度来具体理解命名空间的工作原理。
手工创建一个资源隔离的类似容器对象
(1)首先配置user1用户sudo免密,并将user1添加到docker组
配置/etc/sudoers文件
user1 ALL=(ALL) NOPASSWD:ALL
用visudo -f /etc/sudoers.d/user1
用 visudo 安全编辑器,打开 /etc/sudoers.d 目录下叫 user2 的文件,用来给 user2 用户配置 sudo 权限。
(2)运行一个conns容器

docker run = 创建并启动一个容器 --name conns = 给容器起名字:conns --rm = 容器停止后**自动删除**(干净) -i = 交互式(保持标准输入打开) -d = 后台运行(守护进程) alpine = 使用最小 Linux 镜像(只有几MB,带 rootfs) ash = 容器启动后运行的终端(轻量shell,类似bash)
(3)容器中包含一个精简完整的文件系统
(4)将conns容器的文件系统导出

(5)使用chroot命令把当前shell切换到刚创建的文件系统
sudo chroot $PWD/rootfs ash

新的文件系统好像是独立的root文件系统,但是看ip link命令显示的网络信息,但是网络信息和系统进程信息,可以发现,除了挂载点之外的网络、进程等资源在"模拟容器"中都能看到,这些资源没有隔离。


(6)回到user1,使用unshare命令隔离出一组新的命名空间的集合,unshare命令用于创建一个 新ns集合,并在其中启动一个Bash shell,实现资源隔离,这是Linux容器技术的基础之一。
词逐参数详细解释
unshare
英文意思:不共享 作用:创建一个 "独立隔离空间" (Linux 叫 Namespace 命名空间)
你可以把它理解成:
我要开一个独立房间,不和外面共享任何东西!
每个开关到底做什么?(超级关键)
① --mount
创建独立挂载空间
-
你在里面挂载 overlay、分区、目录
-
外面完全看不见、不受影响
-
这就是你之前
overlay挂载失败的原因!必须开这个才能挂载!
② --pid
创建独立进程空间
-
里面
ps只能看到自己的进程 -
看不到宿主机进程
-
容器 PID 从 1 开始
③ --net
创建独立网络空间
-
里面有独立的网卡、IP
-
默认只有 lo 本地回环
-
和外面网络完全隔离
④ --uts
独立主机名、域名
-
你可以随便改 hostname
-
外面不受影响
⑤ --ipc
独立进程通信
-
隔离信号量、共享内存
-
防止容器间互相干扰
⑥ --user
独立用户空间
-
里面的 UID、GID 独立
-
可以映射成 root,更安全
⑦ --map-root-user
最神奇的参数!
-
在隔离环境里,你是 root
-
在宿主机上,你只是普通用户
-
权限安全隔离,不会搞坏系统
⑧ --fork
-
新建一个子进程来运行隔离环境
-
保证 PID 命名空间正常工作
最后 /bin/bash
进入这个隔离环境后,运行 bash 终端
执行这条命令后,你得到了什么?
你得到了一个:
✅ 独立 root 权限(安全映射)✅ 独立进程✅ 独立网络✅ 独立挂载✅ 独立主机名✅ 独立 IPC✅ 完全隔离、不会影响宿主机
(7)echo $$
输出当前 shell 进程的 PID(进程 ID)。
-
echo:在标准输出上打印其参数。 -
$$:是一个特殊的 shell 变量,它扩展为当前 shell 的进程 ID(PID)。


(8)

(9)如果此时我们在其他终端上检查主机中的进程时,会看到,即使上面的用户root, 在进程之外,也是在普通用户user1的凭据下执行的

这就是PID命名空间的神奇之处,它使得一个进程具有不同的PID号,例如K8s pod场景
PID 命名空间的 "魔术"
当你加上 --pid 参数后:
-
unshare会创建一个独立的 PID 命名空间 -
它
fork()出来的新bash进程,在宿主机上的 PID 是3859 -
但在这个新的 PID 命名空间里,它看到的自己的 PID 是 1(容器里的 PID 1)
也就是说:
-
宿主机视角:进程 PID = 3859
-
容器视角:同一个进程,PID = 1
这就是 PID 命名空间的隔离效果。
(10)回到"模拟容器"环境,如果想要显示当前正在运行的进程,结果先显示的是主机中的所有进程
挂载 /proc 是 PID 命名空间隔离的 "最后一步":只有让容器里的 /proc 指向自己的进程列表,ps 这类工具才能真正 "看到隔离后的世界"。
挂载 /proc 前:

- 此时
ps读取的是宿主机全局的 /proc,所以能看到所有进程 - 你的 PID 命名空间虽然已经创建,但
ps工具不知道要读 "自己的 /proc
挂载 /proc 后:mount -t proc proc /proc


- 此时
ps读取的是新 PID 命名空间对应的 /proc - 只能看到自己命名空间里的进程
- 你的
bash进程,在这个命名空间里的 PID 变成了 1(容器里的 "init 进程")
(11)我们在容器外部创建一个网络设备并将其放入命名空间:


回到"模拟容器"环境,会看到有一个新的网络设备出现了

核心目标
你这段操作,就是给 unshare 创建的 "模拟容器",配置一个独立的虚拟网卡,让它和宿主机可以通信。
这也是 Docker/K8s 容器网络的底层原型!
二、命令拆解(从宿主机操作)
- 创建一对虚拟网卡(veth pair)
bash
运行
sudo ip link add vethLocal type veth peer name vethContainer
-
这会创建一对 "网线两端" 的虚拟网卡:
-
vethLocal:留在宿主机这边 -
vethContainer:准备放到容器里
-
-
它们天生是连通的,宿主机和容器可以通过这对网卡互相通信
- 把网卡都 up 起来
bash
运行
sudo ip link set vethLocal up
sudo ip link set vethContainer up
- 让这对虚拟网卡从
DOWN变成UP状态,准备好工作
- 找到容器进程的 PID
bash
运行
ps -ef | grep '/bin/bash'
# 得到容器内 bash 进程的 PID:3591
-
这个
3591就是你unshare启动的隔离环境里的 bash 进程 PID -
它同时也属于新创建的网络命名空间
- 把容器端的网卡,移动到容器的网络命名空间里
bash
运行
sudo ip link set vethContainer netns 3591
-
关键操作:把
vethContainer这一端,"塞" 进 PID 3591 进程所属的网络命名空间里 -
宿主机这边就看不到
vethContainer了,只有容器里能看到它
问题1:/proc # 挂载点:把它挂到系统的 /proc 目录上 挂载到了系统的/proc为什么 ps -ef 不是系统的进程
unshare 开了独立命名空间,从宿主机全局 /proc 里,隔离拆分出专属这个容器自己的 /proc
-
先分清两个关键
-
你开了:
unshare --mount👉 拥有独立挂载命名空间 👉 你里面的所有mount,只在你这个隔离环境生效,不影响外面宿主机 -
mount -t proc proc /proc-
语法里最后
/proc:是当前隔离环境内 的根下/proc -
不是直接改写宿主机真实全局
/proc
-
-
为什么挂载同名目录,进程不一样
-
没挂载前:你环境里的
/proc继承自宿主机,里面是全系统所有进程 ,ps能看到宿主机所有进程 -
执行挂载后:在你自己独立挂载视图 里,把当前目录下的
/proc,重新挂载了一份绑定当前 PID 命名空间 的procfs这份新proc只显示本 PID 命名空间内的进程,和宿主机进程列表切割开
- 最通俗比喻
整台电脑 = 一栋大楼
-
宿主机
/proc= 大楼总监控室,看所有人 -
你
unshare进去 = 单独一间独立房间 -
房间里也有一个牌子叫
/proc你在房间里重新装了只看本房间人员的小监控 虽然门牌都叫/proc,但数据源完全不一样
- 一句话直击本质
路径名都是/proc,但挂载来源 + 绑定的命名空间不同
-
旧挂载:关联全局 PID 空间 → 看全系统进程
-
新挂载:关联新建独立 PID 空间 → 只看容器内进程