一文读懂Docker与Kubernetes:从容器引擎到编排平台,底层原理全解析
作为云原生时代的基石,Docker 与 Kubernetes 几乎成了现代软件交付的标配。但你真的了解它们的底层运作机制吗?本文将从容器本质出发,深入 Linux 内核的 namespace、cgroup 和联合文件系统,再延伸到 Pod、调度、网络、存储等 K8s 核心原理,带你构建完整知识体系。
目录
容器不是虚拟机
Docker 底层三驾马车
2.1 命名空间 (Namespace) —— 隔离的魔法
2.2 控制组 (Cgroup) —— 资源的围栏
2.3 联合文件系统 (UnionFS) —— 镜像分层与复用
Docker 工作流程全景
为什么需要 Kubernetes?
Kubernetes 架构解密
5.1 控制平面与工作节点
5.2 Pod:最小的调度单元
5.3 声明式 API 与控制循环
Kubernetes 核心底层原理
6.1 调度器如何做决策
6.2 网络模型与 CNI
6.3 持久化存储与 CSI
从 Docker 到 K8s:协作关系演变
总结与学习建议
1. 容器不是虚拟机
很多人刚接触容器时,会把它类比为“轻量级虚拟机”。这个说法不够准确。
虚拟机通过 Hypervisor 模拟完整硬件,每个虚拟机拥有独立的内核、操作系统,隔离性强但资源开销大。
容器直接运行在宿主机内核上,通过 Linux 内核特性实现进程级别的隔离和资源限制,启动时间可以达到毫秒级,单机可运行成百上千个容器。
理解容器,必须回到 Linux 内核的三大机制:Namespace、Cgroup 和 UnionFS。
2. Docker 底层三驾马车
Docker 本身是容器运行时和管理工具,它把底层技术包装成易用的命令行和 API。真正干活的是内核特性。
2.1 命名空间 (Namespace) —— 隔离的魔法
Namespace 让一组进程“以为自己独占系统”,实际上彼此不可见。Linux 内核提供 8 种命名空间:
| 命名空间类型 | 隔离内容 | 作用 |
|---|---|---|
| Mount | 文件系统挂载点 | 容器拥有独立根文件系统 |
| PID | 进程编号 | 容器内进程 PID=1 看不到外部进程 |
| Network | 网络设备、IP、端口 | 容器可拥有独立网卡和 IP |
| IPC | 消息队列、信号量 | 防止进程间通信干扰 |
| UTS | 主机名和域名 | 容器可设置自己的 hostname |
| User | 用户和用户组 | 允许非 root 用户映射 |
| Cgroup | Cgroup 根目录视图 | 限制容器只能看到自己的资源限制 |
| Time | 系统时间 (较新内核) | 容器可有独立系统时间 |
当你执行docker run,Docker 会创建一组新的 namespace,把容器进程放进去,实现隔离。
动手体验:在宿主机查看容器内进程
bash
# 查看宿主机上的 PID 命名空间隔离 docker run -d --name test nginx # 容器内查看 nginx 的 PID 是 1 docker exec test ps aux # 回到宿主机查看真实 PID ps aux | grep nginx # 发现 PID 完全不同
2.2 控制组 (Cgroup) —— 资源的围栏
Cgroup(control group)负责限制、审计和隔离进程组使用的物理资源(CPU、内存、磁盘 I/O、网络等)。没有 Cgroup,一个容器可能耗尽宿主机资源。
关键子系统:
cpu:限制 CPU 使用率 (
--cpus,--cpu-shares)memory:限制内存使用,达到上限触发 OOM Kill
blkio:限制磁盘 I/O
net_cls:标记网络包,配合流量控制
devices:控制设备访问权限
Docker 命令示例:
bash
docker run -d --memory="256m" --cpus="1.5" myapp
这背后实际是往/sys/fs/cgroup/目录下的对应子系统目录里写入限制值。Cgroup v2 进一步统一了层级结构,简化管理。
2.3 联合文件系统 (UnionFS) —— 镜像分层与复用
Docker 镜像为何能快速分发、分层构建?答案就是联合文件系统(如 OverlayFS、AUFS 等)。
它将多个目录(层)挂载为单一视图。最下层是只读的镜像层,最上层是可写的容器层。
写时复制(Copy-on-Write):当容器修改只读层的文件时,联合文件系统会先将文件复制到可写层,再修改。删除文件则通过“白out文件”标记隐藏。
典型 Overlay2 结构:
text
overlay2 ├── lower (镜像层,可多层,只读) ├── upper (容器层,读写) ├── merged (统一视图) └── work (内部工作目录)
这种分层技术使镜像构建缓存复用、启动容器极快、存储占用小。开发者常写的 Dockerfile 中每一行指令就会产生一个新层。
3. Docker 工作流程全景
整体架构可以分为:
Docker 客户端(
dockerCLI) 通过 REST API 与守护进程通信。Docker 守护进程(
dockerd) 负责构建、运行、管理容器。containerd管理容器生命周期(拉取镜像、创建容器、管理存储网络)。
runc是最终的容器运行时,基于 OCI 标准,使用 libcontainer 直接与内核打交道,创建 namespace、cgroup 等。
一个docker run命令背后大致流程:
CLI 发送请求到 dockerd
dockerd 调用 containerd 启动容器
containerd 转化成 runc 能理解的 OCI bundle
runc 创建容器进程、设置 namespace、cgroup、mount rootfs
容器启动,进程完成隔离
4. 为什么需要 Kubernetes?
单机 Docker 可以打包应用、隔离环境,但生产环境中必须解决:
多主机调度:哪个机器有资源跑新容器?
服务发现与负载均衡:容器 IP 会变,如何自动注册与发现?
滚动更新与回滚:不停机更新版本。
健康检查与自动修复:挂掉的容器自动重启。
配置和密钥管理:不改镜像而更新配置。
Kubernetes(K8s)正是为了解决大规模容器编排问题而生。它借鉴了 Google 内部 Borg 系统的经验,构建在声明式 API 和控制循环之上。
5. Kubernetes 架构解密
5.1 控制平面与工作节点
K8s 集群分为:
Control Plane:大脑,管理集群状态
kube-apiserver:统一入口,RESTful API 网关etcd:持久化存储所有集群数据(键值数据库)kube-scheduler:决定 Pod 分配到哪个节点kube-controller-manager:运行各种控制器(副本、节点等)
Worker Node:运行工作负载
kubelet:节点代理,负责与 apiserver 通信,管理本机容器kube-proxy:实现 Service 网络代理和负载均衡容器运行时(containerd、CRI-O 等):运行容器
5.2 Pod:最小的调度单元
Kubernetes 不直接管理容器,而是管理Pod。Pod 是一组共享网络和存储命名空间的容器集合。
同一个 Pod 内的容器可以通过localhost通信,共享 Volume。设计模式上,Pod 常用于“边车”模式,如主容器+日志收集容器。
Pod 的底层实现原理:
Kubelet 在启动 Pod 时,会先创建一个infra 容器(也叫 pause 容器),它负责创建并持有 Network Namespace。然后业务容器加入这个命名空间,从而共享同一个网络栈,因此看到相同的 IP 和端口空间。
5.3 声明式 API 与控制循环
不同于命令式操作(一步步告诉怎么做),K8s 采用声明式:你提交期望状态(yaml),由控制器调谐到该状态。
核心机制是控制循环(Control Loop):
text
for { 实际状态 := 观察集群当前状态 期望状态 := 从 API 对象读取 spec if 实际状态 != 期望状态 { 执行操作使实际状态趋向期望状态 } }每个控制器(如 Deployment、ReplicaSet)都是一个独立的控制循环,它们监听特定资源变化,持续驱动系统向用户声明的目标收敛。
6. Kubernetes 核心底层原理
6.1 调度器如何做决策
kube-scheduler 负责把未调度的 Pod 绑定到合适的 Node。主要流程:
过滤(Predicates)
剔除不满足条件的节点:资源不足、节点选择器不符、污点容忍等。打分(Priorities)
对剩余节点按策略打分(如资源均衡度、镜像本地化等),选出最优。
调度器是可插拔的,你可以自定义调度策略。
6.2 网络模型与 CNI
K8s 网络要求:所有 Pod 可以直接通信,无需 NAT;节点可以与 Pod 通信;Pod 看到的 IP 与其他 Pod 看到的 IP 一致。这并非 Docker 默认网络能实现。
CNI (Container Network Interface)定义容器网络插件标准。常见实现:
Flannel:简单 overlay,通过 VXLAN 或 host-gw
Calico:基于 BGP 的三层网络,支持网络策略
Cilium:基于 eBPF,高性能且具备可观测性
Service 的底层实现:
ClusterIP Service 本质是一个虚拟 IP,由 kube-proxy 在节点上设置 iptables 或 IPVS 规则,将 Service IP + Port 的流量负载均衡到后端 Pod。
例如,使用 iptables 模式,访问 ClusterIP 时,规则做 DNAT 转换到随机选中的 Pod IP。这也解释了为什么 Service IP ping 不通,因为它没有真正绑定网卡。
6.3 持久化存储与 CSI
容器本身是无状态的,Pod 销毁数据消失。K8s 通过Volume解决持久化。除了简单的 emptyDir、hostPath,最常用的是PV (PersistentVolume) / PVC (PersistentVolumeClaim)体系。
PV:管理员或 StorageClass 动态创建的集群存储资源(如云盘)
PVC:用户声明的存储需求(大小、访问模式)
绑定后,Pod 引用 PVC,就可以挂载持久化存储。
底层依赖CSI (Container Storage Interface)驱动,实现对不同存储系统的统一接入(AWS EBS、Ceph、NFS 等)。挂载过程涉及 Attach(将远程卷关联到节点)、Mount(挂载到容器内文件系统)两步,由 kubelet 与 CSI 插件协作完成。
7. 从 Docker 到 K8s:协作关系演变
早期 Kubernetes 直接内置 Docker 支持,后来抽象出CRI (Container Runtime Interface)标准,让任何符合 CRI 的运行时都可接入。由于 Docker 本身的臃肿(包含 dockerd、containerd 链太长),社区转向更精简的运行时。
containerd目前是 kubelet 默认的 CRI 实现,它还直接兼容 OCI 镜像。从用户角度看,依然可以用docker build构建镜像,K8s 能直接使用。
这条链路体现了分层解耦的思想:Docker CLI → dockerd → containerd → runc → 容器进程
在 Kubernetes 中变为:kubelet → CRI plugin (containerd) → runc → 容器进程
8. 总结与学习建议
Docker 和 Kubernetes 的成功,源于它们巧妙地将 Linux 内核的强大能力抽象成开发者友好的工具。掌握底层原理,不仅能从容应对故障排查,还能更好地设计云原生应用。
进阶建议:
手动用
unshare、cgroup创建容器,加深理解。阅读
runc源码中 namespace 设置部分。研究一个 CNI 插件的工作原理。
部署一个多节点 K8s 集群并观察控制循环日志。
容器技术仍在快速演进,eBPF、WebAssembly 等新技术不断融入,但 Namespace、Cgroup、UnionFS 始终是根基。打好底层基础,才能无惧技术变革。
本文深入介绍了 Docker 和 Kubernetes 的底层技术原理,如果对你有帮助,欢迎点赞、收藏、转发,也欢迎在评论区讨论你的见解!