如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
可观测可视化 Grafana 版,10个用户账号 1个月
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: 本文分享了 OpenKruise 中一些功能的用法,以及它如何与 Kubelet 和 CRI 合作。如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限。

作者:王思宇,阿里云技术专家,OpenKruise 社区负责人


通常情况下,人们只能使用普通旧数据作为 Kubernetes 中最小的操作单元。一些公司在他们的集群中入侵了 Kubelet 的代码,以便他们可以对容器做更多的事情。然而,为运行时扩展操作确实是一种错误的方法,因为它不利于开源和社区的合作。现在,云原生计算基金会沙箱项目之一 OpenKruise 提供了高级功能,可以在每个原始 Kubernetes 集群中操作容器运行时。在本次演讲中,我们将介绍 OpenKruise 中一些功能的用法,以及它如何与 Kubelet 和 CRI 合作。


本次分享主要分为以下几个部分,首先我们介绍在 Kubernetes 中,针对于对容器 runtime 的操作限制有哪些,也就是说我们在 Kubernetes 中,它的机制限制了我们哪些操作,其实是对 controller runtime 是做不到的;第二点是 OpenKruise 是怎样拓展对 controller runtime 的这些操作;第三点是我们简单做一个 demo,我们如何通过 OpenKruise 来实现这些操作的;第四点是简单介绍一下我们后续的一些规划。


Kubernetes 中针对容器运行时的操作有哪些限制?


Kubernetes 中的容器运行时


图片1.png


如上图所示,这是一个 Kubernetes 的基本结构,它的结构在每个节点( Node)上,其实是 Kubelet 在 API server 里面收到它的。比如 Pod 的变化,当 Kubelet 收到一个 Pod 创建之后,通过 CRI(Container Runtime Interface) , CNI 以及类似的公共接口(例如 CSI)来调用底层真正的接口实现者去完成操作。对于容器运行时来说,是通过 CRI 接口调用底层真正的 Runtime 运行时来完成对容器的创建和启动镜像拉取这些操作。


其中 CRI 是 Kubernetes1.5 之后加入的一个新功能,由协议缓冲区和 gRPC API 组成,提供了一个明确定义的抽象层,它的目的是对于 Kubelet 能屏蔽底下 Runtime 实现的细节而只显示所需的接口。(https://github.com/kubernetes/cri-api


在 Kubernetes1.5 之前,Kubelet 与 Docker 是相耦合的,Kubelet 其实是引入了 Docker 的 client,由它们直接对 Docker 操作。有了 CRI ,对于 Kubelet 来说就不用关心底层真正的 Runtime 实现是什么,而只需要调用这层接口,接口背后的实现可能是 Containerd-d ,可能是 CRI-O,也可能是 Docker。


CRI 的职责是对容器运行时以及对镜像做相关的管理,包括对容器的启停操作,对 Sandbox 容器的操作,容器 States 的数据采集,以及镜像的拉取和查询等操作。因此,CRI 提供了比较完善的容器接口,如下图。


2.png


Kubernetes 中容器运行时的操作限制


Kubernetes API 并没有提供对容器运行时的接口操作,它唯一提供的是对 v1 版本的 Pod 操作(Pod API CRUD,Pod Subresources API)。除了 Pod 创建和更新之外,唯一能跟 Runtime 做比较相对应操作的是 Exec subresource 和 Log subresource。


Kubernetes 的 API 层面限制了用户只能创建或删除 Pod ,除此之外,里面的容器只能做 Exec, Log 这样的操作。在 Kubernetes 接口层面,用户无法进行比如拉取镜像、重启容器等操作。image.gif


3.png


那有没有可能去拓展这个 API 呢?


我们发现 Kubelet 目前没有提供任何 hook 解决 plugin 的这个操作,来让外层能去动态拓展 Kubelet 所做的事情。( Kubelet 的接口是不提供这样的插件机制的)那是否可以加入一个与 Kubelet 类似的新组件,可以连接到 CRI API,来拓展 Kubernetes 容器进行时的操作呢?


我们同样会调用 CRI 这一层,比如它可以拉镜像,可以重启容器,它的上层也可以接收一个对于 Kubernetes API 上定义的一个 CRD 资源,这个 CRD 资源定义了让用户能够声明对 CRI 接口做一些操作。比如它可以定义指定用户去拉镜像,去重启容器,可以做更多的事情。


这种方式是我们能想到的,对于这个 Kubernetes 容器运行时 operations 的拓展思路。


4.png


OpenKruise 是什么?


OpenKruise 概念


Openkruise 是 Kubernetes 的一个拓展套件,它弥补了 Kubernetes 很多功能不足,例如对于应用工作负载(应用部署发布相关功能)的不足,对于 container runtime 操作的不足等。它可以配合原生 Kubernetes 使用,并为管理应用容器、sidecar、镜像分发等方面提供更加强大和高效的能力。


2020 年 11 月,OpenKruise 作为 Sandbox 项目加入 CNCF。


5.png


Openkruise 本身并不是一个 PasS 平台。但 PasS 平台可以通过利用 Openkruise 提供的拓展能力更好的管理,运维云原生的应用。感兴趣的朋友可以通过以下网址了解更多 OpenKruise 相关信息。


Github:

https://github.com/openkruise/kruise


WebSite:

https://openkruise.io


OpenKruise 的功能


OpenKruise 是基于 CRD 的拓展,其功能大致可分为五部分:


(1)应用工作负载:包括针对无状态应用,有状态应用的灰度发布、流量控制、它的原地升级等相关功能;

(2)Sidecar 容器管理:提供更多增强的独立定义以及独立部署;

(3)应用多分区管理(Multi-domain management):一个应用如果要部署在多个分区,会进行打散和分片的管理。

(4)应用可用性防护:保护云原生应用在 Kubernetes 上运行时的高可用性;

(5)拓展增强操作:通过这种方式来实现对 container runtime(运行时)增强的操作能力。其中拓展增强操作是本文主要介绍的功能,后续我们会详细展开。


6.png

OpenKruise 功能图


OpenKruise 的架构


7.png

如图所示,OpenKruise 主要分成中心端(Kruise-manager)和节点端(Node)两个组件。中心端的 Kruise-manager 包含 controllers 和 webhooks ,通过 Kruise-manager 中心端的角色和每个节点上 kruise-daemon 功能结合,可以完成很多 Kubernetes 本身不提供的能力。Kruise-daemon 是用来避免对 Kubelet 做改造,通过拓展的方式对 CRI runtime 进行操作。


Runtime 的拓展功能


Runtime 有三个核心拓展功能。


原地升级功能


原地升级是一种可以避免删除、新建 Pod 的升级镜像能力。


8.png


如上图所示:第一部分并不是直接通过 kruise-daemon 拓展,而是利用 Kubelet 的原生机制,叫做原地升级。


如何理解原地升级?我们举一个简单的例子:比如原来有一个 pod-a ,此时的 pod-a 是通过 deployment 的或者 Openkruise 的 cloneset 扩容出来的。如果我们想要升级 app容器的镜像版本,比如从 Fv1 升级到 Fv2,正常情况下大家使用 development 部署是采用 Recreatte Update 也就是重建 Pod 升级,重建完成后我们会看到 Pod 的名字,Pod UID, (镜像也会升级为 V2) 很大程度上都会发生改变。


再看后面两者,前者 Pod 的名字和 UID 一定会发生变化,因为它已经不是同一个 Pod 的对象了。相对于我们这次介绍的原地升级,Pod 的对象其实还是原来的对象,Pod 的名字,Pod UID 都不变。其次,Pod 所在节点的 IP 也都不变,唯一变化的是镜像从 v1 级到了 v2,由于 Pod 节点没有发生任何变化,Pod 对象就不需要经过调度器重新调度,IP 分配,volume 分配,挂载这些耗时也都消除掉了,因此一个很明显好处就是节省了调度的耗时。


大家都知道,当应用镜像从 v1 升到 v2 的过程中,可能只是最顶上的 layers 发生了变化,底下绝大部分的这个 base 镜像,公共 layers 是没有发生变化的。
当我们在同一个节点上面做原地升级的时候,可以复用原有 v1 镜像的大部分 layers ,只用下载小部分的 layers 镜像。


在升级 app 容器的过程中,Pod 中的其他容器。如  sidecar 容器,是一直正常运行的,没有受到影响。反过来说,当我们升级 sidecar 容器时,容器也是正常运行的。这样可以很大程度上避免在升级一个旁路(比如运维)容器的过程中,也要对业务能力造成影响。


1.1 优势

  • 节省操作耗时,包括:Pod 调度、IP 分配、volume 分配、挂载等;
  • 复用大部分镜像层;
  • 当一个容器进行升级时,不会对 Pod 中的其它容器造成影响;


1.2 工作原理
原地升级的原理可以简单理解为 Kubelet 在创建每个容器时,会为容器计算一个 hash 值,当上层修改了比如 app 容器的 image 之后,Kubelet 就认为容器的 hash 值发生了变化。当 Kubelet 发现 Pod spec 中 app 容器的 hash 值和实际的,如 container d 对应的 hash 值不一致时,就会把旧的 app 容器停掉,用新的镜像再重建新的 app 容器,从而实现容器的原地升级的能力。


9.png


容器重启功能image.gif


10.png


容器重启的功能是很多业务,包括运维平台都很依赖的功能。大家可能会问,在 Kubernetes 中,一个 Pod 既然是无状态的,那么想重启时就直接删除 Pod,再新建一个 Pod 不就可以了吗?


这是当然可以的,但对于业务来说可能还存在很多 debug 场景,并不只是重建一个新的 Pod 就可以,而是要从原地把容器进行重启,相当于把里面的业务进程重启。比如想保留一些 volume 中的数据,一些网络、堆栈信息等,这些场景都导致业务需要有 Kubernetes Pod 的容器原地重启能力。


Kubernetes 原生是不具备容器重启能力的,对于 Kubernetes 来说,如果想要重启容器,只能手动进入容器,把容器里的应用进程杀掉,这时当容器退出,Kubernetes 会再把它拉起。当然这种方式其实都比较 hack 的这么一种方式, Openkruise 所提供的容器重启能力对于 API 来说,只需要创建一个 CR。


CR 里写的东西很明确,name spacesl 只需定义跟 pod 在同一个 name spacesl 里,其中 name 是自定义的名字,通过指定需要重启的 Pod 是哪个,需要重启 pod 出来哪些 containers,当定义了这些信息之后,提交 CR,当 kruise 收到 CR ,kruise - manager 会先经过 webhooks,对它注入一些信息,接下来 kruise-daemon 拿到 CR 会根据 CR 中定义的信息(比如会找到对应 Pod 的容器)执行 preStop hook,再通过 CRI 接口,通过 EXTC 执行 preStop,当 preStop 执行完成之后再调用这个 CRI 的 stop 。


其实这个停止方式和本身 Kubelet 在删除 Pod 时对容器的停止方式是一致的。当 kruise-daemon 对旧容器,比如对零号的 app(app_0)容器停掉之后,Kubelet 感知到 app 容器停掉了,接着就会新建一个 F1 容器并把它拉起,通过这种方式来实现优雅的容器原地重启能力。
代码示例:


apiVersion: apps.kruise.io/v1alpha1
kind: ContainerRecreateRequest
metadata:
namespace: pod-namespace
name: xxx
spec:
podName: pod-name
containers:
- name: app
strategy:
# ...
activeDeadlineSeconds: 300
ttlSecondsAfterFinished: 1800
status:
containerRecreateStates:
- name: app
phase: Succeeded
phase: Completed
# ..


镜像预热功能


提前在节点上包括新建的 Node 上预热,就可以大幅度减少后续 Pod 扩容的耗时。


11.png


从上图我们可以看到,对于上层用户来说 Openkruise 提供了一个 CRD 叫 ImagePullJob,用户可以定义需要预热哪个镜像,也可以选择性的配置 selector(selector 可以是节点的标签选择器也可以根据 Pod 进行选择),以上都可以在 Pod 所在节点上进行预热。


当用户建立 ImagePullJob 后,对于 kruise 内部逻辑来说,kruise 会把 ImagePullJob 拆分到每个 node 对应 node image 的 CR 上,当同步上去后,节点上的 kruise-daemon 会拿到这个节点所对应node image 的 CR ,在节点上预热 CR 中定义的多个镜像。


换句话说,每个节点所对应的 node image 中的镜像列表,其实就表示了上层所有 ImagePullJob 指定在这个节点上要拉取镜像全集。kruise-daemon 底层拿到 node image 后,相当于也是调用 CRI 的 Pod image 接口来完成镜像的预热。
代码示例:


apiVersion: apps.kruise.io/v1alpha1
kind: ImagePullJob
metadata:
name: test-job
spec:
image: nginx:latest
parallelism: 10
selector:
# ...
podSelector:
# ...
completionPolicy:
# ...


未来项目规划


2021 年 12 月,OpenKruise 完成了首个正式版本 v1.0 的发布,使云原生应用自动化达到新的高峰。OpenKruise 从 2019 年发布 0.1 版本到现在已经有 2 年多的时间,已有超过 70 位贡献者参与贡献,star 数量已经超过 3000。2022,我们将推进  OpenKruise 成为 CNCF  Incubation 项目,推动云原生应用自动化领域进一步成熟。


使用用户:

  • 阿里巴巴集团, 蚂蚁集团, 斗鱼TV, 申通, Boss 直聘
  • 杭银消费, 万翼科技, 多点, Bringg, 佐疆科技
  • Lyft, 携程, 享住智慧, VIPKID, 掌门 1 对 1
  • 小红书, 比心, 永辉科技中心, 跟谁学, 哈啰出行
  • Spectro Cloud, 艾佳生活, Arkane Systems, 滴普科技, 火花思维
  • OPPO, 苏宁, 欢聚时代, 汇量科技, 深圳凤凰木网络有限公司
  • 小米, 网易,美团金融, Shopee, LinkedIn
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
12天前
|
存储 Kubernetes Cloud Native
探索Python编程的奥秘云原生时代的容器编排:Kubernetes入门与实践
【8月更文挑战第30天】本文以浅显易懂的方式,探讨了Python编程的核心概念和技巧。从基础语法到高级特性,再到实际应用案例,逐步引导读者深入理解Python编程的精髓。通过本文的学习,读者将能够掌握Python编程的基本技能,并激发进一步探索的兴趣。
27 13
|
12天前
|
Kubernetes Cloud Native Docker
云原生入门:从容器到Kubernetes的旅程
【8月更文挑战第31天】云原生技术正改变着应用的开发、部署和运维方式。本文将带你走进云原生的世界,从容器的基础开始,探索Docker和Kubernetes如何助力现代软件开发与运维。你将学会如何使用Docker创建和管理容器,以及如何通过Kubernetes进行集群管理,实现服务的自动化部署、扩展和管理。准备好让你的应用在云端自由翱翔了吗?让我们启航!
|
11天前
|
Kubernetes Cloud Native 应用服务中间件
云原生入门:Kubernetes 和容器化技术的实践之旅
【8月更文挑战第31天】 在这篇文章中,我们将踏上一场探索云原生世界的旅程。我们将从基础的容器化技术讲起,逐步深入到Kubernetes这个强大的容器编排工具。文章会通过一个实际的例子,带领大家了解如何将一个简单的应用容器化并在Kubernetes集群上运行起来。无论你是云原生领域的新手,还是希望巩固知识的开发者,这篇文章都会为你提供一次实操的机会,让你对云原生有一个更加直观的认识。
|
12天前
|
运维 Kubernetes Cloud Native
云原生技术入门:从容器到Kubernetes的探索之旅
【8月更文挑战第30天】在数字时代的浪潮中,云计算已成为推动创新的重要力量。本文旨在通过浅显易懂的语言,为初学者揭开云原生技术的神秘面纱,从容器化技术的基础出发,逐步深入到Kubernetes集群管理的实际应用。我们将一起见证代码如何转化为可在云端无缝运行的服务,体验技术变革带来的无限可能。
|
13天前
|
Kubernetes Cloud Native 网络安全
云原生入门指南:Kubernetes和容器化技术云计算与网络安全:技术融合的新篇章
【8月更文挑战第30天】在云计算的浪潮中,云原生技术如Kubernetes已成为现代软件部署的核心。本文将引导读者理解云原生的基本概念,探索Kubernetes如何管理容器化应用,并展示如何通过实践加深理解。
|
14天前
|
Cloud Native 持续交付 Docker
云原生之旅:Docker容器化实战指南
【8月更文挑战第29天】本文将引领你进入云原生技术的世界,以Docker容器化为切入点,深入浅出地介绍如何利用Docker进行应用的打包、部署及管理。我们将通过实际代码示例,一步步展示Docker镜像的构建过程,以及如何运行和管理这些容器。无论你是初学者还是有一定经验的开发者,都能从中获得宝贵的知识和实操经验。
|
9天前
|
NoSQL 关系型数据库 Redis
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
mall在linux环境下的部署(基于Docker容器),docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongodb、minio详细教程,拉取镜像、运行容器
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
|
9天前
|
应用服务中间件 nginx Docker
Docker同一台宿主机容器通信-通过容器名称互联
本文详细介绍了如何通过容器名称实现同一宿主机上容器间的互联,并提供了实战案例。首先,文章解释了容器间通过自定义名称访问的原理,随后演示了创建并连接Tomcat与Nginx容器的具体步骤。此外,还讨论了配置中可能出现的问题及解决方案,包括避免硬编码IP地址和使用自定义容器别名来增强系统的灵活性与可维护性。通过这些实践,展示了如何高效地配置容器间通信,确保服务稳定可靠。
16 1
Docker同一台宿主机容器通信-通过容器名称互联
|
6天前
|
Cloud Native 持续交付 Docker
云原生技术实践:Docker容器化部署教程
【9月更文挑战第4天】本文将引导你了解如何利用Docker这一云原生技术的核心工具,实现应用的容器化部署。文章不仅提供了详细的步骤和代码示例,还深入探讨了云原生技术背后的哲学,帮助你理解为何容器化在现代软件开发中变得如此重要,并指导你如何在实际操作中运用这些知识。
|
9天前
|
存储 Unix 虚拟化
Docker容器简介
Docker是一种轻量级的虚拟化技术,它通过容器化应用,提高了硬件资源利用率,简化了应用的部署、运输和运行,且与虚拟机相比,具有更快的交付速度和更低的资源消耗。
24 2

相关产品

  • 容器服务Kubernetes版