docker容器底层原理依赖namespace实现隔离、cgroups实现资源限制、unionfs实现分层存储,三者协同使容器成为共享宿主机内核的轻量级隔离进程。

从进程视角切入:容器本质就是一个被包装的进程
别一上来就看 Dockerfile 或 docker run。先问自己:Linux 里怎么让一个普通进程“以为自己独占系统”?答案就是 namespaces 和 cgroups —— 它们不是 Docker 发明的,是 Linux 内核原生能力。
- 用
unshare命令手动创建 PID、mount、network 命名空间,观察ps和df输出变化,比看图更直观 - 跑个简单容器:
docker run -it --rm ubuntu:22.04 ps aux,再进宿主机执行ps aux | grep ubuntu,对比进程树,立刻明白“容器内看到的 PID 1 实际是宿主机上某个子进程” - 记住关键点:容器没操作系统内核,它共享宿主机 kernel;所谓“启动快”,是因为没开机过程,只是启了一个受限进程
拆开镜像看分层:UnionFS 是高效复用的核心
Docker 镜像不是打包压缩包,而是一堆只读层 + 一层可写层的叠加。理解这个结构,才能搞懂为什么 docker build 能缓存、为什么不同镜像能共用基础层。
- 用
docker image inspect nginx:alpine查看RootFS.Layers,数一数有多少层;再对比ubuntu:22.04和你基于它构建的镜像,找重复的 layer ID - 运行容器后,
docker diff <container_id></container_id>会显示哪些文件在可写层被新增/修改/删除 —— 这就是 UnionFS 的“覆盖逻辑”在起作用 - 避免误区:Dockerfile 每条指令(如 RUN、COPY)都生成新层,但合并层(
docker build --squash)不推荐用于生产,会影响缓存复用
动手验证资源控制:cgroups 不是开关,是调节阀
限制内存或 CPU 不是加个 flag 就完事。cgroups 的行为直接影响 OOM Killer 触发时机、CPU 时间片分配,甚至容器是否“卡住不动”。
- 启动两个容器:
docker run -m 100M --name mem1 ubuntu:22.04 stress --vm 1 --vm-bytes 200M和docker run -m 100M --name mem2 ubuntu:22.04 sleep infinity,观察前者被 OOM Kill,后者仍存活 - 用
docker stats实时看内存/网络/blkio 使用,再进容器内部执行cat /sys/fs/cgroup/memory/memory.limit_in_bytes,确认值与-m设置一致 - 注意:CPU 限额(
--cpus 0.5)和 CPU 权重(--cpu-shares)机制不同,前者是硬上限,后者是相对权重,在多容器争抢时才体现
串联整个生命周期:从 pull 到 run 的每一步发生了什么
把命令串起来看数据流向,就能看清 Docker Daemon 如何协调各组件:
-
docker pull ubuntu:22.04→ 从 Registry 下载 manifest + 各 layer blob → 本地解压为只读层(存于/var/lib/docker/overlay2) -
docker run ubuntu:22.04→ Daemon 创建新命名空间 → 分配 cgroups → 加载镜像只读层 + 初始化可写层 → 调用runc启动进程 - 容器退出后,可写层自动丢弃(除非用了 volume);镜像层不变,供下次启动复用











