探讨一下k8s弃用docker的原因

早在2020年k8s官方就已经发布了弃用docker的消息,且在k8s的1.24版本以后便不再维护dockershim,直接使用containerd作为容器运行时。

一、什么是容器运行时?

容器运行时(Container Runtime)是一种负责在操作系统层面创建和管理容器的软件工具或组件,主要任务包括创建和启动容器、管理容器文件系统、管理和限制资源、协助配置容器网络、保证容器与宿主机间相互隔离。

docker和containerd都是比较常用的容器运行时,而containerd究竟是什么,他与docker之间的关系又是什么?

二、什么是containerd?

在2016年12月14日,Docker公司宣布将containerd从docker 中分离,并由开源社区独立发展、运营;containerd是docker的基础组件之一,相较于docker来说更为纯粹,它更专注于镜像管理和容器执行,具有更小的资源占用,更快的启动时间,以及更好的性能表现。

主要任务包括:

• 管理容器的生命周期(从创建容器到销毁容器)
• 拉取/推送容器镜像
• 存储管理(管理镜像及容器数据的存储)
• 调用 runC 运行容器(与 runC 等容器运行时交互)
• 管理容器网络接口及网络ctr:containerd 的命令行客户端。

既然docker和containerd都曾作为k8s的容器运行时,那我们就详细了解一下k8s运行的原理。

三、k8s是如何管理容器?

探讨一下k8s弃用docker的原因

k8s调用容器运行时接口(CRI)去操作容器运行时,容器运行时遵循 OCI 规范,并通过 runc 来实现与操作系统内核的交互,以此完成容器的创建、运行、销毁等工作;

CRI:k8s提供的插件接口,是k8s和容器运行时之间通讯的主要协议;其运行时会从k8s获取gRPC请求,再根据OCI规范来创建json配置;

OCI:全称为Open Container Initiative,是一个轻量级,开放的治理结构,在 Linux 基金会的支持下成立,致力于围绕容器格式和运行时创建开放的行业标准。

k8s提出CRI时候,docker刚拆出containerd,此时CPI与docker的调用完全不兼容;为了兼容当时已经很成熟的docker,k8s提出了一个折中的方案,称为docker shim

四、什么是docker shim?

docker shim是k8s与docker两者间的适配器,也成为“垫片”;

k8s会通过CRI接口调用docker shim,docker shim作为Server接受来自于CRI的请求,docker shim接受到请求后,再发给docker的dockerd去调用containerd ;

此时在容器退出状态被 docker收集之前,shim 会一直存在,也会一直占用资源。

五、为什么弃用docker而选择contained;

所以很明显,使用docker作为k8s的容器运行时的话,k8s的调用链为:

k8s-->CRI-->docker shim-->dockerd-->containerd

而如果直接使用containerd作为容器运行时的话,那调用链可以简化为:

k8s-->CRI-->containerd

可以看到,两个调用链的最终效果是完全一致的,但是第二条调用链因为去除了docker shim和dockerd,不仅性能提高,而且因为链路变短,资源占用也会变小,同时也会更为稳定;

从2018年k8s 1.10与containerd1.1集成的相关测试数据中可以看出,对比于docker,containerd1.1作为容器启动时,延迟降低了20%左右,CPU 使用率降低了 68%,内存使用率降低了 12%,这是一个很可观的数据;

再者,k8s使用的只有docker的部分功能,多余的功能比如说网络或者存储卷这些很有可能会带来更多的安全隐患,所以弃用contained而选择docker是一个很明智的选择。