kubernetes容器运行时演进本质是抽象层级上移与职责剥离:docker一体化架构被解耦,cri定义标准接口,containerd成为oci/cri双兼容的事实标准,k8s 1.24彻底移除dockershim,实现轻量、可替换、标准化运行时体系。

直接看架构图,其实抓不住演进的关键——真正推动变化的是运行时抽象层级的上移和职责剥离。Docker早期把构建、镜像、网络、存储、运行全包在一起,而Kubernetes要管成百上千台机器上的容器,必须把“怎么运行一个容器”这件事标准化、轻量化、可替换。
从Docker Daemon到CRI接口:解耦是必然选择
Docker Engine(dockerd)原本既是镜像管理器,又是容器执行器,还自带网络插件和存储驱动。这种一体化设计在单机开发中很顺手,但在K8s集群里就成了瓶颈:
- 每次调用都要经过完整的Docker API栈,中间多出dockershim适配层,带来额外延迟
- Docker内置的Swarm编排能力与K8s冲突,属于冗余功能
- K8s无法直接控制底层运行时行为(比如cgroup设置、OOM策略),调度精度受限
于是Kubernetes在1.5版本引入CRI(Container Runtime Interface),定义了一组gRPC接口,只关心三件事:拉镜像、启停容器、查状态。所有具体实现(containerd、CRI-O)只要满足这个契约,就能接入集群。
containerd成为事实标准:精简、稳定、可嵌入
containerd是从Docker Engine中剥离出来的核心运行时,2017年捐给CNCF后持续迭代。它不处理镜像构建(交给buildkit)、不提供CLI(由nerdctl或kubectl间接调用),只专注一件事:可靠地运行容器。
- 直接对接runc(符合OCI规范的运行时),跳过Docker daemon中间层
- 内置快照器(snapshotter)支持OverlayFS、Btrfs等文件系统,镜像分层更高效
- 通过CRI插件暴露标准接口,Kubelet通过Unix socket与其通信,路径短、故障面小
现在主流发行版(如Ubuntu 22.04+、RHEL 9)默认安装containerd,K8s 1.24+已彻底移除dockershim,Docker Desktop底层也改用containerd作为运行时。
cgroup驱动统一:systemd正在接管资源视图
架构图里常被忽略的一环,是容器如何真正绑定到宿主机cgroup。早期Docker用cgroupfs直写/sys/fs/cgroup,但systemd早已把进程、服务、socket都纳入自己的cgroup树管理。
- 混用两种驱动会导致资源统计不一致,比如top看到内存使用低,但systemd显示cgroup内存超限触发OOM
- K8s节点推荐配置containerd使用systemd cgroup driver,让kubelet、containerd、systemd共享同一套cgroup路径(如/sys/fs/cgroup/system.slice/containerd.service)
- 对应关键配置项:[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] 中设置 SystemdCgroup = true
镜像构建与运行分离:BuildKit + OCI镜像成为新基线
过去Dockerfile构建强依赖Docker daemon,现在BuildKit作为独立构建引擎,支持并行层解析、缓存导入导出、无守护进程模式(rootless build)。生成的镜像完全符合OCI规范,可被containerd、CRI-O、Podman原生加载。
- 构建不再需要“Docker”——
buildctl build --output type=image,name=localhost:5000/app . - 运行也不再需要“Docker”——
ctr images pull localhost:5000/app && ctr run --rm localhost:5000/app test - K8s只需关注OCI镜像的拉取与解包,底层运行时可自由切换
架构演进不是功能堆砌,而是层层卸载非核心职责。Docker退为开发者工具链一环,containerd稳坐运行时中枢,systemd夯实资源底座,CRI划定边界——整条链路变得更透明、更可控、也更难出错。











