容器运行时探讨--从dockershim正式从K8s删除说起

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 2022年05月,Kubernetes 1.24正式发布,比较引人注目的就是在这个版本中正式将dockershim 组件从 kubelet 中删除。从这个版本开始,用户使用Kubernetes时需要优先选择containerd 或 CRI-O作为容器运行时。如果希望继续依赖 Docker Engine 作为容器运行时,需要cri-dockerd组件。

背景

2022年05月,Kubernetes 1.24正式发布,比较引人注目的就是在这个版本中正式将dockershim 组件从 kubelet 中删除。从这个版本开始,用户使用Kubernetes时需要优先选择containerd 或 CRI-O作为容器运行时。如果希望继续依赖 Docker Engine 作为容器运行时,需要cri-dockerd组件。

其实,早在在一年多前,Kubernetes 1.20版本就宣布对Docker的支持置为废弃(Deprecated)状态,不再演进。 Kubernetes 1.20 变更日志描述如下: "Docker support in the kubelet is now deprecated and will be removed in a future release. " 。Kubernetes弃用Docker,其实也不用过分紧张,在文章别慌: Kubernetes 和 Docker 中, 作者简介清晰的表达了观点:弃用 Docker 这个底层运行时,转而支持符合为 Kubernetes 创建的容器运行接口 Container Runtime Interface (CRI) 的运行时。 Docker 构建的镜像,将在你的集群的所有运行时中继续工作,一如既往。


那么Kubernetes为什么要移除Docker?主要原因还是Docker长期以来不支持Kubernetes主推的CRI容器运行时标准,Kubernetes需要长期维护着dockershim组件来专门适配Docker。dockershim是出现在Kubernetes发展初期、Docker如日中天阶段的产物,但是随着containerd等容器运行时的发展,Kubernetes也有了足够的理由不再维护dockershim了。

Kubernetes移除dockershim后,是不是就完全不支持Docker了?答案是否定的。目前社区里已经有一个独立于Kubernetes并且支持CRI的shim cri-dockerd(由Mirantis提供,于2019年收购Docker Enterprise部门),用户可以使用该shim实现Kubernetes对Docker对支持。

此外,由于Docker Image已经成为了各类容器运行时使用的标准镜像格式,未来很长一段时间,开发阶段使用Docker,生产环境中使用的Containerd等其他容器运行时,可能会成为一种普遍的现象。

这里我们简要介绍了一下Kubernetes移除dockershim的整个事件。同时,也提到了很多概念(CRI、dockershim、containerd等),为了更好的加深理解,本文将对容器运行时进行深入的探讨。

容器运行时发展史

为了对容器运行时有全面的认识,我们首先回顾下容器运行时的发展史。


2013年前,以Google为主导的容器虚拟化技术,例如Cgroups技术、LXC(Linux Container)、LMCTFY项目。

2013 年,Docker 项目正式发布,凭借着“Build,Ship and Run Any App, Anywhere”的理念迅速席卷天下。
2014 年,Google基于内部的 Borg 系统开源了 Kubernetes项目,用于解决大规模集群的容器部署、运行、管理等问题。随后几年,Kubernetes逐渐成为容器编排领域的标准。

2014 年,CoreOS 基于App Container Specification,实现了容器引擎 Rocket (简称 rkt)试图与 Docker 分庭抗礼。

2015年,Docker发布容器集群管理系统Docker swarm,以及配套的Docker machine、Docker Compose等工具,力图构建完善的容器编排系统,和Kubernetes展开正面竞争。

2015年,为了避免容器技术领域的分裂,Docker联合Linux基金会(Linux Foundation)推动成立了OCI(Open Container Initiative)组织,旨在制定一个容器镜像格式与运行时标准。同时,Docker公司将libcontainer模块捐给CNCF社区,作为OCI标准的实现,这就是我们常说的runc。

2015年,Cloud Native Computing Foundation(CNCF)成立,旨在“构建云原生计算 —— 一种围绕着微服务、容器和应用动态调度的、以基础设施为中心的架构,并促进其广泛使用”。

2016年,为了适应OCI标准,Docker将containerd独立拆分,并捐赠给了社区。这次拆分使得 Docker 将容器的管理功能移出 Docker 的核心引擎,并移入一个单独的守护进程(即 containerd)。

2016年,微软在 Windows Server 上为基于 Windows 的应用添加了容器支持,称之为 Windows Containers。它与 Windows Server 2016 一同发布,可以原生地在 Windows 上运行Docker容器。

2016年,自 Kubernetes 1.5开始,Container Runtime Interface(CRI)发布,通过 CRI 可以支持 kubelet 使用不同的容器运行时,而不需要重新编译。

2017 年,容器生态开始模块化、规范化。CoreOS 和 Docker 联合提议将 rkt 和 containerd 作为新项目纳入 CNCF。OCI 发布 1.0,CRI/CNI 得到广泛支持。
2017 年 - 2019 年,容器引擎技术飞速发展,新技术不断涌现。Kata Containers 社区成立,Google 开源 gVisor 代码,AWS 开源 firecracker,阿里云发布安全沙箱 1.0。

2020年 12月8日 Kubernetes 决定在 v1.20 版本之后将废弃 Docker 作为容器运行时,转而使用为 Kubernetes 创建的 Container Runtime Interface(CRI)运行时,标志着 Docker 的巅峰已经到了。
2020 年 - 202x 年,容器引擎技术升级,Kata Containers 开始 2.0 架构,阿里云发布沙箱容器 2.0....

2022年,Kubernetes 1.24 正式将dockershim 组件从 kubelet 中删除。

回顾过去10年的历史,基本上就是一段围绕着Kubernetes跟Docker的爱恨情仇,中间也催生了OCI跟CRI两个标准。接下来我们将重点介绍OCI跟CRI。

OCI与Docker的演进

提到容器运行时,不得不提到Open Container Initiative(OCI)。

Open Container Initiative(OCI)

OCI是在防止容器技术分裂的背景下提出的,制定容器镜像格式和容器运行时的正式规范(OCI Specifications),其内容主要包括OCI Runtime Spec(容器运行时规范)、OCI Image Spec(镜像格式规范)、OCI Distribution Spec(镜像分发规范)。

其中,我们熟知的runc就是Runtime Spec的一种参考实现。最早是从 Docker的 libcontainer中迁移而来的,后由Docker捐献给OCI。Docker容器也是基于runc创建的。

这样容器运行时可以从层级上进行如下划分:

  • low-level runtime(即OCI 运行时):指的是仅关注运行容器的容器运行时,调用操作系统,使用 namespace 和 cgroup 实现资源隔离和限制。例如,runc、rkt等。
  • high-level runtime:相较于low-level runtimes位于堆栈的上层,负责传输和管理容器镜像,解压镜像,并传递给low-level runtimes来运行容器。例如 containerd、cri-o等。

Docker

从 Docker 1.11 版本(2016年)开始,Docker Daemon拆分成了多个模块以适应 OCI 标准。其中,containerd 独立负责容器运行时和生命周期(如创建、启动、停止、中止、信号处理、删除等),其他一些如镜像构建、卷管理、日志等由 Docker Daemon 的其他模块处理。

上图操作容器流程可以简化为:Docker Daemon-> containerd -> containerd-shim -> runc。其中,containerd-shim是容器直接操作者。

这里之所以引入containerd-shim,是因为在创建和运行容器之后runc会退出,此时将 containerd-shim 作为容器的父进程,而不是 containerd 作为父进程,从而避免containerd挂掉时,整个宿主机上所有的容器都得退出的问题。

Container Runtime Interface(CRI)

什么是CRI

说到Kubernetes剔除dockershim事件,不得不提到CRI。首先我们先了解下CRI出现的背景:在 Kubernetes 早期(v1.5 之前),Docker 作为K8s支持唯一的容器运行时,Kubelet 是通过硬编码的方式直接调用 Docker API的;后来出现了新的容器运行时rkt,也希望整合进了Kubelet代码中。但是后来随着越来越多的容器运行时的出现,继续内嵌的方式显然已经不适合了。在这个背景下,就提出了Container Runtime Interface(CRI )标准,用于将 Kubelet 代码与具体的容器运行时的实现代码解耦

但是 Kubernetes 推出 CRI 的时候还没有现在的统治地位,各种容器运行时并不会主动去实现 CRI 接口,所以就需要通过CRI shim的方式对各种容器运行时进行适配。例如,Docker就没有打算支持CRI,原因有:Docker出现比Kubernetes早,Docker的地位比较稳固,处于强势的一方;Docker也有意推广Swarm,Swarm被视为K8s的竞品。最终,Kubelet选择了内置dockershim的方式,提供对Docker的支持。


在引入了 CRI 接口之后,Kubelet 的架构。

CRI 定义的 API 主要包括两个 gRPC 服务,ImageService 和 RuntimeService。

  • ImageService:负责拉取镜像、查看和删除镜像等。
  • RuntimeService:负责管理 Pod 和容器的生命周期,以及与容器交互的调用(exec/attach/port-forward)等。


有了CRI,kubelet可以实现对于容器运行时(例如Docker、containerd、CRI-O等)的统一管理。

Docker支持形态:

  • CRI推出初期,Docker江湖地位很高,Kubernetes为了支持Docker,在 kubelet 中内置了 dockershim。
  • 流程:kubelet(CRI Client)通过 CRI 接口调用 dockershim(CRI Server);dockershim请求 Docker Daemon去调用containerd,然后通过containerd-shim、runc去真正创建容器。

很显然,Docker的调用链路有点过长,且冗余操作较多。也正是因为这个原因,随着 CRI 的生态越来越完善,最终Kubernetes 社区在2020年7月决定开始着手移除 dockershim 了,1.24 版本已正式删除dockershim

Containerd支持形态:

  • containerd 1.0:对 CRI 的适配是通过一个单独的 CRI-Containerd 进程来完成的。独立CRI-Containerd显然是有些多余的,主要原因是,CRI初期还没有绝对的统治力,需要kubelet去适配各容器运行时。
  • containerd1.1:去掉了 CRI-Containerd,直接把适配逻辑作为插件集成到了 containerd 主进程中。


综上可见,CRI可以实现kubelet对Docker、containerd、CRI-O的统一管理。同时,Kubernetes 1.24将dockershim 组件从 kubelet 中删除后,也建议用户使用containerd 或 CRI-O。那么接下来我们将重点介绍下containerd跟CRI-O。

containerd

containerd 是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性。

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

containerd 最早是从 Docker 里分离出来,作为一个独立的开源项目,目标是提供一个更加开放、稳定的容器运行基础设施。(详见Docker部分的架构图)

从containerd提供的官方架构图可以看出 containerd 采用的也是 C/S 架构。containerd管理着容器生命周期,从镜像传输和存储到容器执行和监测,再到底层存储到网络附件等等。具体运行容器由 runc 负责,实际上只要是符合 OCI 规范的容器都可以支持。


此外,containerd 还实现了更丰富的容器接口,可以使用 ctr 工具来调用这些丰富的容器运行时接口,而不只是 CRI 接口。例如:

  • crictl:一个类似 docker 的命令行工具,用来操作 CRI 接口。
  • critest:验证 CRI 接口是否是符合预期。
  • 性能工具:测试接口性能。

CRI-O

Kubernetes 社区也做了一个专门用于 Kubernetes 的 CRI 运行时 CRI-O,可以直接兼容 CRI 和 OCI 规范。CRI-O是使用Docker作为Kubernetes运行时的轻量级替代方案,支持任何符合 OCI 标准的容器运行时。kubelet通过CRI直接与CRI-O对话,以提取一个镜像并启动较低级别的运行时(例如runc)。

CRI-O和containerd之间的一个重要区别是删除了一些不必要的Linux功能,以减少外部攻击的可能。

CRI-O是通过直接在 OCI 上包装容器接口来实现的一个 CRI 服务。对外提供的只有具体的 CRI 接口,没有类似containerd一样的ctr工具能力。

整体架构如下:

多容器运行时

随着越来越多的容器运行时的出现,不同的容器运行时也有不同的需求场景,于是就有了多容器运行时的需求,为此Kubernetes社区推出了 RuntimeClass。


目前阿里云 ACK 安全沙箱容器已经支持了多容器运行时。如下图所示,左侧是 runc 的 Pod,对应的 RuntimeClass 是 runc;右侧是runv 的Pod,对应的  RuntimeClass 是 runv。在containerd可以配置多个容器运行时。

  • runc:kube-apiserver->kubelet->cri-plugin(cri-plugin 在 containerd 的配置文件中查询 runc 对应的 Handler)-> Shim API runtime v1  -> containerd-shim(它是一个实现了 CRI 的插件)-> 创建runc容器
  • runv:kube-apiserver->kubelet->cri-plugin(cri-plugin 在 containerd 的配置文件中查询 runv 对应的 Handler)-> Shim API runtime v2  -> containerd-shim-kata-v2(它是一个实现了 CRI 的插件)-> 创建kata容器

Kubernetes日志采集支持

我们知道日志作为可观测性建设中的重要一环,可以记录详细的访问请求以及错误信息,非常利于问题的定位。Kubernetes上的应用、Kubernetes组件本身、宿主机等都会产生各类日志数据,而且伴随着Kubernetes的发展过程,中间经历了从Docker到CRI的阶段,也提高了日志采集场景的复杂度。


iLogtail作为阿里云SLS推出的可观测数据采集的基础设施,承载了阿里巴巴集团、蚂蚁、公有云上的日志、监控、Trace、事件等多种可观测数据的采集工作。在Kubernetes场景下,对Docker、containerd都有很好的支持。iLogtail一般采用Sidecar和DaemonSet两种方式部署模式,

Logtail采集业务Pod/容器的前提是需要能够访问到宿主机上的容器运行时并且可以访问到业务容器的数据。

  • 访问容器运行时:Logtail容器会将宿主机上容器运行时(Docker Engine/ContainerD)的sock挂载到容器目录中,因此Logtail容器就可以访问到这台节点的Docker Engine/ContainerD
  • 访问业务容器的数据:Logtail容器会将宿主机的根目录('/'目录)挂载到容器的/logtail_host挂载点上,因此可以通过/logtail_host目录访问其他容器的数据(前提是容器运行时中的文件系统以普通文件系统的形式存储在宿主机上,一般overlayfs即可,或者容器的日志目录挂载到宿主机上,例如以hostPath或emptyDir方式挂载)

具体的工作步骤如上图,主要分为两部分:

  • 容器发现:
  • 从容器运行时(Docker Engine/ContainerD)中获取到所有的容器及其配置信息,例如容器名、ID、挂载点、环境变量、Label等
  • 根据采集配置中的IncludeEnv、ExcludeEnv、IncludeLabel、ExcludeLabel定位需要采集的容器。
  • 容器数据采集:
  • 确定容器被采集的数据地址,包括标准输出和文件的采集地址。
  • 标准输出:容器的标准输出最终必须保存成文件才能被采集到,对于DockerEngine和ContainerD都需要配置LogDriver,配置为json-file、local即可(一般默认配置都会保存到文件,所以大部分情况不需要关心)。
  • 容器文件:容器文件为的系统为overlay时,会自动根据UpperDir查找到所有容器中的文件;但是ContainerD默认的配置为devicemapper,这种情况下必须把日志挂载到HostPath或者EmptyDir才可以查找到对应的路径。
  • 根据对应的地址采集数据,其中对于标准输出比较特殊,需要去解析标准输出文件,从而得到用户实际的标准输出。
  • 最后,根据配置的解析规则解析原始的日志,并上传到SLS。

更多详细信息,请参见文章《Kubernetes日志采集原理全方位剖析》。目前iLogtail已开源,欢迎加星关注。

总结

本文的主要内容就到此为止了,这里为大家简单总结一下:

  • 介绍 OCI及Docker的一些发展演进。
  • 介绍了CRI以及符合CRI标准的containerd跟CRI-O。
  • Kubernetes日志采集原理。

参考文档

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
14天前
|
人工智能 弹性计算 运维
ACK Edge与IDC:高效容器网络通信新突破
本文介绍如何基于ACK Edge以及高效的容器网络插件管理IDC进行容器化。
|
16天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
155 77
|
25天前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
3天前
|
存储 Kubernetes 开发者
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
Docker 是一种开源的应用容器引擎,允许开发者将应用程序及其依赖打包成可移植的镜像,并在任何支持 Docker 的平台上运行。其核心概念包括镜像、容器和仓库。镜像是只读的文件系统,容器是镜像的运行实例,仓库用于存储和分发镜像。Kubernetes(k8s)则是容器集群管理系统,提供自动化部署、扩展和维护等功能,支持服务发现、负载均衡、自动伸缩等特性。两者结合使用,可以实现高效的容器化应用管理和运维。Docker 主要用于单主机上的容器管理,而 Kubernetes 则专注于跨多主机的容器编排与调度。尽管 k8s 逐渐减少了对 Docker 作为容器运行时的支持,但 Doc
27 5
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
|
23小时前
|
Prometheus Kubernetes 监控
OpenAI故障复盘 - 阿里云容器服务与可观测产品如何保障大规模K8s集群稳定性
聚焦近日OpenAI的大规模K8s集群故障,介绍阿里云容器服务与可观测团队在大规模K8s场景下我们的建设与沉淀。以及分享对类似故障问题的应对方案:包括在K8s和Prometheus的高可用架构设计方面、事前事后的稳定性保障体系方面。
|
15天前
|
人工智能 运维 监控
阿里云ACK容器服务生产级可观测体系建设实践
本文整理自2024云栖大会冯诗淳(花名:行疾)的演讲,介绍了阿里云容器服务团队在生产级可观测体系建设方面的实践。冯诗淳详细阐述了容器化架构带来的挑战及解决方案,强调了可观测性对于构建稳健运维体系的重要性。文中提到,阿里云作为亚洲唯一蝉联全球领导者的容器管理平台,其可观测能力在多项关键评测中表现优异,支持AI、容器网络、存储等多个场景的高级容器可观测能力。此外,还介绍了阿里云容器服务在多云管理、成本优化等方面的最新进展,以及即将推出的ACK AI助手2.0,旨在通过智能引擎和专家诊断经验,简化异常数据查找,缩短故障响应时间。
阿里云ACK容器服务生产级可观测体系建设实践
|
15天前
|
运维 Kubernetes 调度
阿里云容器服务 ACK One 分布式云容器企业落地实践
阿里云容器服务ACK提供强大的产品能力,支持弹性、调度、可观测、成本治理和安全合规。针对拥有IDC或三方资源的企业,ACK One分布式云容器平台能够有效解决资源管理、多云多集群管理及边缘计算等挑战,实现云上云下统一管理,提升业务效率与稳定性。
|
25天前
|
存储 Prometheus 监控
Docker容器内进行应用调试与故障排除的方法与技巧,包括使用日志、进入容器检查、利用监控工具及检查配置等,旨在帮助用户有效应对应用部署中的挑战,确保应用稳定运行
本文深入探讨了在Docker容器内进行应用调试与故障排除的方法与技巧,包括使用日志、进入容器检查、利用监控工具及检查配置等,旨在帮助用户有效应对应用部署中的挑战,确保应用稳定运行。
32 5
|
25天前
|
开发框架 安全 开发者
Docker 是一种容器化技术,支持开发者将应用及其依赖打包成容器,在不同平台运行而无需修改。
Docker 是一种容器化技术,支持开发者将应用及其依赖打包成容器,在不同平台运行而无需修改。本文探讨了 Docker 在多平台应用构建与部署中的作用,包括环境一致性、依赖管理、快速构建等优势,以及部署流程和注意事项,展示了 Docker 如何简化开发与部署过程,提高效率和可移植性。
53 4
|
9天前
|
关系型数据库 应用服务中间件 PHP
实战~如何组织一个多容器项目docker-compose
本文介绍了如何使用Docker搭建Nginx、PHP和MySQL的环境。首先启动Nginx容器并查看IP地址,接着启动Alpine容器并安装curl测试连通性。通过`--link`方式或`docker-compose`配置文件实现服务间的通信。最后展示了Nginx配置文件和PHP代码示例,验证了各服务的正常运行。
29 3
实战~如何组织一个多容器项目docker-compose

相关产品

  • 容器服务Kubernetes版