# 扩展
k8s 的软件生态很开放,可以在不修改 k8s 源码的情况下,通过多种方式扩展 k8s 的功能。比如:
- CRI、CNI、CSI、admission 等插件
- APIService
- CRD
- Custom Controller
从通信角度来看,k8s 的扩展方式分为两大类:
- Binary Plugin :让 k8s 执行某个二进制文件。例如 kubelet 会执行 CRI 插件 ,kubectl 命令集成了 kustomize 。
- Webhook :独立运行一个服务器,让 k8s 发送网络请求来调用它。例如 APIService、Custom Controller 。
# API 规范
k8s 内部的各个组件通过 API 相互调用,并且定义了 API 规范。
- 采用 gRPC 协议进行通信。
- API 规范使得 k8s 组件之间比较解耦,有利于模块化设计、更换组件。
- 例如 k8s 容器运行时的 API 规范是 CRI ,如果一个插件符合该 API 规范,则可以替换 k8s 默认的容器运行时组件。
容器运行时接口(Container Runtime Interface ,CRI)
- :k8s 的一个 API 规范。kubelet 会通过该接口,调用担任容器运行时的组件,从而管理容器、镜像。
- 早期的 k8s 默认采用 Docker 作为容器引擎,因为 Docker 是当时最流行的容器引擎。
- 后来 k8s 社区制定了 CRI 规范,但 Docker 不符合该规范。因此 k8s 社区开发了一个符合 CRI 规范的 dockershim 模块,通过它间接调用 dockerd ,从而管理容器。
- 2020 年, k8s 社区认为维护 dockershim 模块比较麻烦,因此弃用 dockershim 模块,建议用户改用 containerd 或 CRI-O 作为 CRI 组件。
- 不使用 dockershim 模块,就不能通过 docker 命令查看、管理 kubelet 部署的容器。但用 docker build 命令构建的镜像符合 OCI 标准,依然可以被 containerd 或 CRI-O 运行。
- 2021 年,Mirantis 公司和 Docker 公司联合开发 cri-dockerd 项目,用于继续实现 dockershim 的功能,从而不必用户更换 CRI 组件。
容器网络接口(Container Network Interface ,CNI)
- :k8s 的一个 API 规范。kubelet 会通过该接口,管理容器的网络。
容器存储接口(Container Storage Interface ,CSI)
- :k8s 的一个 API 规范。kubelet 会通过该接口,管理容器的存储层。
- k8s 本身提供了 hostPath、ConfigMap 等类型的 volume 。而一些第三方的存储程序可通过 CSI 接口接入 k8s 集群,提供一些其它类型的 volume 。
扩展资源(Extended Resource)
- Pod 的 resources.requests 中可以分配 cpu、memory、ephemeral-storage 等基础资源。除此之外,用户可以向 apiserver 注册一些扩展资源,然后分配给 Pod 使用。
- 比如告诉 apiserver :某个 node 拥有一种名为 nvidia.com/gpu 的扩展资源,capacity 容量为 1 。
- apiserver 只管记录每个 node 拥有哪些扩展资源,不关心这些扩展资源是如何工作的。
设备插件(Device Plugin)
- :k8s 的一个 API 规范。用户可通过该接口,将某个主机上的 GPU、FPGA 等设备资源,注册为扩展资源,并且允许 kubelet 监控、上报这些设备的状态。
# API Aggregation
apiserver 进程中设计了一个聚合层(Aggregation Layer),允许用户注册一些自定义的 API 服务器,被 apiserver 反向代理,从而在逻辑上聚合成一个 API 服务器。
- 优点:方便扩展 apiserver ,添加自定义的 API 。
例如 metrics-server 是一个给 k8s 增加 CPU、内存使用率等监控指标的工具,采用 API Aggregation 的方式工作。原理如下:
- 按 API 规范开发 metrics-server 程序,然后在 k8s 中以 Deployment 方式部署,并绑定 Service 。
- 创建一个 APIService 类型的资源,将 metrics-server 注册为一个 API 服务器。
apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: name: v1beta1.metrics.k8s.io # 该 API 服务器的名称,格式为 ${version}.${group} spec: group: metrics.k8s.io # API 服务器的组名 groupPriorityMinimum: 100 # 该 group 的优先级 insecureSkipTLSVerify: true # apiserver 向该 API 服务器发送 HTTPS 请求时,是否跳过 TLS 认证 service: # 让 apiserver 将请求发送到该 k8s service ,从而调用该 API 服务器 name: metrics-server namespace: kube-system port: 443 version: v1beta1 # API 服务器的版本 versionPriority: 100 # 该 version 在当前 group 全部版本中的优先级
- 客户端发送 HTTPS 请求给 apiserver 。如果请求指向的 URI 为
/apis/metrics.k8s.io/v1beta1/...
,则 apiserver 会将请求转发给 metrics-server 这个 APIService 。
用户可创建多个 APIService 资源。
- 如果多个 APIService 划分到同一个 group ,只是 version 不同,则表示同一种 API 服务器的不同版本的实例。
- 整个 group 的优先级,取决于该 group 中 groupPriorityMinimum 最高的那个 APIService 。
# CRD
自定义资源的声明(CustomResourceDefinition,CRD)
- :k8s 原生提供的一种资源,供用户注册一些自定义的 k8s 资源类型。然后用户就可以创建这些类型的 k8s 资源实例。
- 优点:k8s 外部的资源通常用 shell 脚本管理,注册为 CRD 资源之后能像 k8s 原生资源一样管理,更方便。
- 缺点:自定义资源可能需要进行一些改造,才适合使用 k8s 的声明式配置文件。
例:
- 创建 CRD,注册一个资源类型 Zone ,表示服务器地区。
apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: zones.test.com # 该 CRD 资源的名称,格式为 ${plural}.${group} spec: group: test.com names: kind: Zone # 该 CRD 资源的类型名 listKind: ZoneList plural: zones # 复数的英文名 singular: zone # 单数的英文名 scope: Cluster # 该 CRD 资源的作用域。取值为 Namespaced 表示单个命名空间,取值为 Cluster 表示整个集群 versions: # 可以给该 CRD 资源注册多个版本 - name: v1 # 该 CRD 资源的其中一个版本号 schema: # 声明该 CRD 资源的数据结构。主要是配置文件中,各个字段的名称、数据类型 openAPIV3Schema: type: object properties: spec: # 声明 spec 字段,取值为 object 类型,并通过 properties 声明子字段 properties: city: # 声明 spec.country 字段,取值为 string 类型 nullable: true # 是否允许取值为空 type: string type: object served: true # 是否允许通过 RESTful API 访问该 CRD 资源 storage: true # 必须有且仅有一个版本,标记为存储版本
- 创建一个 Zone 类型的资源实例:
apiVersion: test.com/v1 kind: Zone metadata: name: zone1 spec: city: beijing
- 可用 kubectl 命令查看:
kubectl get zones
- 删除一个 CRD 时,会删除这种类型的所有资源实例。
- 可用 kubectl 命令查看:
- 创建 CRD,注册一个资源类型 Zone ,表示服务器地区。
比较 CRD 与 APIService 资源:
- 注册 CRD 资源时,不需要编程。而注册 APIService 资源时,需要开发、部署自己的 API 服务器。
- 一个 CRD 资源可注册多个 versions 。而一个 APIService 资源只注册一个 version 。
# Operator
创建 CRD 资源之后,它们只是作为静态数据存储在 k8s 中。可以在 k8s 中添加自定义控制器(Custom Controller),负责监听 k8s 事件、自动管理 CRD 资源。
Operator 是一种部署 k8s 应用的方式,由 CoreOS 团队提出。它组合使用 CRD 和 Custom Controller ,能自动完成大部分运维工作。
例如 Oracle MySQL Operator (opens new window) 用于在 k8s 中部署 MySQL 集群,步骤如下:
- 下载该 Operator 的发行版,执行命令
kubectl apply -f xx
或helm install xx
安装到 k8s 中。这会对 k8s 做出以下改动:- 注册一些 CRD 资源,比如 InnoDBCluster 表示 MySQL 集群,MySQLBackup 表示 MySQL 备份。
- 部署一个名为 mysql-operator 的 Deployment ,作为 Custom Controller 工作。
- 创建一些 RBAC 资源。
- 新建一个 InnoDBCluster 资源,此时 Custom Controller 会自动配置 StatefulSet、Service、Volume 等资源,部署 MySQL 集群。
apiVersion: mysql.oracle.com/v2 kind: InnoDBCluster metadata: name: mycluster spec: secretName: mypwds # 指定 MySQL 挂载的 Secret instances: 3 # 部署的 MySQL 实例数 version: 8.0.31 # 部署的 MySQL 版本 mycnf: | # 传入 my.conf 配置文件 [mysqld] max_connections = 151 ...
- 部署 MySQL 集群之后,创建 MySQLBackup 资源,则 Custom Controller 会自动创建 Job 去备份数据。
- 下载该 Operator 的发行版,执行命令
开发 Operator 时,常用的编程框架:
- kubebuilder
- Operator Framework
<operatorhub.io> 网站上分享了一些 Operator 。