kubelet 原理解析三:runtime

2020-03-27  本文已影响0人  徐亚松_v

本文转自:https://feisky.xyz/posts/kubernetes-container-runtime/

架构

Kubelet 架构图


image
* 一个是内置的 dockershim,实现了 docker 容器引擎的支持以及 CNI 网络插件(包括 kubenet)的支持
* 另一个就是外部的容器运行时,用来支持 runc、containerd、gvisor 等外部容器运行时

Kubelet 通过 CRI 接口跟外部容器运行时交互,它包括

这样,Kubernetes 中的容器运行时按照不同的功能就可以分为三个部分:

这样的话,我们就可以基于这三个不同部分来看一看容器运行时的演进过程

演进过程

image

容器运行时的演进可以分为三个阶段:

容器运行时接口(CRI)

容器运行时接口(CRI)是一个用来扩展容器运行时的接口,它基于 gPRC,用户不需要关心内部通信逻辑,而只需要实现定义的接口就可以,包括 RuntimeService 和 ImageService。

除了 gRPC API,CRI 还包括用于实现 streaming server 的库(用于 Exec、Attach、PortForward 等接口)和 CRI Tools。

image

基于 CRI 接口的容器运行时通常称为 CRI shim, 这是一个 gRPC Server,监听在本地的unix socket上;而kubelet作为gRPC的客户端来调用CRI接口。另外,外部容器运行时需要自己负责管理容器的网络,推荐使用CNI,这样跟Kubernetes的网络模型保持一致。

CRI 的推出为容器社区带来了新的繁荣,cri-o、frakti、cri-containerd 等一些列的容器运行时为不同场景而生:

而基于这些容器运行时,还可以轻易联结新型的容器引擎,比如可以通过 clear container、gVisor 等新的容器引擎配合 cri-o 或 cri-containerd 等轻易接入 Kubernetes,将 Kubernetes 的应用场景扩展到了传统 IaaS 才能实现的强隔离和多租户场景。

当使用CRI运行时,需要配置kubelet的--container-runtime参数为remote,并设置--container-runtime-endpoint为监听的unix socket位置(Windows上面为 tcp 端口)。

CRI 接口

CRI 接口包括 RuntimeService 和 ImageService 两个服务,这两个服务可以在一个 gRPC server 里面实现,当然也可以分开成两个独立服务。目前社区的很多运行时都是将其在一个 gRPC server 里面实现。

image

分类

管理镜像的 ImageService 提供了 5 个接口,分别是查询镜像列表、拉取镜像到本地、查询镜像状态、删除本地镜像以及查询镜像占用空间等。这些都很容易映射到 docker API 或者 CLI 上面。

而 RuntimeService 则提供了更多的接口,按照功能可以划分为四组:

Streaming API

Streaming API 用于客户端与容器进行交互,包括Exec、PortForward 和 Attach 等三个接口。Kubelet 内置的 docker 通过 nsenter、socat 等方法来支持这些特性,但它们不一定适用于其他的运行时,也不支持 Linux 之外的其他平台。因而,CRI 也显式定义了这些 API,并且要求容器运行时返回一个 streaming server 的 URL 以便 Kubelet 重定向 API Server 发送过来的流式请求。

这是因为所有容器的流式请求都会经过 Kubelet,这有可能会给节点的网络流量带来瓶颈,因而 CRI 要求容器运行时启动一个对应请求的单独的流服务器,将地址返回给Kubelet。Kubelet然后将这个信息再返回给Kubernetes API Server,它会打开直接与运行时提供的服务器相连的流连接,并通过它跟客户端连通。


image

这样一个完整的 Exec 流程就如上图所示,分为多个阶段:

在 v1.10 及更早版本中,容器运行时必需返回一个 API Server 可直接访问的 URL(通常跟 Kubelet 使用相同的监听地址);而从 v1.11 开始,Kubelet 新增了--redirect-container-streaming选项(默认为 false),支持不转发而是代理 Streaming 请求,这样运行时可以返回一个 localhost 的 URL(当然也不再需要配置 TLS)。

运行时分类

以下是几个常见容器运行时的例子,它们各有所长,并且也支持不同的容器引擎:

image

隔离性

在多租户场景下,强隔离(特别是虚拟化级别的隔离)是一个最基本的需求。

以前使用 Kubernetes 时,由于只支持Docker 容器,而它只提供了内核命名空间(namespace)的隔离,虽然也支持 SELinux、AppArmor 等基本的安全控制,但还是无法满足多租户的需求。所以曾经社区有人提出节点独占的方式实现租户隔离,即每个容器或租户独占一台虚拟机,资源的浪费是很明显的。

有了 CRI 之后,就可以接入 Kata Container、Clear Container 等基于虚拟化的容器引擎。这样通过虚拟化实现了容器的强隔离,不同租户的容器也可以运行在相同的 Node 上,大大提高了资源的利用率。

当然了,多租户不仅需要容器自身的强隔离,还需要众多其他的功能一起配合,比如

网络隔离,比如可以使用 CNI 构建新的网络插件,把不同租户的 Pod 接入到相互隔离的虚拟网络中。
资源管理,比如基于 CRD 构建租户 API 和租户控制器,管理租户和租户的资源。
认证、授权、配额管理等等也都可以在 Kubernetes API 之上构建。

工具

CRI Tools 是社区 Node 组针对 CRI 接口开发的辅助工具,它包括两个工具:crictl 和 critest。

crictl 是一个容器运行时命令行接口,它对系统和应用的排错来说是个很有用的工具。当使用 Docker 运行时,调试系统信息的时候我们可能使用 docker ps 和 docker inspect 等命令检查应用的进程情况。但是对于其他基于 CRI 的容器运行时来说,它们可能没有自己的命令行工具;或者即便有,它们的操作界面也不一定与 Kubernetes 中的概念一致。更不用说,很有很多的命令对 Kubernetes 没什么用,甚至会损害系统(比如 docker rename)。因而,我们推荐使用 crictl 作为 Docker CLI 的继任者,用于 Kubernetes 节点上 pod、容器以及镜像的除错工具。

crictl 提供了类似 Docker CLI 的使用体验, 并且支持所有 CRI 兼容的容器运行时。并且,crictl 提供了一个对 Kubernetes 来说更加友好的容器视角:它就是为 Kubernetes 而设计的,有不同的命令分别与Pod 和容器进行交互。例如 crictl pods 会列出 Pod 信息,而 crictl ps 只会列出应用容器的信息。

而 critest 则是一个容器运行时的验证测试工具,用于验证容器运行时是否符合 Kubelet CRI 的要求。除了验证测试,critest 还提供了 CRI 接口的性能测试,比如 critest -benchmark。

推荐将 critest 集成到容器运行时开发的 Devops 流程中,保证每个变更都不会破坏 CRI 的基本功能。另外,还可以选择将 critest 的测试结果与 Kubernetes Node E2E 的结果提交到 Sig-node 的 TestGrid,向社区和用户展示。

多容器运行时

多容器运行时用于不同的目的,比如使用虚拟化容器引擎式运行不可信应用和多租户应用,而使用 Docker 运行系统组件或者无法虚拟化的容器(比如需要 HostNetwork 的容器)。比如典型的用例为:

以前,多容器运行时通常以注解(Annotation)的形式支持,比如 cri-o、frakti 等都是这么支持了多容器运行时。但这一点也不优雅,并且也无法实现基于容器运行时来调度容器。因而,Kubernetes 在 v1.12 中将开始增加 RuntimeClass 这个新的 API 对象,用来支持多容器运行时。

GPU 支持

Nvidia提供了k8s pod使用GPU的一整套解决方案。运行时方面,nvidia提供了特定的运行时,主要的功能是为了让container访问从node节点上分配的GPU资源。

image

如上图所示,libnvidia-container被整合进docker的runc中。通过在runc的prestart hook 中调用nvidia-container-runtime-hook来控制GPU。启动容器时,prestart hook会校验环境变量GPU-enabled来校验该容器是否需要启用GPU,一旦确认需要启用,就调用nvidia定制的运行时来启动容器,从而为容器分配limit指定个数的GPU。

在k8s的调度方面,nvidia基于k8s的device plugin来实现了kubelet层GPU资源的上报,通过在pod的spec中指定对应的limit来声明对GPU个数的申请情况。在spec中必须指定limit的值(且必须为整数),reqire的值要么不设置,要么等于limit的值。

NVIDIA_VISIBLE_DEVICES : controls which GPUs will be accessible inside the container. By default, all GPUs are accessible to the container.
NVIDIA_DRIVER_CAPABILITIES : controls which driver features (e.g. compute, graphics) are exposed to the container.
NVIDIA_REQUIRE_* : a logical expression to define the constraints (e.g. minimum CUDA, driver or compute capability) on the configurations supported by the container.

serverless

无服务器(Serverless)现在是一个很热门的方向,各个云平台也提供很多种类的无服务器计算服务,比如 Azure Container Instance、AWS Farget 等。它们的好处是用户不需要去管理容器底层的基础设施,而只需要管理容器即可,并且容器通常按实际的运行时间收费。这样,对用户来说,不仅省去了管理基础设施的繁琐步骤,还更节省成本。

那么 CRI 在这里有什么应用场景呢?假如你是一个云平台的管理者,想要构建一个无服务器容器服务,那么使用 CRI 配合多容器运行时就是一个很好的思路。这样的话,

那么,对云平台的用户呢?这些无服务器容器服务提供的功能通常都比较简单,并不具备编排的功能。但可以借助 Virtual Kubelet 项目,使用 Kubernetes 为这些平台的容器提供编排功能。

Virtual Kubelet 是针对 Serverless 容器平台设计的虚拟 Kubernetes 节点,它模拟了 Kubelet 的功能,并将 Serverless 容器平台抽象为一个虚拟的无限资源的 Node。这样就可以通过 Kubernetes API 来管理其上的容器。

image

目前 Virtual Kubelet 已经支持了众多的云平台,包括

上一篇下一篇

猜你喜欢

热点阅读