容器技术建立在 Linux 内核的进程、文件系统和网络机制之上——本篇建立这三块前置认知
为什么需要这篇
后续的 Namespace、Cgroups、OverlayFS 都是 Linux 内核特性。如果你不清楚"进程是怎么创建的""/proc 是什么""网络接口为什么能配 IP",后续章节会频繁出现认知断层。本篇目标是用最短篇幅补齐这些前置概念,不求精通,但求理解"是什么、为什么"。
Linux 中一切工作都由进程完成。你在终端敲的每条命令、运行的每个程序、容器里的每个应用,都是一个进程。理解进程的创建方式是理解容器的第一步。
Linux 创建新进程只有一种途径:fork(分裂)。
| 操作 | 含义 | 类比 |
|---|---|---|
fork() |
当前进程复制一份自己,产生一个子进程(内容完全相同) | 细胞分裂——分裂后两个细胞一模一样 |
exec() |
用一个新程序替换当前进程的代码和数据 | 换魂——壳还是那个壳,里面的灵魂换了 |
典型流程(你在 shell 里输入 ls 时发生的事):
bash 进程(PID 100)
│
├─ fork() → 子进程(PID 101),内容和 bash 完全一样
│
└─ 子进程调用 exec("ls") → PID 101 变成了 ls 程序
→ ls 执行完毕,退出
→ bash 继续等待下一条命令
为什么不直接"创建"一个全新进程?因为 Linux 的设计哲学是复制比从零构建更简单——先 fork 出一份副本,再按需替换内容(exec)。这个"先复制再替换"的模式贯穿整个系统。
PID(Process ID)是内核分配给每个进程的唯一编号:
init(或 systemd),系统启动后的第一个用户态进程,是所有进程的祖先# 查看当前 shell 的 PID
echo $$
# 查看进程的父进程
ps -o pid,ppid,comm -p $$
由于每个进程都由 fork 产生,所有进程形成一棵树:
# 查看进程树
pstree -p
# 输出示例(精简):
# systemd(1)─┬─sshd(800)───sshd(1200)───bash(1201)───pstree(1234)
# ├─dockerd(900)───containerd(901)
# └─nginx(1000)─┬─nginx(1001)
# └─nginx(1002)
与容器的关系
容器里的进程也在这棵树上——它只是一个普通的子进程。但通过 PID Namespace(第 02 篇),容器内的进程"以为"自己的 PID 是 1,看不到树的其他部分。隔离的是视图,不是进程本身。
在讨论容器技术之前,必须先把 Linux 文件系统的基本概念理清。很多人对"文件系统"的理解停留在"文件夹和文件"的层面——这在 Windows 的日常使用中够用,但在 Linux 里不够。下面我们从最核心的一条设计哲学讲起,逐层建立认知。
Linux 有一条贯穿整个系统的设计理念:一切皆文件(Everything is a file)。这句话不是比喻,而是字面意义上的事实。
在 Linux 中,"文件"不是一个狭义概念(只有 Word 文档、图片才叫文件)。文件是一个抽象接口——只要一个东西支持以下四个基本操作,它在 Linux 眼里就是"文件":
| 系统调用 | 含义 | 类比 |
|---|---|---|
open() | 打开一个文件,获得文件描述符(file descriptor) | 拿起电话听筒 |
read() | 从文件中读取数据 | 听对方说话 |
write() | 向文件中写入数据 | 对着话筒说话 |
close() | 关闭文件,释放文件描述符 | 挂断电话 |
关键是:读一个 .txt 文件、读键盘输入、读网络数据、读进程信息,用的都是同一套 open → read → write → close 系统调用。程序员不需要为"不同类型的 IO"写完全不同的代码,这是巨大的简化。
| 类型 | 例子 | 你平时就在这里看到它 |
|---|---|---|
| 普通文件(Regular File) | 源代码、日志、图片、二进制程序 | ~/project/main.go |
| 目录(Directory) | 文件夹——本质上是一个存有"文件名→inode 编号"映射关系的特殊文件 | 任何一个文件夹 |
| 设备文件(Device File) | 硬盘、键盘、鼠标、终端 | /dev/sda(硬盘)、/dev/tty(终端) |
| 管道(Pipe / FIFO) | 进程间单向数据流 | ls | grep foo 的 | |
| 套接字(Socket) | 网络连接端点,也可以是本机进程间通信 | /var/run/docker.sock |
| 符号链接(Symlink) | 指向另一个文件路径的"快捷方式" | /usr/bin/python → python3 |
| 虚拟文件(Virtual File) | 内核实时生成的"假"文件,不存在于磁盘 | /proc/cpuinfo、/sys/class/net/eth0/address |
一切皆文件 = 一切都有统一的读写接口。想象一个万能插座:不管你插的是台灯、电视、电风扇、手机充电器,插座提供的都是 220V 交流电。Linux 的"文件接口"就是这个万能插座——不论底层是硬盘、键盘、网卡还是内核数据,上层都用同一套 open/read/write/close 来操作。
假设你想获取当前系统有几颗 CPU 核心。在 Linux 里,你不需要调用特殊 API,直接"读文件"就行:
# 读取 /proc/cpuinfo —— 就像读一个普通文本文件一样
cat /proc/cpuinfo | grep 'processor' | wc -l
# 和读普通文件完全相同的系统调用链:
# open("/proc/cpuinfo") → read() → write() → close()
再比如,你想限制一个进程的 CPU 使用量(第 03 篇 Cgroups 的核心操作),不需要特殊工具,直接"写文件":
# 往一个"文件"里写个数字,内核就帮你限制了这个 Cgroup 的 CPU
echo 50000 > /sys/fs/cgroup/cpu/mygroup/cpu.cfs_quota_us
读文件 = 查询信息,写文件 = 修改配置。这就是 Linux 把复杂性隐藏在统一接口背后的方式。
关键认知
"一切皆文件"不是 Linux 的专利(Unix 首创),但 Linux 把这条哲学贯彻得最彻底。理解这一点后,你会发现:在 Linux 里,"探索系统"就是"浏览文件"——你用 ls 和 cat 就能窥探内核的内部状态,不需要特殊工具。这是 Linux 作为学习平台的巨大优势。
Windows 用户习惯了 C:\、D:\、E:\ 这样的盘符体系——每个分区是一个独立的"根",各自拥有一棵目录树:
Windows(多根):
C:\ D:\ E:\
├─ Windows ├─ 游戏 ├─ 电影
├─ Program Files ├─ 照片 └─ 音乐
└─ Users └─ 文档
Linux 的设计完全不同:整个系统只有一棵目录树,根就是 /。不管你插了几个 U 盘、挂了几块硬盘,它们全部"接入"到这棵树的某个分支上:
Linux(单根):
/
├─ bin/ ← 基本命令(ls, cat, cp...)
├─ etc/ ← 系统配置
├─ home/
│ └─ yutong/ ← 你的个人文件
├─ mnt/
│ ├─ usb/ ← U 盘的内容出现在这里(通过 mount 接入)
│ └─ data/ ← 另一块硬盘的内容出现在这里
├─ proc/ ← 内核进程信息(虚拟,不存在于磁盘)
├─ sys/ ← 内核子系统信息(虚拟)
├─ dev/ ← 设备文件
└─ var/ ← 日志、缓存等可变数据
Windows 盘符 = 独立的岛屿,每个岛有自己的地图系统。C 岛和 D 岛之间没有天然的层次关系。Linux 目录树 = 一棵大树,/ 是树干,所有存储设备和文件系统都是长在这棵树上的树枝。你要访问 U 盘?它就在 /mnt/usb 这个树枝上——和访问 /home/yutong 没有任何语法差异。
/home/yutong/file.txt 就行了——它可能在本地 SSD 上,也可能是网络挂载的 NFS 远程文件系统,对程序来说没有区别。/home/yutong/projects,让那个目录拥有独立的存储空间——不需要给它单独的盘符。/ 不是宿主机的 /,而是一个隔离的目录树(通过 pivot_root 或 chroot 实现)。没有"统一树"的设计,容器文件系统隔离会复杂得多。有了"一切皆文件"和"统一目录树"的认知后,还有一个关键概念需要理解:文件名和文件内容是两个不同的东西,它们通过 inode 关联。
inode(Index Node,索引节点)是文件系统内部的一个数据结构,每个文件(或目录)都对应唯一一个 inode。inode 里存的是:
| inode 存储的内容 | 说明 |
|---|---|
| 文件类型与权限 | 是普通文件、目录还是符号链接?rwx 权限是什么? |
| 所有者信息 | UID(属主)、GID(属组) |
| 大小 | 文件占多少字节 |
| 时间戳 | 最后访问时间(atime)、最后修改时间(mtime)、最后属性变更时间(ctime) |
| 数据块指针 | 文件的实际数据存在磁盘的哪些位置 |
| 链接计数 | 有多少个"文件名"指向这个 inode |
注意 inode 里不存什么:不存文件名。文件名存在哪里?存在目录里。目录本质上是一个"文件名 → inode 编号"的映射表:
目录 "/home/yutong" 的内部结构(概念示意):
文件名 → inode 编号
. → 524288 (指向目录自身)
.. → 262144 (指向父目录 /home)
notes.txt → 789012 (指向 notes.txt 的数据)
project/ → 890123 (指向 project 子目录)
inode = 房产证,记录着"这块地(数据块)的面积、位置、产权人"。文件名 = 门牌号,只是贴在门口的标签。房产证(inode)只有一份,但一栋房子可以有多个门牌号(多个文件名指向同一个 inode)——这就是硬链接(hard link)的本质。
为什么 mv(移动/重命名)几乎瞬间完成?因为 mv 只修改了目录里的"文件名 → inode 编号"映射,没有碰实际数据。只要源和目标在同一个文件系统内(inode 编号空间一致),mv 就是改一行记录而已。
# 查看文件的 inode 编号
ls -i notes.txt
# 输出: 789012 notes.txt
# mv 改名后,inode 编号不变
mv notes.txt notes_v2.txt
ls -i notes_v2.txt
# 输出: 789012 notes_v2.txt ← 还是同一个 inode
为什么删除大文件很快,但删除很多小文件很慢?删除文件本质是减少 inode 的链接计数 + 标记数据块为可用。大文件只有一个 inode 要处理;100 万个小文件有 100 万个 inode 要逐个处理。
为什么硬链接不能跨文件系统?因为 inode 编号只在单个文件系统内有意义。文件系统 A 的 inode 789012 和文件系统 B 的 inode 789012 是完全不同、毫无关系的两个文件。
# 创建硬链接——两个文件名,同一个 inode
ln source.txt hardlink.txt
ls -li source.txt hardlink.txt
# 输出同样的 inode 编号,链接计数 +1
# 符号链接就不一样了——它是一个"指向路径"的特殊文件,有自己的 inode
ln -s source.txt softlink.txt
ls -li source.txt softlink.txt
# 输出不同的 inode 编号
inode 与容器的关系
OverlayFS(第 04 篇)的分层机制本质上是在不同层之间管理 inode 的指向关系。写时复制(Copy-on-Write)就是:当你要修改 lower 层的一个文件时,先把它的 inode 指向的数据块复制到 upper 层,然后修改 upper 层的副本——所有这些操作对容器进程透明。
现在你理解了"统一目录树"和"inode",挂载就好理解了。在讲命令之前,先建立直觉。
一块磁盘(或一个虚拟文件系统)在被格式化后,上面有一个独立的文件系统——它有自己的目录结构、自己的 inode 编号空间。但在挂载之前,这个文件系统和 Linux 的目录树是彼此隔离的,你无法访问它。
挂载(mount)就是把一个独立的文件系统"接入"到 Linux 目录树的某个节点上,让那个节点成为访问这个文件系统的入口。
挂载前:
Linux 目录树 独立文件系统(比如 U 盘)
/ /(U 盘自己的根)
├─ home/ ├─ 照片/
├─ mnt/ ← 空目录 ├─ 文档/
└─ var/ └─ 音乐/
挂载后:sudo mount /dev/sdb1 /mnt/usb
Linux 目录树
/
├─ home/
├─ mnt/
│ └─ usb/ ← 现在访问这里,看到的是 U 盘的内容
│ ├─ 照片/ ← U 盘的内容"长"到了这棵树上
│ ├─ 文档/
│ └─ 音乐/
└─ var/
挂载 = 把 U 盘插到目录上。Linux 的目录树是一个插线板,每个目录节点都可以是一个"插座"。当你把 U 盘 mount 到 /mnt/usb,就等于把 U 盘插进了这个插座——之后电流(数据访问)就通了。当你 umount,就是拔掉插头——U 盘还是那个 U 盘,但不再能通过目录树访问到了。
回到"统一目录树"的设计:因为 Linux 只有一棵树,所以任何存储资源要可访问,就必须成为这棵树的一部分。挂载就是"成为这棵树的一部分"的操作。
Windows 其实也有挂载——只是把挂载点固定在盘符(C:、D:)上,你感知不到"挂载"这个动作。Linux 把这个权力完全交给用户:你想把 U 盘挂到哪个目录都行。
在前面的"一切皆文件"小节你已经看到:硬盘上的 ext4/xfs 文件系统需要挂载,虚拟文件系统同样需要挂载。实际上,Linux 系统启动时,内核会先挂载一个临时 rootfs(在内存中),然后再把真正的根文件系统挂载到 /:
# 查看当前所有挂载点——你会看到各种类型
mount | head -20
# 典型输出解读:
# /dev/sda1 on / type ext4 ← 根文件系统,磁盘上的 ext4,挂载到 /
# proc on /proc type proc ← /proc 是虚拟文件系统,挂载到 /proc
# sys on /sys type sysfs ← /sys 是虚拟文件系统,挂载到 /sys
# tmpfs on /run type tmpfs ← 内存文件系统,挂载到 /run
# /dev/sdb1 on /mnt/usb type vfat ← U 盘(FAT32),挂载到 /mnt/usb
| 文件系统类型 | 存储在哪里 | 典型挂载点 | 容器场景 |
|---|---|---|---|
ext4 / xfs | 磁盘 | /、/home | 容器镜像的底层存储 |
tmpfs | 内存(掉电丢失) | /tmp、/run | 容器的 tmpfs 挂载 |
proc | 内核虚拟生成 | /proc | 每个容器需要独立的 /proc |
sysfs | 内核虚拟生成 | /sys | Cgroup 路径依赖 /sys 挂载 |
overlay | 基于多个下层目录构建 | 容器的 rootfs | Docker 镜像分层(第 04 篇) |
bind mount | 把现有目录"映射"到另一个位置 | 任意 | docker run -v /host/path:/container/path |
# 查看所有挂载点
mount
# 也可以用 findmnt 获得更清晰的树状输出
findmnt
# 挂载磁盘分区到目录
sudo mount /dev/sdb1 /mnt/usb
# 挂载时指定文件系统类型
sudo mount -t ext4 /dev/sdc1 /mnt/data
# 挂载虚拟文件系统(proc、tmpfs 等)
sudo mount -t proc proc /some/dir/proc
sudo mount -t tmpfs tmpfs /tmp
# 绑定挂载(bind mount)——让一个目录同时出现在两个位置
sudo mount --bind /original/path /another/path
# 卸载
sudo umount /mnt/usb
# 如果提示 "target is busy",说明有进程在使用该挂载点
# 用 lsof /mnt/usb 查看是谁,关闭后就能卸载
# 强制卸载(当普通卸载失败时)
sudo umount -l /mnt/usb # lazy unmount,等没人用了再真正卸载
挂载与容器的关系——这是理解容器文件系统的关键
容器的文件系统隔离通过 Mount Namespace(第 02 篇)实现。每个容器拥有独立的挂载表(mount table):
-v /host:/container 本质上就是一个 bind mount,把宿主机的目录"映射"到容器内部/一个帮助你记住的直觉
你可以把 Linux 的文件系统想成一棵树 + 很多个"挂件"。树干和主要树枝是根文件系统(ext4/xfs 在磁盘上),而 /proc、/sys、U 盘、OverlayFS 都是"挂件"——它们通过 mount 挂在树的某个节点上。内核负责让这一切看起来像是一棵完整的、无缝的树,这就是 VFS(Virtual File System)层的工作。
前面讲了很多抽象概念,现在来看两个最重要的虚拟文件系统实例。它们完美体现了"一切皆文件"哲学。
/proc 是一个虚拟文件系统——它不占磁盘空间,内容由内核实时生成。每次你读取 /proc 下的文件,内核都会现场计算并返回当前状态。
| 路径 | 内容 | 谁在用 |
|---|---|---|
/proc/[PID]/ | 某个进程的详细信息 | ps、top、htop |
/proc/[PID]/ns/ | 进程所属的各个 Namespace | nsenter、docker exec |
/proc/[PID]/cgroup | 进程所属的 Cgroup | Cgroup 管理工具 |
/proc/meminfo | 系统内存使用情况 | free 命令 |
/proc/cpuinfo | CPU 信息 | lscpu |
# /proc 中每个数字目录就是一个进程
ls /proc | grep '^[0-9]' | head -5
# 查看当前 bash 进程的信息
ls /proc/$$/
# 输出: cmdline cwd environ exe fd maps mem ns status ...
# 查看某进程的命令行
cat /proc/$$/cmdline | tr '\0' ' '
# 查看系统内存(free 命令的数据来源就是这个文件)
cat /proc/meminfo | head -5
关键认知
ps 命令不是"直接问内核有哪些进程",而是遍历 /proc 目录,读取每个数字子目录的信息拼装输出。所以在容器中如果 /proc 没有重新挂载(还是宿主机的),ps 就会显示宿主机所有进程——这就是 Namespace 篇中 --mount-proc 参数的意义。
/sys(sysfs)也是虚拟文件系统,主要暴露内核子系统和设备的信息:
/sys/fs/cgroup/ — Cgroup 文件系统的入口(第 03 篇核心内容)/sys/class/net/ — 所有网络接口的信息/sys/block/ — 块设备信息(磁盘)# 查看所有网络接口
ls /sys/class/net/
# 输出: eth0 lo docker0 veth1234
# 通过 sysfs 查看某网卡的 MAC 地址
cat /sys/class/net/eth0/address
/proc 偏向"进程视角"(每个进程的状态),/sys 偏向"设备/子系统视角"(硬件和内核模块的状态)。两者都是内核暴露信息的窗口,容器技术大量依赖它们——Cgroup 通过写文件限制资源,Namespace 通过读文件查看隔离状态。
网络接口是 Linux 收发网络数据的端点,可以是物理的也可以是虚拟的:
| 接口 | 类型 | 用途 |
|---|---|---|
eth0 | 物理网卡 | 连接外部网络 |
lo | 回环(loopback) | 本机自己跟自己通信(127.0.0.1) |
docker0 | 虚拟网桥 | Docker 容器的默认网关 |
vethXXXX | 虚拟以太网 | 连接容器和网桥 |
对内核来说,物理网卡和虚拟网卡没有本质区别——都是"网络接口"这个抽象的实例,都能配 IP、收发数据包。这是容器网络的基础。
# 查看所有网络接口及其 IP
ip addr show
# 简写
ip a
RFC 1918 规定了三段永远不会出现在公网的地址,任何人都可以在内网随意使用:
| 网段 | 范围 | 常见用途 |
|---|---|---|
10.0.0.0/8 | 10.0.0.0 ~ 10.255.255.255 | 大型企业内网、云厂商 VPC、容器网络 |
172.16.0.0/12 | 172.16.0.0 ~ 172.31.255.255 | Docker 默认网段(172.17.0.0/16) |
192.168.0.0/16 | 192.168.0.0 ~ 192.168.255.255 | 家庭路由器、小型办公网络 |
IP 地址 32 位,/数字 表示前多少位是网络号,剩余是主机号:
10.0.0.1/24
网络号:10.0.0.___ (前 24 位,标识"哪个子网")
主机号:_______.1 (后 8 位,标识"子网中第几台")
该子网范围:10.0.0.0 ~ 10.0.0.255(可用主机地址 .1 ~ .254)
类比邮政地址:网络号 = 街道名,主机号 = 门牌号。同一条街(同一子网)的住户可以直接喊话通信,不同街道需要通过邮局(路由器)转发。/24 就是说"街道名占了地址的前 24 位"。
快速记忆:
/24 = 256 个地址(一个"小区"),最常用/16 = 65536 个地址(一个"城区")/8 = 约 1677 万个地址(一个"城市")veth = Virtual Ethernet。veth pair 是成对出现的虚拟网络接口——数据从一端进,必定从另一端出:
┌─────────────────┐ ┌─────────────────┐
│ Namespace A │ │ Namespace B │
│ │ │ │
│ [veth-a] ●━━━━━━ 虚拟网线 ━━━━━━● [veth-b] │
│ │ │ │
└─────────────────┘ └─────────────────┘
从 veth-a 发出的数据 → 立刻出现在 veth-b
从 veth-b 发出的数据 → 立刻出现在 veth-a
物理世界里,两台电脑用一根网线直连就能通信。veth pair 就是这根网线的虚拟版。Network Namespace 隔离后两个空间完全不通(像两个密封的房间),veth pair 就是在墙上打一个洞穿一根线,让两边能互通数据。
# 创建一对 veth
sudo ip link add veth-a type veth peer name veth-b
# 查看——两个接口同时出现
ip link show type veth
# 给 veth-a 配 IP 并启动
sudo ip addr add 10.0.0.1/24 dev veth-a
sudo ip link set veth-a up
# 给 veth-b 配 IP 并启动
sudo ip addr add 10.0.0.2/24 dev veth-b
sudo ip link set veth-b up
# 测试连通
ping -c 2 10.0.0.2
# 清理
sudo ip link del veth-a # 删一端,另一端自动消失
veth pair 是一对一的连接。当有多个容器需要互通时,不可能两两直连(N 个容器需要 N*(N-1)/2 根线)。这时需要网桥(Bridge):
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 容器 A │ │ 容器 B │ │ 容器 C │
│ [veth-a] │ │ [veth-b] │ │ [veth-c] │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━ docker0 网桥
│
[宿主机网络]
| 物理世界 | 虚拟世界 |
|---|---|
| 交换机(Switch) | Linux Bridge(如 docker0) |
| 网线插到交换机端口 | veth 一端连到 Bridge |
| 交换机转发数据帧给目标端口 | Bridge 按 MAC 地址转发给目标 veth |
# 创建网桥
sudo ip link add br0 type bridge
sudo ip link set br0 up
# 创建两对 veth,一端接入网桥
sudo ip link add veth1 type veth peer name veth1-br
sudo ip link add veth2 type veth peer name veth2-br
# 把 "网桥侧" 的端接入 br0
sudo ip link set veth1-br master br0
sudo ip link set veth2-br master br0
sudo ip link set veth1-br up
sudo ip link set veth2-br up
# 给 "容器侧" 配 IP
sudo ip addr add 10.0.0.1/24 dev veth1
sudo ip addr add 10.0.0.2/24 dev veth2
sudo ip link set veth1 up
sudo ip link set veth2 up
# veth1 和 veth2 现在可以通过 br0 互通
ping -c 2 -I veth1 10.0.0.2
Docker 网络的本质
安装 Docker 后系统多出一个 docker0 网桥。每创建一个容器,Docker 就生成一对 veth——一端放进容器的 Network Namespace(容器里看到的 eth0),另一端接到 docker0 网桥。所有容器通过这个网桥互通,再通过 NAT/iptables 规则与外网通信。
| 模块 | 核心认知 | 后续关联 |
|---|---|---|
| 进程模型 | fork 复制 + exec 替换;PID 是进程身份证;所有进程形成树 | PID Namespace 让容器看到独立的进程树(第 02 篇) |
| 文件系统基础 | 一切皆文件(统一 open/read/write/close 接口);统一目录树(单根 /);inode 是文件身份证(文件名≠文件内容) | 容器 rootfs 就是一棵独立的目录树;OverlayFS 的分层本质是 inode 指向的切换(第 04 篇) |
| mount 挂载 | 所有文件系统必须通过 mount 接入目录树才能访问;不只是接 U 盘,proc/sys/overlay 等虚拟文件系统也通过 mount 接入 | Mount Namespace 隔离挂载表;Docker -v 本质是 bind mount;OverlayFS 通过 mount 实现分层合并(第 02、04 篇) |
| /proc 与 /sys | 虚拟文件系统,不占磁盘,内核实时生成;ps 命令读的是 /proc 而非直接问内核 | Namespace 通过 /proc/PID/ns/ 暴露;Cgroups 通过 /sys/fs/cgroup/ 控制资源(第 03 篇) |
| 网络接口 | 物理网卡和虚拟网卡对内核无区别,都能配 IP 收发数据 | Network Namespace 给容器一套独立的网络接口(第 02 篇) |
| IP/子网/CIDR | 10.x / 172.16~31.x / 192.168.x 是私有地址;/24 表示子网大小 | 容器网络全部使用私有地址 |
| veth pair | 成对的虚拟网线,数据从一端进另一端出 | 连接容器 NS 与宿主机网桥(第 02 篇 Network NS) |
| 网桥 | 虚拟交换机,多个 veth 接入后可互通 | docker0 就是一个网桥,所有容器通过它互联 |
pstree -p 观察你的系统进程树,找到当前 shell 的位置ls -li 查看它的 inode 编号;mv 重命名后再次查看,验证 inode 不变;创建一个硬链接,观察两个文件名是否共享同一个 inodels /proc/$$/ 探索你当前 bash 进程的信息,尝试读取 cmdline、status、ns/mount | grep -E 'proc|sys|cgroup',看看系统中有哪些虚拟文件系统被挂载ip addr 查看你系统上有哪些网络接口,哪些是物理的、哪些是虚拟的ping 验证连通性后删除