近日 K8s 官方称最先将在 1.23版本弃用 docker 做为容器运行时,并在博客中强调可使用如 containerd 等 CRI 运行时来代替 docker。本文会作详细解读,并介绍 docker 与 containerd 的关系,以及为何 containerd 是更好的选择。这里先回答下TKE用户关心的问题:咱们的集群该怎么办?node
Docker support in the kubelet is now deprecated and will be removed in a future release. The kubelet uses a module called "dockershim" which implements CRI support for Docker and it has seen maintenance issues in the Kubernetes community. We encourage you to evaluate moving to a container runtime that is a full-fledged implementation of CRI (v1alpha1 or v1 compliant) as they become available. (#94624, @dims) [SIG Node]git
K8s 在 1.20的 change log 中提到 K8s 将于 1.20版本开始逐步放弃对 Docker 的支持。在 K8s 的官方博客中也提到具体的声明和一些 FAQ。github
在博客中提到 K8s 将在 1.20版本中添加不推荐使用 docker 的信息,且最先将于 1.23版本中把 dockershim 从 kubelet 中移除,届时用户将没法使用 docker 做为 K8s 集群的运行时,不过经过 docker 构建的镜像在没有 docker 的 K8s 集群中依然可使用。docker
本次改动主要内容是准备删除 kubelet 中的 dockershim,固然这种作法也是符合预期的。在早期 rkt 和 docker 争霸时,kubelet 中须要维护两坨代码分别来适配 docker 和 rkt ,这使得 kubelet 每次发布新功能都须要考虑对运行时组件的适配问题,严重拖慢了新版本发布速度。另外虚拟化已是一个广泛的需求,若是出现了类型的运行时,SIG-Node 小组可能还须要把和新运行时适配的代码添加到 kubelet 中。这种作法并非长久之计,因而在 2016 年,SIG-Node提出了容器操做接口 CRI(Container Runtime Interface)。 CRI 是对容器操做的一组抽象,只要每种容器运行时都实现这组接口,kubelet 就能经过这组接口来适配全部的运行时。但 Docker 当时并无(也不打算)实现这组接口, kubelet 只能在内部维护一个称之为“dockershim”组件,这个组件充当了 docker 的 CRI 转接器,kubelet 在建立容器时经过 CRI 接口调用 dockershim ,而 dockershim 在经过 http 请求把请求交给 docker 。因而 kubelet 的架构变成下图这样:
在使用实现了 CRI 接口的组件做为容器运行时的状况下,kubelet 建立容器的调用链如图中红色箭头所示,kubelet 中的 ContainerManager 能够直接经过 CRI 调用到容器运行时,这过程当中只须要一次 grpc 请求;而在使用 docker 时,ContainerManager 会走图中蓝色的调用链, CRI 的请求经过 unix:///var/run/dockershim.sock 流向 dockershim,dockershim 作转换后把请求转发给 docker,至于为何 docker 后面还有个 containerd 稍后会讲到。在 kubelet 中实现 docker 的转接器原本就是一种不优雅的实现,这种作法让调用链变长且不稳定性,还给 kubelet 的维护添加了额外工做,把这部份内容从 kubelet 删掉就是时间问题了。json
If you’re an end-user of Kubernetes, not a whole lot will be changing for you. This doesn’t mean the death of Docker, and it doesn’t mean you can’t, or shouldn’t, use Docker as a development tool anymore. Docker is still a useful tool for building containers, and the images that result from running
docker build
can still run in your Kubernetes cluster.api
消息一出,你们最关心的事情应该就是弃用 docker 后到底会产生什么影响?安全
官方的答复是:Don't Panic!随后又重点解释了几个你们最关心的问题,咱们来分析下官方提到的这些方面:网络
正常的 K8s 用户不会有任何影响架构
是的,生产环境中高版本的集群只须要把运行时从 docker 切换到其余的 runtime(如 containerd)便可。containerd 是 docker 中的一个底层组件,主要负责维护容器的生命周期,跟随 docker 经历了长期考验。同时 2019年初就从 CNCF 毕业,能够单独做为容器运行时用在集群中。TKE 也早在 2019 年就已经提供了 containerd 做为运行时选项,所以把 runtime 从 docker 转换到 containerd 是一个基本无痛的过程。CRI-O 是另外一个常被说起的运行时组件,由 redhat 提供,比 containerd 更加轻量级,不过和 docker 的区别较大,可能转换时会有一些不一样之处。运维
开发环境中经过docker build
构建出来的镜像依然能够在集群中使用
镜像一直是容器生态的一大优点,虽然人们老是把镜像称之为“docker镜像”,但镜像早就成为了一种规范了。具体规范能够参考image-spec。在任何地方只要构建出符合 Image Spec 的镜像,就能够拿到其余符合 Image Spec 的容器运行时上运行。
在 Pod 中使用 DinD(Docker in Docker)的用户会受到影响
有些使用者会把 docker 的 socket (/run/docker.sock)挂载到 Pod 中,并在 Pod 中调用 docker 的 api 构建镜像或建立编译容器等,官方在这里的建议是使用 Kaniko、Img 或 Buildah。咱们能够经过把 docker daemon 做为 DaemonSet 或者给想要使用 docker 的 Pod 添加一个 docker daemon 的 sidecar 的方式在任意运行时中使用 DinD 的方案。TKE 也专门为在 containerd 集群中使用 DinD 提供了方案,详见 在containerd中使用DinD。
因此 containerd 究竟是个啥?和 docker 又是什么关系?可能有些同窗看到博客后会发出这样的疑问,接下来就给同窗们讲解下 containerd 和 docker 的渊源。
2016年,docker 把负责容器生命周期的模块拆分出来,并将其捐赠给了社区,也就是如今的 containerd。docker 拆分后结构以下图所示(固然 docker 公司还在 docker 中添加了部分编排的代码)。
在咱们调用 docker 命令建立容器后,docker daemon 会经过 Image 模块下载镜像并保存到 Graph Driver 模块中,以后经过 client 调用containerd 建立并运行容器。咱们在使用 docker 建立容器时可能须要使用--volume
给容器添加持久化存储;还有可能经过--network
链接咱们用 docker 命令建立的几个容器,固然,这些功能是 docker 中的 Storage 模块和 Networking 模块提供给咱们的。但 K8s 提供了更强的卷挂载能力和集群级别的网络能力,在集群中 kubelet 只会使用到 docker 提供的镜像下载和容器管理功能,而编排、网络、存储等功能都不会用到。下图中能够看出当前的模式下各模块的调用链,同时图中被红框标注出的几个模块就是 kubelet 建立 Pod 时所依赖的几个运行时的模块。
containerd 被捐赠给CNCF社区后,社区给其添加了镜像管理模块和 CRI 模块,这样 containerd 不仅能够管理容器的生命周期,还能够直接做为 K8s 的运行时使用。因而 containerd 在 2019年2月从 CNCF 社区毕业,正式进入生产环境。下图中能看出以 containerd 做为容器运行时,能够给 kubelet 带来建立 Pod 所需的所有功能,同时还获得了更纯粹的功能模块以及更短的调用链。
从上面的对比能够看出从 containerd 被捐赠给社区开始,就一直以成为简单、稳定且可靠的容器运行时为目标;而 docker 则是但愿能成为一个完整的产品。官方文档中也提到了这一点,docker 为了给用户更好的交互和使用体验以及更多的功能,提供了不少开发人员所须要的特性,同时为了给 swarm 作基础,提供了网络和卷的功能。而这些功能其实都是是 K8s 用不上的;containerd 则相反,仅提供了 kubelet 建立 Pod 所须要的基础功能,固然这换来的就是更高的鲁棒性以及更好的性能。在必定程度上讲,即便在 kubelet 1.23 版本以后 docker 提供了 CRI 接口,containerd 仍然是更好的选择。
固然如今有诸多的 CRI 实现者,比较主要的除了 containerd 还有 CRI-O。CRI-O 是主要由 Red Hat 员工开发的 CRI 运行时,彻底和 docker 没有关系,所以从 docker 迁移过来可能会比较困难。无疑 containerd 才是 docker 被抛弃后的 CRI 运行时的最佳人选,对于开发同窗来讲整个迁移过程应该是无感知的,不过对于部分运维同窗可能会比较在乎部署和运行中细节上的差别。接下来咱们重点介绍下在 K8s 中使用 containerd 和 docker 的几处区别。
对比项 | Docker | Containerd |
---|---|---|
存储路径 | 若是 docker 做为 K8s 容器运行时,容器日志的落盘将由 docker 来完成,保存在相似/var/lib/docker/containers/$CONTAINERID 目录下。kubelet 会在 /var/log/pods 和 /var/log/containers 下面创建软连接,指向 /var/lib/docker/containers/$CONTAINERID 该目录下的容器日志文件。 | 若是 Containerd 做为 K8s 容器运行时, 容器日志的落盘由 kubelet 来完成,保存至 /var/log/pods/$CONTAINER_NAME 目录下,同时在 /var/log/containers 目录下建立软连接,指向日志文件。 |
配置参数 | 在 docker 配置文件中指定:"log-driver": "json-file","log-opts": {"max-size": "100m","max-file": "5"} | 方法一:在 kubelet 参数中指定:--container-log-max-files=5 --container-log-max-size="100Mi" 方法二:在 KubeletConfiguration 中指定: "containerLogMaxSize": "100Mi", "containerLogMaxFiles": 5, |
把容器日志保存到数据盘 | 把数据盘挂载到 “data-root”(缺省是 /var/lib/docker)便可。 | 建立一个软连接 /var/log/pods 指向数据盘挂载点下的某个目录。在 TKE 中选择“将容器和镜像存储在数据盘”,会自动建立软连接 /var/log/pods。 |
cni 配置方式的区别
在使用 docker 时,kubelet 中的 dockershim 负责调用 cni 插件,而 containerd 的场景中 containerd 中内置的 containerd-cri 插件负责调用 cni,所以关于 cni 的配置文件须要放在 containerd 的配置文件中(/etc/containerd/containerd.toml):
[plugins.cri.cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d"
stream 服务的区别
说明:
Kubectl exec/logs 等命令须要在 apiserver 跟容器运行时之间创建流转发通道。
如何在 containerd 中使用并配置 Stream 服务?
Docker API 自己提供 stream 服务,kubelet 内部的 docker-shim 会经过 docker API 作流转发。而containerd 的 stream 服务须要单独配置:
[plugins.cri] stream_server_address = "127.0.0.1" stream_server_port = "0" enable_tls_streaming = false [plugins.cri] stream_server_address = "127.0.0.1" stream_server_port = "0" enable_tls_streaming = false
containerd 的 stream 服务在 K8s 不一样版本运行时场景下配置不一样。
从 2019年5月份开始,TKE就开始支持把 containerd 做为容器运行时选项之一。随着TKE逐步在 containerd 集群中支持日志收集服务和 GPU 能力,2020年 9月份 containerd 在 TKE 也摘掉了 Beta 版本的标签,能够正式用于生产环境中了。在长期使用中,咱们也发现了一些 containerd 的问题而且及时进行了修复,如:
想要在TKE集群中使用 containerd 做为运行时有三种方式:
在建立集群时,选择 1.12.4 及以上版本的 K8s 后,选择 containerd 为运行时组件便可
在已有 docker 集群中,经过建立运行时为 containerd 的节点池来建立一部分 containerd 节点(新建节点池 > 更多设置 > 运行时组件)
在已有 docker 集群中,修改集群或者节点池的"运行时组件"属性为"containerd"
注意: 后两种方式会形成同一集群中 docker 节点与 containerd 节点共存,若是有使用 Docker in Docker, 或者其余依赖节点上 docker daemon 与 docker.sock 的业务,须要提早采起措施来避免产生问题,例如经过按节点标签调度,保证这类业务调度到 docker 节点;或者采用如前文所述在 containerd 集群运行 Docker in Docker 的方案。
现阶段关于 containerd 和 docker 选择问题能够查看这里。
[1] Don't Panic: Kubernetes and Docker
[2] Dockershim FAQ
[3] Dockershim Removal Kubernetes Enhancement Proposal
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!