gRPC系列:实际场景中的grpc,kubernetes容器运行时接口
本文翻译Bob Reselman
的发表的文章,译者能力有限,翻译不当处请见谅。
在本系列的前几期中,我们研究了导致创建gRPC的历史事件以及使用gRPC进行编程的细节。我们讨论了gRPC规范的关键概念。我们看了我们为该系列特别创建的演示应用程序。并且,我们研究了如何使用gRPC提供的协议protoc自动生成工具,以多种编程语言创建样板代码,以加快gRPC的开发速度。我们还讨论了在gRPC下编程时如何静态地和动态地绑定到protobuf。此外,我们在Katacoda的交互式学习环境中创建了许多课程 支持我们在介绍性文章中介绍的概念和实践。
介绍了了解什么是gRPC以及其工作原理所需的基础知识之后,我们现在将分几部分介绍如何在实际场景中使用gRPC。在本期中,我们将研究Kubernetes在其容器运行时接口(CRI)技术中如何使用gRPC。
gRPC使用场景?
自2015年以开源项目形式发布以来,gRPC在大小企业中使用均有增长。然而,尽管gRPC作为服务器端技术广受欢迎,但它在面向公众的API中却很少出现。这主要是由于两个原因。首先,gRPC依赖HTTP / 2作为其传输协议。尽管自2015年以来主要的客户端浏览器都支持HTTP / 2,但截至2020年7月,Internet上不到一半的网站支持服务器端的协议。在客户端和Web服务器之间使用gRPC的吸引力还不存在。
之所以无法采用面向公众的gRPC的第二个原因是,使用基于gRPC的特定API的客户端需要访问服务器使用的相同架构定义。(给定gRPC API的架构定义存储在protobuf文件中。)
与使用HTTP / 1.1的REST之类的API格式相比,必须共享一个通用的protobuf文件是一个重要的约束,REST格式使用HTTP / 1.1,并且要求使用客户端不了解该API提供的数据结构。使用REST,您只需调用URL,然后以自描述数据格式(例如JSON,XML或YAML)返回一些数据。
简而言之,gRPC的复杂性使其成为标准,商业网站和公共API的挑战。但是,该技术在服务器端正在蓬勃发展。正如Apollo GraphQL的首席技术官Matt Debergalis所说,这是为GraphQL发布工具和服务器的领先公司之一,
“许多客户都在gRPC之上构建数据图。在一家典型的公司中,您现在拥有数百种服务,而gRPC是针对这些微服务的API的最佳技术,因为它专业且高效。为“数据中心内部”用例而设计,但这不是连接到应用程序的正确技术。
所以你值得使用它。gRPC确实使用了很多,但是在大多数情况下,它是不公开的。它用于促进服务器端后端服务之间的快速,高效的通讯,并且经常用于数据中心资源根据实时变化的负载自动按比例缩放的场景。
而且,在实际场景中如何使用gRPC的主要示例之一是Kubernetes容器运行时接口(K8S CRI);实际上就是这种自动缩放的代名词的技术。Kubernetes的关键功能之一是容器编排。K8S CRI是在Kubernetes下管理容器的关键组件。而且,gRPC被编入业务流程技术的架构中。让我们来看看。
Kubernetes:在容器运行时接口中使用gRPC
为了了解如何将gRPC用作容器运行时接口(CRI)的通信机制,您需要对Kubernetes的工作方式有一个较深的了解,尤其是关于容器在其体系结构中所扮演的角色。
Kubernetes是一种服务管理和容器编排技术,旨在支持以Web规模运行的分布式应用程序。Kubernetes架构背后的基本逻辑是,在Kubernetes中,应用程序或API的功能由名称为service的资源表示。service是对网络中应用程序的抽象。给定服务表示的实际逻辑位于另一个称为pod的抽象资源中。
了解Kubernetes服务和Pod
图1:在Kubernetes中,应用程序逻辑位于由服务在网络上表示的Pod中
如上所述,pods是一种抽象资源。pods是用于托管Linux容器的组织单位。容器是一种用于封装和隔离执行编程逻辑的进程的机制。(请参见下面的图2。)
图2:pods是一个抽象的组织单元,用于托管一个或多个Linux容器
在容器中运行的进程的示例包括Web服务器,消息代理,数据库和其他类型的可执行二进制文件。容器可以容纳一个或多个容器,其中每个容器的功能都是唯一的。换句话说,很有可能拥有一个同时承载Web服务器容器和数据库容器的Pod。但是,请注意,配置Pod并不仅仅是包含随机数量的容器来托管。定义具有多个容器的Pod的结构是一项复杂的工作,需要具有使用Kubernetes架构的经验。
要知道的重要一点是:在Kubernetes中,service代表网络的功能。该功能位于pods中。给定容器中功能的实现是在容器中托管的container中执行的。
这将我们带入了容器。容器不会因魔术而出现在Kubernetes中。它们必须以短暂的方式制造。Kubernetes是一种动态技术。它可以上下扩展其资源以满足当前的需求。这包括根据需要创建和销毁容器。
保证容器的状态
Kubernetes中有一个称为deployment的抽象资源。deployment的任务是确保应该在指定的Kubernetes部署中运行的所有容器确实在运行。这很重要,因为Kubernetes保证将始终保持为集群定义的状态。
状态保证是Kubernetes的一个非常强大的功能。同样,如上所述,它需要对容器管理进行大量控制。
这是容器运行时接口起作用的地方。Kubernetes负责将服务绑定到Pod,并保证应该运行的Pod确实在运行,但是容器运行时接口才真正完成Pod所需的容器的工作。
容器实现的机制
在进入容器运行时接口以及gRPC在容器实现过程中扮演的角色之前,了解容器实现背后的机制很有必要。
在Kubernetes中,虚拟机称为节点。Kubernetes集群由一个控制器节点组成,该控制器节点控制一系列组成工作节点中的活动。简而言之,controller node是老板,而worker node则负责工作。(请参见下面的图3。)
图3:Kubernetes集群的组织层次结构
了解有关Controller-Kubernetes下的Worker架构的更多信息
要总体了解Kubernetes控制平面的详细信息以及API服务器如何在Kubernetes的Controller节点中工作,请阅读ProgrammableWeb上的本文。
控制器节点与辅助节点协调的活动之一是创建和销毁与Pod相关联的Linux容器。
Kubernetes集群中的每个工作程序节点都有一个名为kubelet的代理。您可以将kubelet视为节点的领班。它需要来自控制器平面的命令才能在其节点上进行一些工作,然后确保已完成工作。kubelet的工作之一是在其工作节点上创建和销毁容器。
为什么节点直接与容器一起使用?我以为pod是容器的父级。
pods是绑定到服务的逻辑组织单位。该服务表示网络上的应用程序逻辑。pods为服务提供逻辑。
确实,pods是作为容器父级的组织单位,但是创建和销毁容器的实际工作是由容器所在的工作程序节点完成的。
类似地,您可以将节点视为家具工厂。有许多工人(container)在许多工作台(pod)工作。一个工作台可能正在做椅子,另一把桌子。在工厂的前门是一个负责执行订单的员工。该“订单填充器”知道每个工作台的位置及其制造的产品。您可以将订单填充器视为Kubernetes service。当客户来到工厂并要求椅子时,订单填写者会呼叫工作台,该工作台为客户制作椅子并获得椅子。
但是有一个转折。尽管将劳动者分配到了特定的工作台(pods),但该工作台并未雇用任何劳动者。而是由工厂的领班来雇用和分配工人到工作台上。您可以将领班视为K8S的kubelet。
但是,kubelet不会执行此工作。(记住,kubelet是一个领班。)相反,它告诉容器运行时接口(CRI)进行工作。(请参见下面的图4)
图4:在每个Kubernetes工作者节点中运行的kubelet实例告诉CRI创建容器以响应来自在Kubernetes Controller节点上运行的API服务器的通知
gRPC和CRI
kubelet告诉CRI做什么的方法是通过与嵌入在CRI中的gRPC服务器进行交互。(请参见下面的图5。)
图5:kubelet使用gRPC与容器运行时接口进行交互,以在工作节点上创建和销毁容器
当需要在节点上创建或销毁容器时,kubelet将消息发送到在该节点的CRI实例上运行的gRPC服务器以执行任务,然后CRI与安装在工作节点上的容器运行时引擎进行交互以执行必要操作。
例如,当kubelet想要创建一个容器时,它使用其gRPC客户端将CreateContainerRequest消息发送到CRI组件上托管的RPC(远程过程调用)函数CreateContainer()。的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;
}
清单1:用于使用Kubernetes容器运行时接口创建容器的gRPC函数和消息类型
反过来,CRI将创建请求发送到安装在节点上的实际容器运行时。容器运行时将创建容器。
Kubernetes允许您从节点上的各种容器运行时中安装一个。您可以安装久经考验的Docker运行时,但是也可以安装其他运行时,例如containerd,rkt或cri-o。(选择最适合给定Kubernetes安装的容器运行时可在自定义集群时提供更高的灵活性。)
容器创建完成后,CRI将返回protobuf文件中定义的CreateContainerResponse消息,该消息由gRPC客户端和服务器共享。清单2显示了CreateContainerResponse的定义。
message CreateContainerResponse {
// ID of the created container.
string container_id = 1;
}
清单2:CRI gRPC服务器返回一条CreateContainerResponse消息,该消息具有所创建容器的唯一标识符。
创建和销毁容器只是从容器运行时接口执行的两个活动。还有其他一些事情,例如停止容器,再次启动它,在容器中列出容器,以及更新容器的配置信息等。
了解CRI protobuf文件的详细信息
您可以在此处查看protobuf文件,该文件定义了在容器运行时界面上运行的gRPC服务器支持的类型和功能。protobuf文件是类型和函数的通用参考,这些类型和函数用于促进kubelet上运行的gRPC客户端和CRI上运行的gRPC服务器之间的通信。当您看一下protobuf文件中定义的功能时,您会清楚地了解CRI正在执行的工作量。
gRPC驱动在kubelet和CRI之间发生的所有消息交换。请记住,在kubelet和CRI之间交换消息需要快速进行,有时大约需要几纳秒的时间。一个典型的以Web规模运行的Kubernetes集群可以在数十个甚至数百个节点之间有效地运行数万个容器。因此,速度和效率在通信管道中至关重要。gPRC符合这些要求。
总结
在实际场景中使用gRPC时,Kubernetes是个庞然大物。Kubernetes和gRPC都起源于Google,因此自然而然地,这两种技术都应该在技术领域轻松而大规模地出现。但是,正如本文开头所述,采用率正在稳步增长。还有许多其他公司在其技术栈中使用gRPC。gRPC快速,高效和可靠是有原因的。驱动数据中心的关键任务型应用程序需要这种类型的银弹。如上所述,gRPC非常合适。