关注微信公众号《云原生CTO》更多云原生干货等你来探索

云原生CTO专注于云原生领域kubernetes、istio、serverless、prometheus、golang、rust核心技术二次开发课程打造及技术经验分享,为你云原生之旅保驾护航!


专注于 ​​云原生技术​​ 分享

提供优质 ​​云原生开发​​ 视频技术培训

​面试技巧​​,及技术疑难问题 ​​解答​



Kubernetes 容器运行时接口gRPC的运用_服务器


云原生技术分享不仅仅局限于​​Go​​​、​​Rust​​​、​​Python​​​、​​Istio​​​、​​containerd​​​、​​CoreDNS​​​、​​Envoy​​​、​​etcd​​​、​​Fluentd​​​、​​Harbor​​​、​​Helm​​​、​​Jaeger​​​、​​Kubernetes​​​、​​Open​​​ ​​Policy​​​ ​​Agent​​​、​​Prometheus​​​、​​Rook​​​、​​TiKV​​​、​​TUF​​​、​​Vitess​​​、​​Argo​​​、​​Buildpacks​​​、​​CloudEvents​​​、​​CNI​​​、​​Contour​​​、​​Cortex​​​、​​CRI-O​​​、​​Falco​​​、​​Flux​​​、​​gRPC​​​、​​KubeEdge​​​、​​Linkerd​​​、​​NATS​​​、​​Notary​​​、​​OpenTracing​​​、​​Operator​​​ ​​Framework​​​、​​SPIFFE​​​、​​SPIRE​​​ 和 ​​Thanos​​等


                                    Kubernetes 容器运行时接口gRPC的运用_客户端_02


Kubernetes 容器运行时接口gRPC的运用

              Kubernetes 容器运行时接口gRPC的运用_客户端_03


我们知道远程过程调用​​gRPC​​​ 已经是​​CNCF​​​孵化项目之一了,并且由​​google​​​进行开发,而对于它在​​kubernetes​​​源码内部的运用,可能大部分没有对​​kubernetes​​​源码深入理解的人一无所知,但是这没有关系,这次我们会通过这篇文章来介绍关于这个了不起的​​CNCF​​​孵化的开源项目​​gRPC​​​,并通过容器运行时接口理解它的运用之处,另外我们正在开展关于​​kubernetes​​​源码二次开发及​​kubernetes operator​​​二次开发的系列开发课,所谓目的希望可以帮助大家更深入理解关于​​kubernetes​​​的内部结构并展开​​k8s​​二次开发的技能,感兴趣请查看我另一篇博客​​https://blog.51cto.com/u_14143894/4289356​


什么是 gRPC API

​gRPC​​已成为实现需要大规模快速运行的分布式软件系统的重要技术。简而言之,​​gRPC​​ 是一种​​API​​ 框架,它允许互联网上一个位置的程序将数据传递到互联网上另一个位置的另一个程序中的不同函数进行处理。而其他 ​​API​​框架(例如 ​​REST​​)通常使用基于文本的格式(例如​​JSON​​或​​XML​​)将数据从客户端传递到服务器并再次返回, 在 ​​gRPC​​ 下,数据以二进制格式在客户端和服务器端目标函数之间传递。​​gRPC​​ 有效载荷的二进制性质是其声誉比替代方法更快的原因之一。使用 ​​gRPC​​的程序可以在纳秒内执行,而不是使用基于文本的数据时典型的毫秒时间。

自​​2015​​ 年 ​​2​​ 月 ​​Google​​将规范作为开源发布以来,​​gRPC​​ 引起了开发社区的极大兴趣

Kubernetes 容器运行时接口gRPC的运用_服务器_04

根据 ​​Google Trends​​,自 ​​2015​​ 年发布以来,人们对 ​​gRPC​​ 的兴趣越来越大

大大小小的公司都在使用 ​​gRPC​​,其中包括​​Slack​​、​​Microsoft​​、​​Condé Nast​​、​​Netflix​​和​​Cisco​​等知名品牌。

我们相信 ​​gRPC​​ 是 ​​IT​​ 领域的永久固定装置。事实上,鉴于上述趋势,完全有理由认为该技术的采用将继续增长,尤其是在需要超快系统以满足其关键任务需求的企业中。

但是,在我们详细介绍 ​​gRPC​​ 和 ​​Kubernetes CRI​​之前,我们需要回答许多第一次来到 ​​gRPC​​ 的人提出的问题:为什么我们在前端没有看到那么多 ​​gRPC​​?这是一个很多人都问过的问题,非常值得回答。那么让我们开始。

gRPC 在哪里?

自 ​​2015​​ 年作为开源项目发布以来,​​gRPC​​ 在大大小小的企业中都得到了发展。然而,尽管 ​​gRPC​​ 作为服务器端技术广受欢迎,但在面向公众的 ​​API​​ 中却鲜有存在。这主要是由于两个原因。首先,​​gRPC​​ 依赖​​HTTP/2​​作为其传输协议。虽然主要的客户端浏览器自 ​​2015​​ 年以来都支持​​HTTP/2​​,但截至 ​​2020​​ 年 ​​7​​ 月,互联网上只有不到一半的网站支持服务器端的协议。在客户端和​​Web​​服务器之间使用 ​​gRPC​​的吸引力尚不存在。

面向公众的 ​​gRPC​​采纳缓慢的第二个原因是,使用基于特定 ​​gRPC​​ 的​​API​​的客户端需要访问服务器使用的相同架构定义。(给定 ​​gRPC API​​ 的架构定义存储在 ​​protobuf​​ 文件中。)

与使用 ​​HTTP/1.1​​ 的 ​​API​​ 格式(例如 ​​REST​​)相比,必须共享一个通用的 ​​protobuf​​ 文件是一个重要的限制,它要求消费客户端对 ​​API​​ 提供的数据结构没有任何预知。使用 ​​REST​​,您只需调用一个​​URL​​,一些数据就会以自描述数据格式返回,例如​​JSON​​、​​XML​​或 ​​YAML​​。

简而言之,​​gRPC​​ 的复杂性使得采用标准、商业网站和公共 ​​API​​ 具有挑战性。但是,该技术在服务器端蓬勃发展。

正如为​​GraphQL​​​发布工具和服务器的领先公司之一​​Apollo GraphQL​​​ 的首席技术官​​Matt Debergalis​​所说:


"许多客户在 ​​gRPC​​​ 之上构建数据图。在典型的公司中,您现在拥有数百个服务,而 ​​gRPC​​​ 是这些微服务 ​​API​​ 的最佳技术,因为它非常专注;它非常高效。它是专为“数据中心内部”用例而设计,但它不是连接到应用程序的正确技术。


正因为如此,​​gRPC​​ 确实被大量使用,但在大多数情况下,它是隐藏在公众视野之外的。它用于促进服务器端后端服务之间闪电般快速、高效的通信,并且经常用于数据中心资源自动伸缩以响应实时波动的负载的情况。

而且,​​gRPC​​​ 如何在现实世界中使用的主要例子之一是 ​​Kubernetes​​​ 容器运行时接口 (​​K8S CRI​​​);这种技术实际上是这种自动缩放的同义词。​​Kubernetes​​​ 的关键特性之一是容器编排。​​K8S CRI​​​ 是在 ​​Kubernetes​​​ 下管理容器的关键组件。而且,​​gRPC​​ 已融入编排技术的结构中。让我们来看看。


Kubernetes:在容器运行时接口中使用 gRPC

为了了解 ​​gRPC​​ 如何用作容器运行时接口 (​​CRI)​​ 的通信机制,您需要对 ​​Kubernetes​​ 的工作方式有一个高层次的了解,尤其是容器在其架构中所扮演的角色。

​Kubernetes​​​ 是一种服务管理和容器编排技术,旨在支持以​​Web​​​规模运行的分布式应用程序。​​Kubernetes​​​ 架构背后的基本逻辑是,应用程序或 ​​API​​​ 的功能在 ​​Kubernetes​​​ 中由称为服务的资源表示。​​service​​​是网络上应用程序的抽象。给定​​service​​​表示的实际逻辑驻留在另一个称为​​pod​​ 的抽象资源中。


了解 Kubernetes 服务和 Pod

下面的图 ​​1​​​显示了一个应用程序中存在的三个服务的示例。一项服务提供访问功能。另一个提供目录信息,第三个提供购买功能。这些服务中的每一个都可以通过​​IP​​​地址或​​DNS​​​名称在网络上进行识别。因此,使用该应用程序的消费者将相应地调用网络上的服务。但是,该服务没有自己的功能。相反,服务的功能由驻留在服务绑定到的一个或多个 ​​pod​​ 中的逻辑提供。


Kubernetes 容器运行时接口gRPC的运用_服务器端_05

如上所述,​​pod​​是一种抽象资源。​​Pod​​是用于托管 ​​Linux​​ 容器的组织单元。容器是一种用于封装和隔离执行编程逻辑的进程的机制。(参见下面的图 2。)

Kubernetes 容器运行时接口gRPC的运用_服务器端_06

在容器中运行的进程示例包括​​Web​​​ 服务器、消息代理、数据库和其他类型的可执行二进制文件。一个 ​​Pod​​​ 可以托管一个或多个容器,其中每个容器的功能都是独一无二的。换句话说,有一个 ​​pod​​​可以同时承载 ​​Web​​​ 服务器容器和数据库容器。但是,请注意,配置 ​​pod​​​ 不仅仅是包含随机数量的容器来托管。定义具有多个容器的 ​​Pod​​​ 的结构是一项复杂的工作,需要有实施 ​​Kubernetes​​架构的经验。


要知道的重要一点是:在 ​​Kubernetes​​​ 中,服务代表网络的功能。该功能驻留在 ​​pod​​​ 中。给定 ​​pod​​​中功能的实现在 ​​pod​​ 中托管的容器中执行。


这将我们带到了容器。容器不会神奇地出现在 ​​Kubernetes​​​中。它们需要被制造出来,它们需要以一种短暂的方式被制造出来。​​Kubernetes​​ 是一种动态技术。它可以上下扩展其资源以满足当前的需求。这包括按需创建和销毁容器。


保证容器的状态

​Kubernetes​​ 中有一个抽象资源,称为部署。部署的工作是确保所有应该在给定 ​​Kubernetes​​ 部署中运行的容器确实在运行。这很重要,因为 ​​Kubernetes​​ 保证为集群定义的状态将始终保持不变。

状态保证是 ​​Kubernetes​​ 的一个非常强大的特性。此外,如上所述,它需要对容器管理进行大量控制。

这就是容器运行时接口发挥作用的地方。虽然 ​​Kubernetes​​​ 负责将服务绑定到 ​​pod​​​,并保证应该运行的 ​​pod​​​确实正在运行,但容器运行时执行实际制作​​pod​​所需容器的工作。


容器实现的机制

在我们进入容器运行时以及 ​​gRPC​​ 在容器实现过程中扮演的角色之前,了解容器实现背后的机制很有用。

在 ​​Kubernetes​​​ 中,虚拟机称为节点。​​Kubernetes​​​ 集群由一个控制器节点组成,该节点控制一组组成工作节点中的活动。简而言之,控制器节点是​​master​​​,工人节点做​​worker​​。(参见下面的图 3。)


Kubernetes 容器运行时接口gRPC的运用_服务器_07Kubernetes 容器运行时接口gRPC的运用_服务器端_08


控制器节点与工作节点协调的活动之一是创建和销毁与​​Pod​​​ 关联的​​Linux​​容器。

​Kubernetes​​ 集群中的每个工作节点都有一个名为​​kubelet​​的代理。您可以将 ​​kubelet​​ 视为节点的工头。它接受来自控制器平面的命令在其节点上做一些工作,然后确保工作完成。​​kubelet​​ 的工作之一是在其工作节点上创建和销毁容器。

​kubelet​​它会告诉容器运行时接口 (​​CRI​​) 来完成这项工作。

Kubernetes 容器运行时接口gRPC的运用_服务器端_09

在每个 ​​Kubernetes​​工作节点中运行的 ​​kubelet​​实例告诉 ​​CRI​​ 创建容器以响应来自运行在 ​​Kubernetes​​ 控制器节点上的 ​​API​​ 服务器的通知

gRPC 和 CRI

​kubelet​​ 告诉 ​​CRI​​ 做什么的方式是与嵌入在 ​​CRI​​ 中的 ​​gRPC​​ 服务器交互。(参见下面的图 5。)

Kubernetes 容器运行时接口gRPC的运用_应用程序_10

​kubelet​​ 使用 ​​gRPC​​ 与容器运行时接口交互以在工作节点上创建和销毁容器

当需要在节点上创建或销毁容器时,​​kubelet​​ 会向运行在节点 ​​CRI​​ 实例上的 ​​gRPC​​服务器发送消息以执行操作,然后 ​​CRI​​ 与安装在工作节点上的容器运行时引擎交互以执行操作

例如,当 ​​kubelet​​​ 想要创建一个容器时,它会使用其 ​​gRPC​​​ 客户端向托管在 ​​CRI​​​ 组件上的​​RPC​​​(远程过程调用)函数​​CreateContainer()​​​发送一条​​CreateContainerRequest​​​消息,​​CreateContainer​​​功能和​​CreateContainerRequest​​示于下面清单1所示。


// CreateContainer creates a new container in specified PodSandbox
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}

message CreateContainerRequest {
// ID of the PodSandbox in which the container should be created.
string pod_sandbox_id = 1;
// Config of the container.
ContainerConfig config = 2;
// Config of the PodSandbox. This is the same config that was passed
// to RunPodSandboxRequest to create the PodSandbox. It is passed again
// here just for easy reference. The PodSandboxConfig is immutable and
// remains the same throughout the lifetime of the pod.
PodSandboxConfig sandbox_config = 3;
}
用于使用 ​​Kubernetes​​ 容器运行时接口创建容器的 ​​gRPC​​函数和消息类型

反过来,​​CRI​​ 将创建请求发送到安装在节点上的实际容器运行时。容器运行时创建容器。

​Kubernetes​​ 允许您从节点上的各种容器运行时安装一个。您可以安装久经考验的 ​​Docker​​ 运行时,但也可以安装其他运行时,例如,​​containerd、rkt​​或​​cri-o​​,(选择最适合给定 ​​Kubernetes​​ 安装的容器运行时在自定义集群时提供了额外的灵活性。)

容器创建完成后,​​CRI​​​ 将返回一个​​CreateContainerResponse​​​消息,如 ​​protobuf​​​ 文件中定义的那样,该消息由 ​​gRPC​​​ 客户端和服务器共享。​​CreateContainerResponse​​的定义如下清单 2 所示。


message CreateContainerResponse {
// ID of the created container.
string container_id = 1;
}
​CRI gRPC​​ 服务器返回一条 ​​CreateContainerResponse​​ 消息,其中包含所创建容器的唯一标识符。

创建和销毁容器只是从容器运行时接口执行的两个活动。还有其他一些,例如停止容器、再次启动它、列出 ​​pod​​ 中的容器以及更新容器的配置信息,仅举几例。


了解CRI protobuf文件的细节

您可以在此处查看定义在容器运行时接口上运行的 ​​gRPC​​服务器支持的类型和功能的 ​​protobuf​​ 文件。​​protobuf​​ 文件是用于促进运行在 ​​kubelet​​上的 ​​gRPC​​ 客户端和运行在​​CRI​​ 上的 ​​gRPC​​服务器的通信的类型和函数的公共引用。当您查看​​protobuf​​文件中定义的函数时,您将清楚地了解 ​​CRI​​ 做了多少工作。

protobuf: ​​https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1alpha2/api.proto​

​gRPC​​ 驱动 ​​kubelet​​ 和 ​​CRI​​ 之间发生的所有消息交换。请记住,​​kubelet​​ 和​​CRI​​ 之间的消息交换需要以闪电般的速度进行,有时需要几纳秒。以 ​​Web​​规模运行的典型 ​​Kubernetes​​ 集群可能有数万个正在运行的容器,它们在数十个甚至数百个节点之间运行。因此,速度和效率在通信管道中至关重要。

在现实世界中使用 ​​gRPC​​​ 时,​​Kubernetes​​​ 是 ​​800 磅​​​的大猩猩。​​Kubernetes​​​和 ​​gRPC​​​ 都起源于 ​​Google​​​,因此这两种技术在技术领域都应该显得很重要并且很容易出现是很自然的。但是,正如本文开头提到的,采用率正在稳步增长。还有许多其他公司在他们的技术堆栈中使用 ​​gRPC​​ 。


而到现在​​gRPC​​ 快速、高效和可靠我们可以看出它被大量人去使用也是有目共睹的事实。