引言:容器世界的格局变迁
2020年,Kubernetes社区宣布将弃用对Docker的原生支持,这一决定在容器生态圈引发了巨大震动。作为容器革命的先驱,Docker曾与Kubernetes有过"蜜月期",但技术演进的洪流最终改变了这一格局。本文将深入剖析这一决策背后的技术动因、实施路径及其对整个云原生生态的影响。
一、历史背景:Kubernetes与Docker的共生关系
1.1 早期的技术依赖(2014-2016)
- Kubernetes最初将Docker作为唯一支持的容器运行时
- Docker提供完整的容器解决方案:
- 容器引擎(dockerd)
- 镜像格式(OCI标准)
- 镜像仓库(Docker Hub)
- 客户端工具(docker CLI)
1.2 CRI的诞生(2016)
随着容器生态多元化,Kubernetes推出容器运行时接口(CRI):
service RuntimeService {rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}// ...
}
CRI接口定义的核心方法(简化版)
这一抽象层使Kubernetes可以支持多种运行时:
二、技术痛点:为什么必须弃用Docker?
2.1 架构冗余问题
Docker在Kubernetes中的调用链存在严重的架构冗余:
Kubelet → dockershim → dockerd → containerd → runc
对比直接使用containerd:
Kubelet → containerd → runc
- 性能损耗:额外调用链增加10-15%延迟
- 资源开销:dockerd进程占用额外100MB内存/节点
2.2 维护成本高昂
组件 | 代码行数 | 维护团队 |
---|---|---|
dockershim | 40,000+ | Kubernetes |
containerd | 20,000+ | CNCF |
CRI-O | 15,000+ | Red Hat |
dockershim成为Kubernetes代码库中最复杂的组件之一
2.3 标准兼容性冲突
- Docker未实现CRI接口
- Kubernetes被迫维护dockershim适配层
- Docker的版本迭代常导致兼容性问题
2.4 安全边界模糊
- dockerd拥有比containerd更大的攻击面
- 历史漏洞统计:
- dockerd相关CVE:32个(2018-2020)
- containerd相关CVE:9个(同期)
三、弃用路线图:平稳过渡的技术方案
3.1 阶段式弃用计划
Kubernetes版本 | 时间 | 状态 |
---|---|---|
1.20 | 2020年12月 | 开始输出弃用警告 |
1.23 | 2021年12月 | dockershim代码标记为废弃 |
1.24 | 2022年4月 | 正式移除dockershim |
3.2 替代方案架构
方案1:迁移到containerd(推荐)
方案2:通过cri-dockerd适配器
// cri-dockerd工作原理
func ServeCRI(socket string) {for {req := ReceiveCRIRequest()resp := ConvertToDockerAPI(req)SendResponse(resp)}
}
四、技术影响:开发者需要知道的关键变化
4.1 不变的部分
- 镜像格式:仍使用OCI标准镜像
- 容器规范:不变(cgroups/namespaces)
- 构建工具:docker build仍可使用
4.2 需要改变的部分
操作 | Docker时代 | 后Docker时代 |
---|---|---|
查看容器 | docker ps | crictl ps |
查看日志 | docker logs | crictl logs |
执行命令 | docker exec | crictl exec |
检查容器 | docker inspect | crictl inspect |
节点运行时 | docker://1.2.3 | containerd://1.4.6 |
4.3 性能对比
指标 | Docker + dockershim | containerd (直接CRI) |
---|---|---|
Pod启动延迟 | 650ms | 520ms |
内存占用 | 150MB | 50MB |
CPU利用率 | 8% | 3% |
冷启动并发量 | 32 pods/sec | 48 pods/sec |
基于Kubernetes 1.24的测试数据(节点配置:4vCPU/8GB)
五、迁移实践:从Docker到containerd
5.1 节点迁移步骤(Ubuntu)
# 1. 驱逐节点
kubectl drain <node-name> --ignore-daemonsets# 2. 卸载Docker
sudo apt remove docker-ce docker-ce-cli# 3. 安装containerd
sudo apt install containerd# 4. 生成默认配置
sudo containerd config default > /etc/containerd/config.toml# 5. 启用systemd cgroup驱动
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml# 6. 重启服务
sudo systemctl restart containerd# 7. 修改kubelet配置
echo 'KUBELET_EXTRA_ARGS="--container-runtime=remote \--container-runtime-endpoint=unix:///run/containerd/containerd.sock"' \| sudo tee /etc/default/kubelet# 8. 重启kubelet
sudo systemctl restart kubelet# 9. 取消驱逐
kubectl uncordon <node-name>
5.2 验证迁移成功
$ kubectl get node <node-name> -o jsonpath='{.status.nodeInfo.containerRuntimeVersion}'
containerd://1.6.21
六、生态影响:容器运行时的新格局
6.1 主流运行时对比
特性 | containerd | CRI-O | cri-dockerd |
---|---|---|---|
维护组织 | CNCF | Red Hat | Mirantis |
架构复杂度 | 低 | 中 | 高 |
Kubernetes集成 | 原生支持 | 原生支持 | 适配器 |
安全沙箱支持 | 是(Firecracker) | 是(Kata) | 有限 |
生产就绪度 | ★★★★★ | ★★★★☆ | ★★★☆☆ |
6.2 云厂商采用情况
- AWS EKS:默认containerd(2022年起)
- Google GKE:containerd作为首选
- Azure AKS:containerd运行时
- 阿里云ACK:containerd/Docker可选
结论:云原生生态的成熟之路
Kubernetes弃用Docker不是简单的技术替代,而是云原生生态成熟的必然结果:
- 解耦趋势:从单体架构到模块化设计
- 关注点分离:运行时与编排层各司其职
- 性能驱动:精简堆栈提升效率
- 安全优先:缩小攻击面
“Docker的革命性在于它让容器技术普及化,而Kubernetes的进步在于它让容器生态专业化。” — Kubernetes联合创始人Joe Beda
此次变革标志着云原生技术栈进入新阶段:
- 容器运行时:containerd/CRI-O成为新标准
- 容器构建:BuildKit等专业化工具兴起
- 安全沙箱:Kata/gVisor填补安全空白
未来属于更加开放、专业化的云原生组件生态,而Docker作为容器革命的先驱,将继续在开发者体验层面发挥重要作用。
附录:延伸阅读
- Kubernetes官方迁移指南
- containerd与Docker架构对比
- CRI标准规范