Add more content

This commit is contained in:
Baohua Yang
2026-01-30 16:48:39 -08:00
parent c58f61dbed
commit fec2e506d9
39 changed files with 8202 additions and 1063 deletions

View File

@@ -1,23 +1,267 @@
# 命名空间
命名空间是 Linux 内核一个强大的特性每个容器都有自己单独的命名空间运行在其中的应用都像是在独立的操作系统中运行一样命名空间保证了容器之间彼此互不影响
## 什么是 Namespace
## pid 命名空间
不同用户的进程就是通过 pid 命名空间隔离开的且不同命名空间中可以有相同 pid所有的 LXC 进程在 Docker 中的父进程为 Docker 进程每个 LXC 进程具有不同的命名空间同时由于允许嵌套因此可以很方便的实现嵌套的 Docker 容器
> **Namespace Linux 内核提供的资源隔离机制它让容器内的进程仿佛运行在独立的操作系统中**
## net 命名空间
有了 pid 命名空间每个命名空间中的 pid 能够相互隔离但是网络端口还是共享 host 的端口网络隔离是通过 net 命名空间实现的 每个 net 命名空间有独立的 网络设备IP 地址路由表/proc/net 目录这样每个容器的网络就能隔离开来Docker 默认采用 veth 的方式将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起
Namespace 是容器技术的核心基础之一它回答了一个关键问题**如何让一个进程"以为"自己独占整个系统**
## ipc 命名空间
容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC) 包括信号量消息队列和共享内存等然而同 VM 不同的是容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互因此需要在 IPC 资源申请时加入命名空间信息每个 IPC 资源有一个唯一的 32 id
```
宿主机视角: 容器内视角:
┌─────────────────────────┐ ┌─────────────────────────┐
│ PID 1: systemd │ │ PID 1: nginx │ ← 容器认为自己是 PID 1
│ PID 2: sshd │ │ PID 2: nginx worker │
│ PID 3: dockerd │ │ │
│ PID 1234: nginx ←──────│─────│ (实际是宿主机的 1234
│ PID 1235: nginx worker │ │ │
└─────────────────────────┘ └─────────────────────────┘
```
## mnt 命名空间
类似 chroot将一个进程放到一个特定的目录执行mnt 命名空间允许不同命名空间的进程看到的文件结构不同这样每个命名空间 中的进程所看到的文件目录就被隔离开了 chroot 不同每个命名空间中的容器在 /proc/mounts 的信息只包含所在命名空间的 mount point
## Namespace 的类型
## uts 命名空间
UTS("UNIX Time-sharing System") 命名空间允许每个容器拥有独立的 hostname domain name 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程
Linux 内核提供了以下几种 NamespaceDocker 容器使用了全部
## user 命名空间
每个容器可以有不同的用户和组 id 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户
| Namespace | 隔离内容 | 容器中的效果 |
|-----------|---------|-------------|
| **PID** | 进程 ID | 容器内 PID 1 开始看不到其他容器和宿主机进程 |
| **NET** | 网络栈 | 独立的网卡IP 地址端口路由表 |
| **MNT** | 挂载点 | 独立的文件系统视图自己的根目录 |
| **UTS** | 主机名 | 独立的主机名和域名 |
| **IPC** | 进程间通信 | 独立的信号量消息队列共享内存 |
| **USER** | 用户/ ID | 容器内的 root 可以映射为宿主机的普通用户 |
| **Cgroup** | Cgroup 根目录 | 隔离 cgroup 层级视图Linux 4.6+ |
*更多关于 Linux 上命名空间的信息请阅读 [这篇文章](https://blog.scottlowe.org/2013/09/04/introducing-linux-network-namespaces/)。
---
## PID Namespace
### 作用
隔离进程 ID让每个容器有自己的进程编号空间
### 效果
```bash
# 宿主机上查看进程
$ ps aux | grep nginx
root 12345 0.0 0.1 nginx: master process
root 12346 0.0 0.1 nginx: worker process
# 容器内查看进程
$ docker exec mycontainer ps aux
PID USER COMMAND
1 root nginx: master process ← 在容器内是 PID 1
2 root nginx: worker process
```
### 关键点
- 容器内的 PID 1 进程特殊重要它是容器的主进程退出则容器停止
- 容器内无法看到宿主机或其他容器的进程
- 宿主机可以看到所有容器内的进程 PID 不同
---
## NET Namespace
### 作用
隔离网络栈每个容器拥有独立的网络环境
### 效果
```
宿主机 容器
┌─────────────────────┐ ┌─────────────────────┐
│ eth0: 192.168.1.10 │ │ eth0: 172.17.0.2 │ ← 不同的 IP
│ docker0: 172.17.0.1│◄───────►│ (veth pair 连接) │
│ 端口 80 可用 │ │ 端口 80 可用 │ ← 可以使用相同端口
└─────────────────────┘ └─────────────────────┘
```
### 关键点
- 每个容器有独立的网卡IP路由表iptables 规则
- 多个容器可以监听相同端口如都监听 80
- Docker 使用 veth pair 连接容器网络和宿主机网桥
---
## MNT Namespace
### 作用
隔离文件系统挂载点每个容器有自己的根目录
### 效果
```
宿主机文件系统: 容器内看到的:
/ / ← 容器的根目录
├── bin/ ├── bin/
├── home/ ├── home/
├── var/ ├── var/
│ └── lib/ │ └── lib/
│ └── docker/ │
│ └── overlay2/ │
│ └── merged/ ────┼─── 这个目录成为容器的 /
└── ... └── ...
```
### chroot 的区别
| 特性 | chroot | MNT Namespace |
|------|--------|---------------|
| 安全性 | 可以逃逸 | 更安全 |
| 挂载隔离 | | 完全隔离 |
| /proc/mounts | 共享 | 独立 |
---
## UTS Namespace
### 作用
隔离主机名和域名让每个容器可以有自己的主机名
### 效果
```bash
# 宿主机
$ hostname
my-server
# 容器内
$ docker run --hostname mycontainer ubuntu hostname
mycontainer
```
UTS = "UNIX Time-sharing System"是历史遗留的名称
---
## IPC Namespace
### 作用
隔离 System V IPC POSIX 消息队列
### 隔离的资源
- 信号量semaphores
- 消息队列message queues
- 共享内存shared memory
### 关键点
- 同一容器内的进程可以通过 IPC 通信
- 不同容器的进程无法通过 IPC 通信除非显式共享
---
## USER Namespace
### 作用
隔离用户和组 ID实现权限隔离
### 效果
```
容器内 宿主机
┌─────────────────┐ ┌─────────────────┐
│ UID 0 (root) │───映射────►│ UID 100000 │ ← 非特权用户
│ UID 1 (daemon) │───映射────►│ UID 100001 │
└─────────────────┘ └─────────────────┘
```
### 安全意义
容器内的 root 用户可以映射为宿主机上的普通用户即使容器被突破攻击者在宿主机上也只有普通权限
> 💡 笔者建议生产环境建议启用 User Namespace增强安全性
---
## 动手实验体验 Namespace
使用 `unshare` 命令可以在不使用 Docker 的情况下体验 Namespace
### 实验 1UTS Namespace
```bash
# 创建新的 UTS namespace 并启动 shell
$ sudo unshare --uts /bin/bash
# 修改主机名(只影响这个 namespace
$ hostname container-test
$ hostname
container-test
# 退出后查看宿主机主机名(未改变)
$ exit
$ hostname
my-server
```
### 实验 2PID Namespace
```bash
# 创建新的 PID 和 MNT namespace
$ sudo unshare --pid --mount --fork /bin/bash
# 挂载新的 /proc
$ mount -t proc proc /proc
# 查看进程(只能看到当前 shell
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 8960 4516 pts/0 S 10:00 0:00 /bin/bash
root 8 0.0 0.0 10072 3200 pts/0 R+ 10:00 0:00 ps aux
```
### 实验 3NET Namespace
```bash
# 创建新的网络 namespace
$ sudo unshare --net /bin/bash
# 查看网络接口(只有 lo
$ ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```
---
## Namespace 的局限性
Namespace 提供了隔离但不是安全边界
| 方面 | 说明 |
|------|------|
| **共享内核** | 所有容器共享宿主机内核内核漏洞可能影响所有容器 |
| **部分资源未隔离** | /proc/sys 部分内容仍可见时间无法隔离 |
| **非虚拟化** | 比虚拟机隔离性弱 |
> 需要更强隔离时可考虑 gVisorKata Containers 等安全容器方案
---
## 本章小结
| Namespace | 隔离内容 | 一句话说明 |
|-----------|---------|-----------|
| PID | 进程 ID | 容器有自己的进程树 |
| NET | 网络 | 容器有自己的 IP 和端口 |
| MNT | 文件系统 | 容器有自己的根目录 |
| UTS | 主机名 | 容器有自己的 hostname |
| IPC | 进程间通信 | 容器间 IPC 隔离 |
| USER | 用户 ID | 容器 root 宿主机 root |
## 延伸阅读
- [控制组Cgroups](cgroups.md)资源限制机制
- [联合文件系统](ufs.md)分层存储的实现
- [安全](../security/README.md)容器安全实践
- [Linux Namespace 官方文档](https://man7.org/linux/man-pages/man7/namespaces.7.html)