Docker 并不是凭空发明了一套全新的隔离技术,而是巧妙地将 Linux 内核已有的几项基础能力进行了组合和封装。理解这些底层实现,能让你对容器的行为有精准的掌控,而不是把它当作一个黑盒。
1. 镜像:联合文件系统(UnionFS)与分层存储
Docker 镜像的分层结构,直接依赖 Linux 的联合挂载(union mount)技术,目前最常见的实现是 OverlayFS。当你执行 docker pull 或 docker build 时,每一层都对应一个只读的文件系统目录。OverlayFS 将这些目录堆叠成一个统一视图:下层只读,最上层可写。
本质上,运行容器时 Docker 做的是:
mount -t overlay overlay -o lowerdir=layer2:layer1,upperdir=upper,workdir=work merged
layer1、layer2是镜像的只读层;upper是容器的可写层(所有修改都在这里);merged就是你在容器里看到的/目录。
同一个基础镜像的多个容器可以共享相同的只读层,仅各自维护一个很小的 upper 层,这就是镜像复用和快速启动的根源。没有黑科技,只是 Linux 文件系统支持的正常玩法。
2. 容器:命名空间(namespace)与控制组(cgroup)
容器的隔离感来自内核的 命名空间 机制,每种资源类型有一个独立的命名空间:
- PID 命名空间:容器内的进程 PID 从 1 开始,看不见宿主机进程,通过
clone(CLONE_NEWPID)创建。 - 挂载命名空间:容器拥有自己的根文件系统挂载点,
/proc、/sys等也是隔离的。 - 网络命名空间:容器有自己的网卡、IP 地址、路由表、防火墙规则。
- UTS 命名空间:容器可以有独立的主机名和域名。
- IPC 命名空间:隔离共享内存、信号量等进程间通信资源。
- 用户命名空间:容器内的 root 可以映射为宿主机的普通用户,提升安全性。
用 lsns 命令可以直接查看当前系统的命名空间列表;进入容器的命名空间可以用 nsenter 工具,这些都是在不使用 Docker 的情况下,原生调试容器隔离问题的必备技巧。
容器的资源限制则依赖 cgroup(控制组)。docker run --memory 256m --cpus 1 这样的参数,最终会转化为在 /sys/fs/cgroup 下对应的子目录中写入限制值:
echo "256M" > /sys/fs/cgroup/memory/docker/<container_id>/memory.limit_in_bytes
echo "100000" > /sys/fs/cgroup/cpu/docker/<container_id>/cpu.cfs_quota_us
cgroup 严格保证该组内所有进程的总内存或 CPU 用量不超过设定值,这就是容器资源隔离的真相。
3. 数据卷:绑定挂载(bind mount)
Docker 的数据卷(volume)或绑定挂载主机目录,底层就是 Linux 的 bind mount 操作。当你执行:
docker run -v /host/path:/container/path …
Docker 实际执行类似:
mount --bind /host/path /var/lib/docker/.../merged/container/path
这使得宿主机和容器共享同一份 inode 和数据,任何一方的修改立即对另一方可见。这也是为什么容器删除后,宿主机上的数据依然存在——它们本来就是普通目录,只是被挂载到了容器的文件系统视图中。
理解这一点非常重要:你可以直接在宿主机上用 ls、cp 等工具操作这些目录,也可以用 mount | grep bind 查看当前所有的绑定挂载关系。数据卷不是 Docker 的专属存储格式,它就是 Linux 文件系统的一部分。
4. 容器网络:网络命名空间 + veth pair + 网桥 + iptables
Docker 网络模型是几个经典 Linux 网络组件的美妙组合:
- 网络命名空间:每个容器都有独立的网络栈,有自己的
eth0接口。 - veth pair:虚拟以太网对,像一根网线的两头,一头插在容器内部(
eth0),另一头留在宿主机的网络命名空间中,通常命名为vethXXXX。 - 网桥(bridge):宿主机上运行一个虚拟交换机
docker0,所有容器的 veth 宿主机端都连接到这个桥上,形成一个二层网络,容器之间可以直接通过 IP 通信。 - iptables/NAT:当容器需要访问外网时,宿主机进行源地址转换(SNAT),让容器的私有 IP 变为宿主机 IP。当外部需要访问容器的映射端口(
-p 8080:80)时,宿主机进行目的地址转换(DNAT),将发往宿主机 8080 端口的流量转发到容器的 80 端口。
你可以用标准 Linux 命令直接查看这些底层对象:
ip netns list # 查看所有网络命名空间
ip link show type veth # 查看所有 veth 对
bridge link show # 看到哪些 veth 接到了 docker0 桥
iptables -t nat -L -n -v # 查看 NAT 转发规则
这就是容器网络的全部秘密,没有专属协议,全是老旧而可靠的 Linux 网络设施。
小结
Docker 镜像就是叠加在一起的文件系统挂载;容器就是一堆命名空间和 cgroup 限制下的进程;数据卷就是 bind mount;容器网络就是独立网络栈加上 veth、网桥和 iptables。掌握这些底层原理后,排错不再局限于 docker logs,而是可以直接深入 /proc、/sys、mount 和 ip 命令去追踪,实现真正意义上的“洞察容器”。