Kubernetes容器运行时:Containerd vs Docke
- 文章信息 - Author: 李俊才 (jcLee95)
Visit me at CSDN: https://jclee95.blog.csdn.net
My WebSite:http://thispage.tech/
Email: 291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/139843224
HuaWei:https://bbs.huaweicloud.com/blogs/429461
【介绍】:本文关于Kubernetes容器运行时比较:Containerd 与 Docker。

目 录
- 1. 概述
- 2. Containerd 与 Docker的关系
- 3. Kubernetes 中 Containerd 与 Docker 的区别
- 3.1 架构和调用链路
- 3.2 容器创建和网络管理
- 3.3 容器日志管理
- 3.3.1 日志落盘责任
- **Docker**
- **Containerd**
- 3.3.2 日志保留策略
- **Docker**
- **Containerd**
- 3.3.3 日志路径和格式
- **Docker**
- **Containerd**
- 3.4 容器Exec和Probe
- 3.4.1 容器Exec
- **Docker**的Exec实现
- **Containerd**的Exec实现
- 行为差异和影响
- 3.4.2 容器Probe
- **Docker**的Probe实现
- **Containerd**的Probe实现
- 行为差异和影响
- 3.4.3 对启动和停止操作的影响
- 4. 如何选择**Containerd**和**Docker**
- 5. 结论
- F. 附录
1. 概述
容器运行时(Container Runtime)是容器技术栈中的一个关键组件,它在Kubernetes中扮演着举足轻重的角色。本节将介绍容器运行时的基本概念,以及它在Kubernetes集群中所承担的主要职责。
1.1 容器运行时在Kubernetes中的作用在Kubernetes中,每个节点上都运行着一个名为Kubelet的核心组件。Kubelet负责管理节点上的Pod和容器,而这些管理操作需要通过特定的容器运行时来完成。容器运行时作为Kubelet和容器之间的桥梁,为Kubernetes提供了一系列不可或缺的功能:
-
容器生命周期管理: 容器运行时负责管理容器的整个生命周期,包括容器的创建、启动、停止和删除等操作。当Kubelet需要创建或销毁Pod时,实际的容器管理操作就由容器运行时来执行。
-
镜像管理: 容器运行时负责从镜像仓库拉取镜像,并管理节点上的本地镜像存储。当创建Pod时,容器运行时会检查所需的镜像是否已经存在,如果不存在则从指定的镜像仓库中拉取。
-
容器文件系统和网络管理: 容器运行时为每个容器提供独立的文件系统和网络命名空间。它负责准备容器的根文件系统,挂载volumes,配置容器网络,并确保不同容器之间的文件系统和网络隔离。
-
资源限制和安全隔离: 容器运行时负责为容器设置资源限制,如CPU和内存的使用限制。同时,它还要确保容器之间以及容器与宿主机之间的安全隔离,防止恶意容器破坏其他容器或者宿主机。
总之,容器运行时作为连接Kubernetes和底层容器实现的关键组件,提供了容器生命周期管理、镜像管理、存储和网络管理、资源隔离等多方面的支持,是Kubernetes能够管理和编排大规模容器集群的重要基石。
1.2 主流容器运行时介绍在Kubernetes的发展历程中,涌现出了多种容器运行时方案,它们在功能、性能和生态等方面各有特点。以下是两种最主流的容器运行时:
-
Docker: Docker是最早普及容器技术的引擎,在Kubernetes的早期版本中一度作为默认的容器运行时。Docker提供了强大的镜像构建、容器管理功能和丰富的工具生态,使得容器技术得以快速推广。然而,Docker作为一个完整的平台,其功能和体量都相对臃肿,会占用较多的系统资源。同时Docker的发展路线图与Kubernetes也不完全一致,给Kubernetes的版本迭代和适配带来了一定的挑战。
-
Containerd: Containerd是一个更加轻量级、标准化的容器运行时,它起源于Docker项目,后被剥离出
-
来成为一个独立的开源项目。Containerd专注于提供简洁、可靠、高性能的容器执行引擎,遵循行业标准如OCI(Open Container Initiative),为容器编排系统提供了更加可控、可移植的运行时选择。Containerd去除了Docker中许多面向终端用户的功能,如
docker build、docker push等,转而专注于容器执行和管理等核心功能。这种专注使得Containerd在资源占用和稳定性方面都有了显著提升,逐渐成为Kubernetes社区推荐的容器运行时实现。从Kubernetes 1.20版本开始,Containerd已经成为了默认的容器运行时。众多Kubernetes发行版和托管服务也都开始以Containerd作为首选运行时。Containerd简洁而专一的设计,以及对OCI标准的原生支持,使其成为Kubernetes这样的大规模容器编排平台的理想选择。
综上所述,容器运行时作为连接Kubernetes和实际容器实现的桥梁,其地位至关重要。Docker和Containerd代表了两种不同的技术路线:Docker功能丰富,生态完善,但略显臃肿;Containerd则专注容器核心功能,更加轻量和可控。随着Kubernetes的不断发展,以Containerd为代表的轻量级容器运行时正在成为主流选择。但Docker凭借其成熟的生态和强大的功能,在某些特定场景下仍然具有独特的价值。
在选择容器运行时时,需要综合考虑稳定性、性能、资源占用等因素,权衡不同方案的优缺点,有时甚至可以混合使用,发挥各自的长处。无论选择何种容器运行时,其可靠性和性能都将直接影响到Kubernetes集群的整体运行状况,因此做出正确的选择至关重要。
要理解Containerd和Docker的关系,我们需要先了解Containerd的起源和发展历程,以及它在Docker架构中所处的位置。本节将深入探讨Containerd与Docker之间的渊源和演变。
2.1 Containerd的由来Containerd最初是作为Docker的一个子组件而诞生的。在Docker的早期版本中,Containerd就已经存在,负责管理容器的生命周期。然而,随着Docker功能的不断丰富和版本的迭代,Containerd逐渐演变成了一个相对独立的组件。
2.1.1 从Docker的子组件到独立项目2016年,Docker公司决定将Containerd从Docker中剥离出来,使其成为一个独立的开源项目。这一决定的主要目的是让Containerd专注于提供标准化的容器运行时和管理功能,而不是与Docker的其他功能耦合在一起。
2.1.2 Containerd的定位和目标作为一个独立的项目,Containerd的目标是提供一个简单、可靠、可移植的容器运行时,供上层的容器编排系统使用。Containerd遵循行业标准,如OCI(Open Container Initiative),致力于成为Kubernetes等编排引擎的最佳运行时选择。
2.2 Containerd在Docker架构中的位置尽管Containerd已经成为一个独立的项目,但它仍然是Docker架构中不可或缺的一部分。了解Containerd在Docker中的位置,有助于我们理解二者的关系和各自的职责。
2.2.1 Docker的组件架构Docker是由多个组件构成的,主要包括:
- Docker-client:用户与Docker交互的命令行工具。
- Dockerd:Docker的核心守护进程,负责与Docker-client交互,并管理镜像、网络等资源。
- Containerd:负责管理容器的生命周期,如创建、启动、停止等。
- runc:一个轻量级的容器运行时,用于实际启动和运行容器。
在Docker的架构中,Containerd位于Dockerd和runc之间,起到了承上启下的作用:
- 对上,Containerd向Dockerd提供了管理容器生命周期的API。
- 对下,Containerd通过shim(如Containerd-shim)调用runc来实际创建和运行容器。
因此,Containerd是连接Docker上层组件和底层容器运行时的关键纽带。
2.3 Kubernetes弃用Dockershim的原因Kubernetes最初是使用Dockershim这一适配器与Docker集成的。但随着Kubernetes的发展,社区决定弃用Dockershim,转而直接与更加轻量和标准化的容器运行时(如Containerd)集成。
2.3.1 Docker版本更新带来的适配困难Docker作为一个完整的容器平台,其版本更新频率和功能变化较快。这给Kubernetes的适配工作带来了困难。每当Docker发布新版本时,Kubernetes都需要相应地更新Dockershim,以保证兼容性。
2.3.2 Kubernetes对更轻量级运行时的需求Kubernetes作为一个容器编排平台,其实并不需要Docker提供的所有功能。Kubernetes更需要的是一个简单、可靠、易于集成的容器运行时。而Containerd正好满足了这一需求。它专注于容器的核心功能,去除了许多不必要的特性,从而更加轻量和高效。
因此,Kubernetes社区决定逐步弃用Dockershim,转而直接与Containerd等更加轻量级的容器运行时集成。这一决定不仅简化了Kubernetes的架构,也提高了其性能和稳定性。
综上所述,Containerd和Docker之间有着密不可分的关系。Containerd最初是Docker的一个子组件,后来演变成了一个独立的项目。尽管如此,它仍然是Docker架构中的关键组成部分,负责管理容器的生命周期。同时,由于Containerd更加轻量和标准化,它也逐渐成为了Kubernetes等容器编排平台的首选运行时。Kubernetes社区决定弃用Dockershim,转而直接与Containerd集成,就是基于这一考虑。
3. Kubernetes 中 Containerd 与 Docker 的区别 3.1 架构和调用链路在Kubernetes中使用Containerd和Docker作为容器运行时,其内部的架构和调用链路有所不同。本节将深入分析这两种运行时在Kubernetes中的工作方式和流程。
3.1.1 Docker作为Kubernetes容器运行时当Docker作为Kubernetes的容器运行时时,其调用链路如下:
Kubelet -> Dockershim -> Dockerd -> Containerd
-
Kubelet: Kubelet是Kubernetes中每个节点上的核心组件,负责管理节点上的Pod和容器。
-
Dockershim: Dockershim是Kubernetes中的一个适配器组件,它位于Kubelet内部。Dockershim的作用是将Kubelet的容器管理请求翻译成Docker Daemon(Dockerd)能够理解的API调用。
-
Dockerd: Dockerd是Docker的核心守护进程,负责处理Dockershim的请求,并管理Docker镜像、网络和存储等资源。
-
Containerd: Containerd是一个独立的容器运行时,负责实际的容器生命周期管理,如创建、启动、停止和删除容器等操作。在这种架构下,Dockerd会将容器管理的任务委托给Containerd来执行。
可以看出,当使用Docker作为Kubernetes的容器运行时时,请求需要经过多个组件的转发和处理,调用链路相对较长。这种架构虽然能够兼容早期的Docker版本,但也引入了一些额外的复杂性和性能开销。
3.1.2 Containerd作为Kubernetes容器运行时当Containerd作为Kubernetes的容器运行时时,其调用链路如下:
Kubelet -> cri plugin -> Containerd
-
Kubelet: 与使用Docker时一样,Kubelet负责管理节点上的Pod和容器。
-
CRI Plugin: CRI(Container Runtime Interface)是Kubernetes定义的一组标准接口,用于与容器运行时进行交互。当使用Containerd时,Kubelet通过内置的CRI插件直接与Containerd通信。CRI插件将Kubelet的请求转化为Containerd能够理解的gRPC调用。
-
Containerd: Containerd直接处理来自CRI插件的请求,管理容器的生命周期,并通过shim(如Containerd-shim)启动和管理容器。
相比于使用Docker,Containerd作为Kubernetes容器运行时的调用链路更加简洁直接。Kubelet通过CRI插件直接与Containerd通信,减少了中间组件的开销。这种架构更加轻量级,组件之间的耦合度更低,从而提高了整体的稳定性和性能。
3.1.3 架构对比和优劣分析通过对比Containerd和Docker在Kubernetes中的架构和调用链路,我们可以发现以下几点区别:
-
调用链路长度: 使用Docker时,请求需要经过Dockershim、Dockerd等多个组件的转发,调用链路较长。而使用Containerd时,Kubelet通过CRI插件直接与Containerd通信,调用链路更加简洁。
-
组件复杂度: Docker作为一个完整的容器平台,包含了许多Kubernetes不需要的功能,引入了额外的复杂性。而Containerd专注于容器运行时的核心功能,组件更加精简。
-
资源占用:由于Docker包含了更多的组件和功能,因此其资源占用相对较高。Containerd作为一个轻量级的容器运行时,其资源占用更低,对系统的影响更小。
- 稳定性: 在使用Docker时,由于调用链路较长,涉及的组件较多,因此出现问题的概率相对较高。而Containerd的架构更加简洁,组件之间的耦合度更低,因此其稳定性通常更好。
- 升级和维护: Docker的版本更新频率较高,每次更新都可能引入新的特性和变化,这给Kubernetes的适配和维护工作带来了挑战。而Containerd的升级和维护相对更加容易,因为它的功能和接口更加稳定。
总的来说,Containerd作为一个专门为容器编排设计的运行时,其架构更加简洁,调用链路更短,资源占用更低,稳定性更好。这些优势使得Containerd成为Kubernetes社区推荐的容器运行时选择。
然而,这并不意味着Docker在Kubernetes中就完全没有用武之地。对于某些特定的场景,如需要使用Docker提供的特有功能(如
docker build、docker push等),或者需要与现有的Docker工作流集成时,使用Docker作为容器运行时仍然是一个可行的选择。此外,对于已经大规模使用Docker的Kubernetes集群,直接切换到Containerd可能会带来一定的迁移成本和风险。在这种情况下,可以考虑采用渐进式的迁移策略,如在新的节点或集群上使用Containerd,而在已有的节点上继续使用Docker,直到完全过渡到Containerd。
在Kubernetes中,容器的创建和网络管理是通过Pause容器和CNI插件来实现的。Pause容器为业务容器提供了网络命名空间,而CNI插件负责配置容器网络。Containerd和Docker在这两个方面有一些细微的区别。
3.2.1 Pause容器的创建在创建业务容器之前,Kubernetes会先创建一个名为Pause的特殊容器。Pause容器的主要作用是为业务容器提供网络命名空间,使同一Pod内的容器能够共享网络栈。Containerd和Docker在创建Pause容器时有一些不同:
-
Docker:当使用Docker作为容器运行时时,在创建Pause容器的过程中,Docker会自动关闭容器内的IPv6支持。具体来说,Docker会在容器的网络命名空间中将内核参数
net.ipv6.conf.all.disable_ipv6设置为1。这意味着,在默认情况下,使用Docker创建的容器只能使用IPv4。 -
Containerd:而当使用Containerd作为容器运行时时,创建Pause容器的过程不会涉及关闭IPv6的操作。Containerd创建的Pause容器默认同时启用IPv4和IPv6。
这种差异可能会导致一些网络行为上的不同。例如,在使用Containerd时,如果集群启用了IPv6,那么Pod内的容器就可以直接使用IPv6进行通信。而在使用Docker时,即使集群启用了IPv6,Pod内的容器默认也只能使用IPv4通信。
3.2.2 CNI插件的调用在Kubernetes中,容器网络的配置是通过CNI(Container Network Interface)插件来实现的。CNI定义了一组标准接口,用于配置和管理容器的网络。Kubernetes在创建Pod时,会调用CNI插件为Pod配置网络。Containerd和Docker在CNI插件的调用方式上略有不同:
-
Docker:当使用Docker作为容器运行时时,CNI插件的调用是由Kubelet内部的Dockershim来完成的。具体来说,Dockershim会根据Kubelet的配置(如
--cni-bin-dir和--cni-conf-dir参数)调用相应的CNI插件,为容器配置网络。 -
Containerd:当使用Containerd作为容器运行时时,CNI插件的调用是由Containerd内置的CRI插件来完成的。Containerd CRI插件会根据其配置文件(
config.toml)中的cni部分,调用指定的CNI插件为容器配置网络。
尽管Containerd和Docker在CNI插件调用的实现细节上有所不同,但它们最终达到的效果是一致的,即为Pod内的容器配置正确的网络。
3.2.3 网络配置的差异和影响由于Containerd和Docker在Pause容器创建和CNI插件调用方面存在一些差异,这可能会导致某些网络配置和行为上的不同。以下是一些主要的差异和影响:
-
IPv6支持:如前所述,使用Docker创建的容器默认只启用IPv4,而使用Containerd创建的容器默认同时启用IPv4和IPv6。这意味着,在启用IPv6的集群中,使用Containerd可以直接利用IPv6进行Pod内部和Pod之间的通信,而使用Docker则需要额外的配置。
-
DNS解析:由于IPv6的启用状态不同,在某些情况下,Pod内的DNS解析行为可能会有所不同。例如,如果应用程序在进行DNS解析时,倾向于使用IPv6地址,那么在使用Docker时可能会遇到一些问题,因为Docker默认禁用了IPv6。
-
网络性能:Containerd和Docker在网络性能上可能会有一些细微的差异,这主要取决于它们各自的网络栈实现和优化。一般来说,由于Containerd是为Kubernetes设计的,其网络性能可能会更好一些。但具体的性能差异需要通过基准测试来评估。
-
网络插件兼容性:尽管Containerd和Docker都支持CNI标准,但它们对某些CNI插件的兼容性可能有所不同。在选择网络插件时,需要确保其与所使用的容器运行时兼容,并进行充分的测试。
容器日志是了解容器内应用运行状况的重要途径。在Kubernetes中,容器日志的管理方式因所使用的容器运行时而有所不同。本节将重点介绍Containerd和Docker在容器日志管理方面的异同。
3.3.1 日志落盘责任容器日志的落盘,即将日志写入磁盘的过程,在Containerd和Docker中有所不同。
Docker
在使用Docker作为容器运行时时,容器日志的落盘由Docker Daemon(Dockerd)负责。具体而言:
- 容器内的应用将日志输出到stdout和stderr。
- Dockerd捕获这些日志,并将其写入宿主机的文件系统。
- 日志文件的路径通常为
/var/lib/Docker/containers/<container-id>/<container-id>-json.log。
Dockerd负责管理日志文件的生命周期,包括日志的轮转和清理等。
Containerd
当使用Containerd作为容器运行时时,容器日志的落盘由Kubelet负责。具体流程如下:
- 与Docker类似,容器内的应用将日志输出到stdout和stderr。
- Containerd捕获这些日志,并通过CRI接口将其传递给Kubelet。
- Kubelet将日志写入宿主机的文件系统,日志文件的路径通常为
/var/log/Pods/<namespace>_<Pod-name>_<Pod-uid>/<container-name>/0.log。
在这种情况下,Kubelet负责管理日志文件的生命周期,包括日志的轮转和清理等。
3.3.2 日志保留策略日志保留策略决定了容器日志在宿主机上的存储时间和占用空间。Containerd和Docker对日志保留策略的默认配置有所不同。
Docker
在Docker中,日志保留策略由Docker Daemon的配置参数控制:
--log-driver:指定日志驱动,默认为json-file。--log-opts:指定日志驱动的选项,如max-size和max-file等。
默认情况下,Docker会为每个容器保留1GB的日志,并在达到这个限制时进行轮转。可以通过调整--log-opts参数来更改这个默认行为。
Containerd
在Containerd中,日志保留策略由Kubelet的配置参数控制:
--container-log-max-size:指定单个容器日志文件的最大大小,默认为10MB。--container-log-max-files:指定单个容器最多保留的日志文件数量,默认为5个。
这意味着,在默认配置下,Kubelet为每个容器最多保留50MB的日志(10MB * 5个文件)。当日志文件达到--container-log-max-size指定的大小时,Kubelet会对其进行轮转。
需要注意的是,这些默认值可能因Kubernetes版本和发行版而有所不同。用户可以根据实际需求调整这些参数,以平衡日志保留时间和磁盘空间的占用。
3.3.3 日志路径和格式除了日志落盘责任和保留策略外,Containerd和Docker在日志路径和格式方面也有一些差异。
Docker
在Docker中,容器日志的路径和命名格式如下:
- 路径:
/var/lib/**Docker**/containers/<container-id>/<container-id>-json.log - 格式:JSON格式,每行代表一条日志记录。
Docker使用JSON格式记录日志,便于日志的解析和检索。每条日志记录包含了诸如时间戳、日志级别、容器ID等元数据信息。
Containerd
在Containerd中,容器日志的路径和命名格式如下:
- 路径:
/var/log/**Pod**s/<namespace>_<**Pod**-name>_<**Pod**-uid>/<container-name>/0.log - 格式:纯文本格式,不包含额外的元数据。
与Docker不同,Containerd采用纯文本格式记录日志,每行代表一条日志记录,不包含额外的JSON元数据。这种格式更加简洁,但在某些情况下可能不如JSON格式灵活。
需要注意的是,在Kubernetes中,可以通过配置日志代理(如Fluentd、Filebeat等)来收集和转发容器日志,并对其进行结构化处理和分析。这样可以弥补Containerd日志格式简洁的不足,提供更强大的日志管理功能。
3.4 容器Exec和Probe在Kubernetes中,容器的Exec(执行命令)和Probe(健康检查)是两个常用的功能。Containerd和Docker在实现这两个功能时略有不同,这可能会导致某些场景下的行为差异。本节将重点分析这两种容器运行时在Exec和Probe方面的区别。
3.4.1 容器Exec容器Exec是指在容器运行时执行一个命令,通常用于调试、故障排查或一些特定的操作。在Kubernetes中,可以通过kubectl exec命令在容器内执行命令。
Docker的Exec实现
在Docker中,当执行**Docker** exec命令时,Docker会在容器内创建一个新的进程来执行指定的命令。这个新进程与容器的主进程(PID 1)是独立的。Docker的Exec实现有以下特点:
- 独立进程:Exec命令会在容器内创建一个独立的进程,该进程与容器的主进程是分离的。
- 退出状态:Docker的Exec命令的退出状态取决于所执行命令的退出状态。即使容器的主进程仍在运行,只要Exec命令执行完毕并退出,Docker就认为本次Exec操作已经结束。
Containerd的Exec实现
Containerd的Exec实现与Docker略有不同。在Containerd中,Exec命令的行为更加严格,更加符合Kubernetes的预期。Containerd的Exec实现有以下特点:
- 进程组:Containerd的Exec命令会在容器内创建一个新的进程组,该进程组与容器的主进程组是同级的。
- 退出状态:Containerd的Exec命令的退出状态取决于整个进程组的退出状态。只有当Exec命令创建的所有进程都退出后,Containerd才认为本次Exec操作结束。
行为差异和影响
由于Docker和Containerd在Exec实现上的差异,在某些场景下可能会导致不同的行为:
-
长时运行的Exec命令:如果在容器内执行一个长时运行的命令(如
sleep 1000),在Docker中,由于Exec命令是独立进程,所以kubectl exec会立即返回,而容器内的sleep进程会继续运行。但在Containerd中,kubectl exec会一直等待,直到sleep进程结束。 -
Exec命令的退出状态:在Docker中,Exec命令的退出状态只取决于该命令本身的执行结果。而在Containerd中,只有当Exec命令创建的所有进程都退出后,才会返回最终的退出状态。
容器Probe是Kubernetes用于检查容器健康状态的机制,包括livenessProbe(存活探针)和readinessProbe(就绪探针)。这些探针可以通过执行命令、发送HTTP请求或检查TCP端口来判断容器的健康状态。
Docker的Probe实现
在Docker中,当Kubelet执行容器的Probe时,它会在容器内创建一个新的进程来执行指定的探测操作(如执行命令或发送请求)。Docker的Probe实现有以下特点:
- 独立进程:与Exec类似,Probe操作会在容器内创建一个独立的进程,该进程与容器的主进程是分离的。
- 退出状态:Docker的Probe进程的退出状态决定了本次探测的结果。如果进程以0状态码退出,则认为探测成功;否则认为探测失败。
Containerd的Probe实现
Containerd的Probe实现与Exec类似,也是通过创建进程组的方式来执行探测操作。Containerd的Probe实现有以下特点:
- 进程组:Containerd的Probe操作会在容器内创建一个新的进程组,该进程组与容器的主进程组是同级的。
- 退出状态:Containerd的Probe进程组的退出状态决定了本次探测的结果。只有当Probe操作创建的所有进程都退出后,才会根据最终的退出状态判断探测结果。
行为差异和影响
Docker和Containerd在Probe实现上的差异可能会导致以下行为差异:
-
Probe超时:如果容器的Probe操作设置了超时时间,在Docker中,一旦超时,Probe进程就会被强制终止,并认为本次探测失败。但在Containerd中,即使超时,Probe进程组也不会被立即终止,而是等待所有进程自然退出。这可能导致实际的探测时间超过设定的超时时间。
-
Probe退出状态:与Exec类似,在Docker中,Probe进程的退出状态只取
-
决于该进程本身的执行结果。而在Containerd中,只有当Probe操作创建的所有进程都退出后,才会根据最终的退出状态判断探测结果。
容器的启动(postStart)和停止(preStop)操作是通过Kubernetes的生命周期钩子(Lifecycle Hooks)来实现的。这些钩子可以在容器启动后或停止前执行一些自定义的操作,如数据初始化、资源清理等。
由于Docker和Containerd在Exec和Probe实现上的差异,可能会导致postStart和preStop操作的行为有所不同:
-
postStart操作:在Docker中,如果postStart操作执行完毕并退出,即使容器的主进程还没有启动,Kubernetes也会认为容器已经启动成功。但在Containerd中,只有当postStart操作创建的所有进程都退出后,Kubernetes才会继续启动容器的主进程。
-
preStop操作:在Docker中,如果preStop操作执行完毕并退出,即使容器的主进程还在运行,Kubernetes也会认为容器已经停止。但在Containerd中,只有当preStop操作创建的所有进程都退出后,Kubernetes才会继续停止容器的主进程。
这些差异可能会导致容器启动或停止时的行为不一致,特别是当postStart或preStop操作中包含长时运行的命令时。
在Kubernetes集群中选择使用Containerd还是Docker作为容器运行时,需要综合考虑多方面因素。本节将从Kubernetes对运行时的要求出发,分析Containerd和Docker各自的优势和适用场景,并给出迁移到Containerd的一些注意事项。
4.1 Kubernetes对运行时的要求Kubernetes作为一个大规模容器编排平台,对容器运行时有一些特定的要求:
-
稳定性和性能:运行时需要能够稳定、高效地管理大量容器,及时响应Kubernetes的调度和管理请求。频繁的故障或性能瓶颈会直接影响整个集群的可用性。
-
对OCI标准的支持:为了保证容器的可移植性和互操作性,Kubernetes希望运行时能够支持OCI(Open Container Initiative)标准。符合OCI标准的容器运行时可以无缝地与Kubernetes集成,降低了兼容性风险。
-
尽量简洁,不需要过多特有功能:Kubernetes主要依赖运行时提供标准化的容器管理能力,并不需要运行时提供过多的特有功能。过于复杂的运行时反而可能带来更多的维护成本和不稳定因素。
相比于Docker,Containerd在满足Kubernetes需求方面有一些明显的优势:
-
调用链更短,资源占用少,更稳定:如前文所述,使用Containerd时,Kubelet可以通过内置的CRI插件直接与Containerd通信。这种简洁的架构降低了系统复杂度,减少了潜在的故障点。同时Containerd作为一个轻量级运行时,其资源占用也更低,有利于提高节点的可用资源和稳定性。
-
支持OCI标准,面向Kubernetes设计:Containerd从一开始就致力于成为一个标准化的容器运行时。它完全支持OCI标准,并提供了兼容CRI的接口。这种与Kubernetes的契合,使得Containerd成为Kubernetes社区的优先选择。
总的来说,Containerd专注于提供Kubernetes所需的核心功能,摒弃了Docker中许多不必要的特性。这种专注使其更加轻量、稳定,并且与Kubernetes的集成更加无缝。对于大多数Kubernetes用户来说,Containerd是一个更加合适的选择。
4.3 Docker的适用场景尽管Kubernetes社区推荐使用Containerd,但这并不意味着Docker就完全没有用武之地。在某些特定场景下,Docker仍然具有独特的价值:
-
需要使用Docker API、
docker build等功能:如果你的工作流严重依赖Docker提供的一些特有功能,如docker build、docker push等,那么继续使用Docker可能是更好的选择。这些功能虽然对Kubernetes来说不是必需的,但在某些开发和CI/CD场景下还是非常有用的。 -
需要使用Docker Compose或Swarm:如果你的部分工作负载还在使用Docker Compose或Swarm,出于一致性和兼容性考虑,这部分节点可能需要继续使用Docker作为运行时。
需要注意的是,即使你决定在某些节点上继续使用Docker,也可以在其他节点上使用Containerd。Kubernetes支持混合使用多种容器运行时,你可以根据实际需求选择最合适的运行时。
4.4 迁移到 Containerd 的注意事项如果你决定将现有的Kubernetes集群从Docker迁移到Containerd,有以下几点需要注意:
-
Kubernetes版本支持:从Kubernetes 1.20版本开始,Dockershim已经被弃用,而在1.24版本中,Kubernetes已经彻底移除了对Dockershim的支持。因此,对于1.24及更高版本的集群,你必须使用其他运行时如Containerd。新建的集群建议直接使用Containerd。
-
镜像兼容性:Containerd完全兼容Docker镜像格式。这意味着你之前通过Docker构建的所有镜像,在迁移到Containerd后仍然可以继续使用,无需任何修改。
-
容器网络和日志:由于Containerd和Docker在容器网络和日志管理方面有一些细微差异,迁移后可能会遇到一些问题。例如,容器的IPv6支持状态可能会发生变化,日志的格式和路径也可能不同。你需要仔细测试并排查这些潜在问题。
Containerd和Docker作为Kubernetes的两大容器运行时选择,各有其优势和适用场景。Containerd作为一个为容器编排而生的轻量级运行时,以其简洁的架构、对标准的支持以及与Kubernetes的契合,逐渐成为社区的主推选择。Kubernetes社区持续加大对Containerd的投入,使其成为默认的容器运行时。这一趋势反映了Kubernetes对标准化、可移植性和稳定性的追求。
然而,这并不意味着Docker就失去了其价值。作为容器技术的先驱,Docker凭借其功能的丰富性和生态的成熟度,在某些特定场景下仍然不可或缺。特别是对于那些严重依赖Docker特有功能(如docker build、docker push等)或需要与现有Docker工作流集成的用户来说,继续使用Docker可能是更优的选择。
因此,在选择容器运行时时,我们需要全面权衡各种因素,如稳定性、性能、资源占用等。要充分评估自己的实际需求,考虑是否需要Docker提供的特殊功能。在某些情况下,混合使用Containerd和Docker,发挥各自的优势,可能是一个不错的折中方案。
展望未来,随着Kubernetes的不断发展和成熟,以Containerd为代表的标准化容器运行时将成为主流选择。但这并不意味着Docker会完全退出舞台,其在容器技术发展历程中的重要地位和贡献是不可磨灭的。Docker丰富的功能和强大的社区生态,在特定场景下仍将发挥重要作用。
总之,Containerd和Docker之争,反映了Kubernetes社区在追求标准化和兼顾灵活性之间的平衡。作为Kubernetes用户,我们需要根据自身的实际情况,权衡利弊,做出最适合自己的选择。无论选择何种容器运行时,最终目的都是为了更好地支撑Kubernetes,提供稳定、高效、灵活的容器编排服务。
F. 附录 F.1 命令对照表下表列出了Docker、Containerd(ctr)和Kubernetes(crictl 和 kubectl)中常用操作的对应命令:
| 操作 | Docker | Containerd (ctr) | Kubernetes (crictl) | Kubernetes (kubectl) |
|---|---|---|---|---|
| 查看运行的容器 | docker ps | ctr task ls / ctr container ls | crictl ps | kubectl get pods |
| 查看镜像 | docker images | ctr image ls | crictl images | - |
| 查看容器日志 | docker logs | - | crictl logs | kubectl logs |
| 查看容器详情 | docker inspect | ctr container info | crictl inspect | kubectl describe pod |
| 查看容器资源使用情况 | docker stats | - | crictl stats | kubectl top pod |
| 启动已有的容器 | docker start | ctr task start | crictl start | - |
| 停止运行的容器 | docker stop | ctr task kill | crictl stop | kubectl delete pod |
| 运行一个新的容器 | docker run | ctr run | - | kubectl run |
| 创建一个新的容器 | docker create | ctr container create | crictl create | kubectl create -f <pod-spec>.yaml |
| 删除容器 | docker rm | ctr container rm | crictl rm | kubectl delete pod |
| 拉取镜像 | docker pull | ctr image pull | crictl pull | - |
| 推送镜像到仓库 | docker push | ctr image push | - | - |
| 删除本地镜像 | docker rmi | ctr image rm | crictl rmi | - |
| 给镜像打标签 | docker tag | ctr image tag | - | - |
| 在容器中执行命令 | docker exec | - | crictl exec | kubectl exec |
| 导入镜像 | docker load | ctr image import | - | - |
| 导出镜像 | docker save | ctr image export | - | - |
| 构建镜像 | docker build | - | - | kubectl apply -f <build-spec>.yaml |
| 查看容器进程信息 | docker top | - | - | - |
这个表格涵盖了更多的操作,并且列出了Docker、Containerd(ctr)、Kubernetes(crictl)和Kubernetes(kubectl)中的对应命令(如果有的话)。
需要注意的是,由于Kubernetes主要关注容器的编排和管理,而不是单个容器的操作,因此有些Docker和Containerd的命令在Kubernetes(kubectl)中并没有直接的对应。这些操作通常是在创建或管理Pod时通过配置文件(如YAML)来指定的。
另外,kubectl 和 crictl 都是与 Kubernetes 相关的命令行工具,但它们的目的和使用场景有所不同:
从目的和功能看:
-
kubectl:kubectl是Kubernetes的标准命令行工具,用于管理和操作Kubernetes集群中的各种资源,如Pod、Service、Deployment、Namespace等。它通过Kubernetes API服务器与集群进行交互,提供了一组丰富的子命令和选项,用于创建、查看、更新和删除Kubernetes对象。kubectl的主要目的是管理和编排容器,而不是直接与容器运行时交互。
-
crictl:crictl是CRI(Container Runtime Interface)的命令行工具,用于与兼容CRI的容器运行时(如Containerd)进行直接交互。它提供了一组标准化的命令,用于管理和操作容器和镜像,如创建容器、启动容器、查看容器状态等。crictl的主要目的是调试和排查与容器运行时相关的问题,而不是管理Kubernetes资源。
从使用场景看:
-
kubectl:kubectl是Kubernetes用户和管理员的主要工具。它用于日常的集群管理和应用部署任务,如创建和管理Pod、Service、Deployment等,查看集群状态,更新应用配置等。kubectl适用于所有需要与Kubernetes集群交互的场景。
-
crictl:crictl主要是为开发人员和管理员提供的一个调试和排查工具。当遇到与容器运行时相关的问题时,如容器无法启动、镜像拉取失败等,可以使用crictl来直接与容器运行时交互,以诊断和解决问题。crictl适用于需要直接访问和操作容器运行时的场景。
F.2 参考资料
| 序号 | 文档名称 | 文档链接 |
|---|---|---|
| 1 | Kubernetes官方文档 - 容器运行时 | https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/ |
| 2 | Kubernetes官方文档 - 使用Containerd作为容器运行时 | https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/#containerd |
| 3 | Kubernetes官方文档 - 从Docker迁移到Containerd | https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/#migrating-from-dockershim |
| 4 | Containerd官方文档 - Kubernetes集成 | https://github.com/containerd/containerd/blob/main/docs/getting-started.md#kubernetes-integration |
| 5 | Containerd官方文档 - 与Docker的差异 | https://github.com/containerd/containerd/blob/main/docs/getting-started.md#differences-with-docker |
| 6 | Docker官方文档 - Kubernetes集成 | https://docs.docker.com/get-started/kube-deploy/ |
| 7 | Docker官方博客 - Kubernetes中的Docker和Containerd | https://www.docker.com/blog/what-is-containerd-runtime/ |
| 8 | Kubernetes弃用Dockershim的博客公告 | https://kubernetes.io/blog/2020/12/02/dockershim-faq/ |
| 9 | Kubernetes官方文档 - 弃用Dockershim的FAQ | https://kubernetes.io/zh-cn/blog/2020/12/02/dockershim-faq/ |
| 10 | CNCF官方博客 - Containerd毕业公告 | https://www.cncf.io/announcements/2019/02/28/cncf-announces-containerd-graduation/ |
| 11 | Kubernetes官方文档 - 容器运行时接口(CRI)规范 | https://github.com/kubernetes/kubernetes/blob/242a97307b34076d5d8f5bbeb154fa4d97c9ef1d/docs/devel/container-runtime-interface.md |
| 12 | OCI(Open Container Initiative)官网 | https://opencontainers.org/ |
| 13 | OCI运行时规范 | https://github.com/opencontainers/runtime-spec |
| 14 | OCI镜像规范 | https://github.com/opencontainers/image-spec |
相关文章:
Kubernetes容器运行时:Containerd vs Docke
容器化技术笔记 Kubernetes容器运行时:Containerd vs Docke - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this arti…...
【java 线程的状态】
介绍 Java 线程在运⾏的⽣命周期中的指定时刻只可能处于下⾯ 6 种不同状态的其中⼀个状态 状态名称说明NEW初始状态,线程被构建,但是还没有调用start()方法RUNNABLE运行状态,Java线程将操作系统中的就绪和运行两种状态统称为"运行中"BLOCKED阻塞状态,表示线程阻塞于…...
php加密验签
签名生成步骤(小程序端/前端): 确定参与签名的参数:选择需要参与签名的请求参数,通常包括请求的时间戳、随机数、请求的数据等。 参数排序与拼接:将所有参与签名的参数按照字母顺序排序,并拼接成…...
【Golang - 90天从新手到大师】Day06 - 数组
系列文章合集 Golang - 90天从新手到大师 数组是golang中最常用的一种数据结构,数组就是同一类型数据的有序集合 定义一个数组 格式: var name [n]type n为数组长度,n>0 且无法修改,type为数组的元素类型如: var a [2]int上面的例子定义了一个长度为2,元素类型为int的数组…...
java的有参构造方法
java的有参构造方法和无参构造方法类似,区别是构造方法名称里后面跟着一个括号,括号里是参数的定义 示例代码如下 class student4{private String name;private int age;public student4(String n,int a) {namen;agea;System.out.println("调用了…...
Vue66-vue-默认插槽
一、默认插槽需求 1-1、原本的写法: 在每个category组件中用v-show来做条件渲染,但是不方便! 1-2、默认插槽 img标签,ul标签,video标签,都是在app组件中完成解析之后,塞到category组件中的&…...
tsf-consul的使用
在腾讯云微服务平台TSF中使用Consul作为服务发现组件,通常需要遵循以下步骤: ### 1. 创建应用 首先,您需要在TSF控制台创建一个应用。在创建应用时,选择合适的业务类型、开发语言、开发框架等信息。对于使用Consul作为服务发现组件的Spring Cloud应用,您需要选择“业务应…...
【perl】基本语法 /备忘录/
分享 perl 语言学习资源 Perl 教程|极客教程 (geek-docs.com) Perl [zh] (runebook.dev) Perl 运算符 | 菜鸟教程 (runoob.com) Perl Documentation - Perldoc Browser Search the CPAN - metacpan.org 当然还有一些经典书籍,不再列举。 1、数字 1.1、数字表…...
mongodb 集群安装
整体架构图: 1. 配置域名 Server1: OS version: CentOS Linux release 8.5.2111 hostnamectl --static set-hostname mongo01 vi /etc/sysconfig/network # Created by anaconda hostnamemong01 echo "192.168.88.20 mong1 mongo01.com mongo…...
绿茶集团重启IPO:流量渐退、业绩波动,还能讲出好故事吗?
近日,绿茶集团有限公司(下称“绿茶集团”)向港交所递交上市申请,花旗、招银国际为其联席保荐人。 回望绿茶集团的上市之路,可谓有诸多坎坷。该公司于2021年3月首度向港交所发起冲击,但却将中文版招股书中的“流动负债总额”错写成…...
Git与SSH
Git Git是一种分布式版本控制系统,最初由Linus Torvalds为管理Linux内核开发而设计并开发。Git可以帮助开发团队协作管理代码,跟踪代码变更历史,并在需要时回溯到特定版本。 分布式版本控制:每个开发者都可以拥有完整的代码仓库…...
我的创作纪念日--码农阿豪
个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119qq.com] 📱…...
Git 学习笔记(超详细注释,从0到1)
Git学习笔记 1.1 关键词 Fork、pull requests、pull、fetch、push、diff、merge、commit、add、checkout 1.2 原理(看图学习) 1.3 Fork别人仓库到自己仓库中 记住2个地址 1)上游地址(upstream地址):http…...
GitLab项目组相关操作(创建项目组Group、创建项目组的项目、为项目添加成员并赋予权限)
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。 君不见,黄河之水天上来,奔流到海不复回。 君不见,高堂明镜悲白发,朝如青丝暮成雪。 ——《将…...
英语恶补ing
ing的词组都有停下来做某事的感觉了。 second hand是形容词了。 wouldnt buy这里的would是情态动词,也是助动词 助动词不能单独使用,要搭配实义动词,这样才能构成谓语 情态动词(modals)在英语中有多种作用ÿ…...
DS1339C串行实时时钟-国产兼容RS4C1339
RS4C1339串行实时时钟是一种低功耗的时钟/日期设备,具有两个可编程的一天时间报警器和一个可编程方波输出。地址和数据通过2线双向总线串行传输。时钟/日期提供秒、分钟、小时、天、日期、月份和年份信息。对于少于31天的月份,月末的日期会自动调整&…...
神经网络模型---LeNet-5
一、LeNet-5 1.定义LeNet-5模型 model models.Sequential([1.1添加一个二维卷积层,有6个过滤器,每个过滤器的尺寸是5x5。输入图像尺寸是28x28像素,具有1个颜色通道,激活函数是relu layers.Conv2D(6, (5, 5), activationrelu, input_shape…...
免费分享:1994-2020年中国各行业二氧化碳排放数据(附下载方法)
日前,国务院印发《2024—2025年节能降碳行动方案》针对重点领域进行部署,同时明确了制度标准、价格政策、资金支持、科技引领、市场化机制、全民行动等6项措施,为节能降碳提供支撑保障。1994-2020年中国各行业二氧化碳排放数据为评估环境政策…...
Qemu虚拟机在线迁移到VMware
libvirt版本:libvirt-10.0.0qemu版本:qemu-8.2.0 在生产环境中,大多数的场景是 vmware 虚拟机迁移到 qemu 环境,一般是通过关机然后导出、导入磁盘镜像来实现。 如果要将 qemu 环境虚拟机迁移到 vmware 怎么办呢?要求…...
计算机游戏因为d3dcompiler_47.dll丢失无法启动怎么办?解决只要d3dcompiler_47.dll丢失无法启动游戏软件的方法
d3dcompiler_47.dll 是一个动态链接库文件,属于 Microsoft DirectX 的一部分,主要负责编译和运行 3D 图形程序。它是支持 Direct3D 功能的核心组件,Direct3D 是一种用于编程 3D 图形的 API,广泛应用于游戏和图形密集型应用程序中。…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
