docker容器直接调用宿主机linux内核,不运行独立内核。所有容器进程通过namespaces实现视图隔离、cgroups实现资源限制,系统调用直通宿主机内核处理,uname -r结果与宿主机一致。

Docker 容器不运行自己的操作系统内核,而是直接使用宿主机的 Linux 内核。这种共享不是“复制”或“桥接”,而是进程在宿主机内核空间中被调度、执行系统调用的原生行为。
内核调用路径是直通的
容器里的进程(比如 ps 或 curl)发起系统调用时,CPU 会陷入内核态,由宿主机内核直接处理——中间没有虚拟化层、没有翻译、也不经过额外的内核实例。你可以用 uname -r 在容器里执行,结果和宿主机完全一致,这就是最直观的证据。
- 所有容器进程与宿主机普通进程一样,注册在同一个 PID 树下(只是被 PID namespace 隔离了视图)
- 网络数据包从容器发出后,直接进入宿主机的 netfilter 链,由宿主机内核协议栈处理
- 文件读写最终落到宿主机的 VFS 层,再由具体文件系统(如 ext4、XFS)驱动完成
隔离靠 namespace,不限制内核访问
共享内核不等于共享环境。Linux namespace 提供的是“视图隔离”,而非“内核分割”。每个容器看到的是一套独立的资源视角,但底层仍由同一内核服务:
-
PID namespace:容器内 PID 1 是它自己的 init 进程,但该进程在宿主机上仍有真实 PID(比如 1287),可被
ps aux查到 - NET namespace:容器有自己的 lo、eth0 等网络设备,但这些设备本质是宿主机内核创建的虚拟接口(veth pair + bridge)
- MNT namespace:挂载点相互不可见,但所有 mount 操作最终由宿主机内核的 VFS 层统一管理
- UTS、IPC、USER namespace 同理,都是对同一内核对象的不同命名/权限/可见性封装
cgroups 负责资源约束,不干预内核调用
内核共享带来高效,但也需要防止某个容器耗尽资源。cgroups 不改变系统调用路径,而是在内核调度和内存分配等关键路径上插入检查点:
- CPU 使用受
cpu.weight或cpu.max限制,但调度器仍是宿主机的 CFS - 内存分配申请会被 cgroups memory controller 拦截,超限时触发 OOM killer(杀的是容器内进程,不是内核)
- IO、PID 数量、设备访问等也通过类似机制约束,全部基于宿主机内核已有的子系统
用户空间独立,内核空间共用
容器镜像打包的是用户空间软件(glibc、bash、nginx 等),它们运行在各自的 rootfs 中,彼此文件系统不可见;但所有这些二进制文件,都链接到宿主机内核提供的 syscall 接口。这意味着:
- 容器不能加载内核模块(没权限,也没独立内核)
- 容器无法升级或替换内核版本——它只能适配宿主机当前运行的内核
- 某些依赖特定内核特性的程序(如 eBPF 程序)必须与宿主机内核版本兼容











