很多人用 Docker 时觉得它很"轻量",但轻量背后其实是一套扎实的 Linux 内核机制。理解这些底层知识,能帮你更好地排查容器问题、设计镜像和规划资源。
文章目录
-
- 一、容器不是虚拟机
- 二、Namespace:隔离"看到什么"
- 三、Cgroups:限制"能用多少"
- 四、UnionFS:镜像分层的秘密
- 五、网络:靠虚拟设备串联
- [六、一次 `docker run` 发生了什么](#六、一次
docker run发生了什么) - 小结
一、容器不是虚拟机
虚拟机靠 Hypervisor 模拟硬件,每个 VM 有独立内核;而容器直接共享宿主机内核,只是通过内核提供的隔离能力"伪装"成独立系统。所以容器启动快、开销小,但也意味着容器内的内核版本必须和宿主机兼容。
Docker 的底层依赖主要有四块:Namespace、Cgroups、UnionFS 和网络虚拟化。
二、Namespace:隔离"看到什么"
Namespace 是 Linux 内核提供的资源隔离机制。Docker 启动容器时,会为进程创建一组新的 Namespace,让进程只能看到自己 Namespace 内的资源。
常见的几种:
- PID Namespace:容器内进程 PID 从 1 开始,看不到宿主机其他进程
- Mount Namespace:容器有独立的挂载点视图,根目录由镜像提供
- Network Namespace:独立的网卡、IP、端口、路由表
- UTS Namespace:独立的主机名和域名
- IPC Namespace:独立的信号量、消息队列
- User Namespace:用户和用户组映射,可实现 root 权限隔离
举个例子,容器里执行 ps aux 只看到几个进程,就是 PID Namespace 在起作用。
三、Cgroups:限制"能用多少"
Namespace 解决"看得见看不见",Cgroups(Control Groups)解决"能用多少"。它负责对 CPU、内存、磁盘 I/O、网络带宽等资源做限制和统计。
Docker 运行时的 --cpus、--memory、--blkio-weight 等参数,最终都是写到 Cgroups 的对应子系统里。比如:
bash
docker run -m 512m --cpus=1.5 nginx
本质是在 /sys/fs/cgroup/ 下为这个容器创建一组限制文件。如果容器被 OOM Kill,往往就是 memory cgroup 触发了限制。
四、UnionFS:镜像分层的秘密
Docker 镜像最迷人的地方是"分层复用",靠的是联合文件系统(UnionFS),常见实现有 OverlayFS、AUFS、devicemapper 等。
核心思想:
- 镜像由多个只读层叠加而成,每层是一次构建的变更
- 容器启动时,在最上面加一个可写层
- 读文件时从上层往下找,写文件时用 Copy-on-Write:把文件从只读层复制到可写层再修改
这带来几个直接结果:
- 镜像层可共享:多个容器用同一镜像,只读层在磁盘上只有一份
- 容器删除后数据丢失:可写层随容器销毁,所以要用 Volume 持久化
- 构建要合并指令 :Dockerfile 里多条
RUN会生成多层,层数越多镜像越大,所以常把命令用&&合并
五、网络:靠虚拟设备串联
Docker 默认的 bridge 网络,底层是 Linux 的 veth pair 和 网桥:
- 每个容器有一对 veth 设备,一端在容器 Network Namespace 内(如 eth0),另一端挂在宿主机的
docker0网桥上 - 容器间通信通过网桥二层转发
- 访问外网靠 iptables 的 NAT 规则做源地址转换
- 端口映射
-p 8080:80本质是一条 DNAT 规则
六、一次 docker run 发生了什么
把上面串起来:
- Docker Daemon 收到请求,准备镜像层
- 创建 Namespace(PID、Mount、Network 等)
- 创建 Cgroups 并写入资源限制
- 用 OverlayFS 挂载镜像层 + 可写层作为容器根文件系统
- 创建 veth pair,接入网桥,配置 IP 和 iptables
- 启动容器内 1 号进程,执行 entrypoint
小结
| 机制 | 作用 |
|---|---|
| Namespace | 隔离资源视图 |
| Cgroups | 限制资源用量 |
| UnionFS | 镜像分层与写时复制 |
| veth + 网桥 | 容器网络通信 |
Docker 本身并不神秘,它更像是对 Linux 内核能力的"封装与编排"。理解了这四块,再看容器的性能问题、网络不通、磁盘占用,思路会清晰很多。