K8s 的核心是 API 而非容器:从理论到 CRD 实践
❝
本文转自 ArthurChiao's Blog,原文:https://arthurchiao.art/blog/k8s-is-about-apis-zh/,版权归原作者所有。欢迎投稿,投稿请添加微信好友:cloud-native-yang
本文串联了以下几篇文章的核心部分:
Kubernetes isn’t about containers[1],2021
Kubernetes is a Database[2], 2019
CRD is just a table in Kubernetes[3], 2020
论述了 K8s 的核心价值是其通用、跨厂商和平台、可灵活扩展的声明式 API 框架, 而不是容器(虽然容器是它成功的基础);然后手动创建一个 API extension(CRD), 通过测试和类比来对这一论述有一个更直观的理解。
例子及测试基于 K8s v1.21.0
,感谢原作者们的精彩文章。
K8s 的核心是其 API 框架而非容器
容器是基础
时间回到 2013 年。当一条简单的 docker run postgre
命令就能运行起 postgre 这样 复杂的传统服务时,开发者在震惊之余犹如受到天启;以 docker 为代表的实用容器技术的横空出世,也预示着一扇通往敏捷基础设施的大门即将打开。此后,一切都在往好的方向迅速发展:
越来越多的开发者开始采用容器作为一种标准构建和运行方式,
业界也意识到,很容易将这种封装方式引入计算集群,通过 Kubernetes 或 Mesos 这样的编排器来调度计算任务 —— 自此,容器便成为这些调度器最重要的 workload 类型。
但本文将要说明,容器并非 Kubernetes 最重要、最有价值的地方,Kubernetes 也并非 仅仅是一个更广泛意义上的 workload 调度器 —— 高效地调度不同类型的 workload 只是 Kubernetes 提供的一种重要价值,但并不是它成功的原因。
API 才是核心
❝
“等等 —— K8s 只是一堆 API?”
“不好意思,一直都是!”
K8s 的成功和价值在于,提供了一种标准的编程接口(API),可以用来编写和使用 软件定义的基础设施服务(本文所说的“基础设施”,范围要大于 IAAS):
Specification + Implementation 构成一个完整的 API 框架 —— 用于设计、实现和使用各种类型和规模的基础设施服务;
这些 API 都基于相同的核心结构和语义:typed resources watched and reconciled by controllers (资源按类型划分,控制器监听相应类型的资源,并将其实际 status 校准到 spec 里期望的状态)。
为了进一步解释这一点,考虑下 Kubernetes 出现之前的场景。
K8s 之前:各自造轮子,封装厂商 API 差异
K8s 之前,基础设施基本上是各种不同 API、格式和语义的“云”服务组成的大杂烩:
云厂商只提供了计算实例、块存储、虚拟网络和对象存储等基础构建模块,开发者需要像拼图一样将它们拼出一个相对完整的基础设施方案;
对于其他云厂商,重复过程 1,因为各家的 API、结构和语义并不相同,甚至差异很大。
虽然 Terraform 等工具的出现,提供了一种跨厂商的通用格式,但原始的结构和语义仍然 是五花八门的,—— 针对 AWS 编写的 Terraform descriptor 是无法用到 Azure 的。
K8s 面世:标准化、跨厂商的 API、结构和语义
现在再来看 Kubernetes 从一开始就提供的东西:描述各种资源需求的标准 API。例如,
描述 pod、container 等计算需求 的 API;
描述 service、ingress 等虚拟网络功能 的 API;
描述 volumes 之类的持久存储 的 API;
甚至还包括 service account 之类的服务身份 的 API 等等。
这些 API 是跨公有云/私有云和各家云厂商的,各云厂商会将 Kubernetes 结构和语义 对接到它们各自的原生 API。 因此我们可以说,Kubernetes 提供了一种管理软件定义基础设施(也就是云)的标准接口。 或者说,Kubernetes 是一个针对云服务(cloud services)的标准 API 框架。
K8s API 扩展:CRD
提供一套跨厂商的标准结构和语义来声明核心基础设施(pod/service/volume/serviceaccount/…), 是 Kubernetes 成功的基础。在此基础上,它又通过 CRD(Custom Resource Definition), 将这个结构扩展到任何/所有基础设施资源。
CRD 在 1.7 引入,允许云厂商和开发者自己的服务复用 K8s 的 spec/impl 编程框架。
有了 CRD,用户不仅能声明 Kubernetes API 预定义的计算、存储、网络服务, 还能声明数据库、task runner、消息总线、数字证书 … 任何云厂商能想到的东西!
Operator Framework[4] 以及 SIG API Machinery[5] 等项目的出现,提供了方便地创建和管理这些 CRD 的工具,最小化用户工作量,最大程度实现标准化。
例如,Crossplane 之类的项目,将厂商资源 RDS 数据库、SQS queue 资源映射到 Kubernetes API,就像核心 K8s controller 一样用自己的 controller 来管理网卡、磁盘等自定义资源。 Google、RedHat 等 Kubernetes 发行商也在它们的基础 Kubernetes 发行版中包含越来越多的自定义资源类型。
小结
我们说 Kubernetes 的核心是其 API 框架,但并不是说这套 API 框架就是完美的。 事实上,后一点并不是(非常)重要,因为 Kubernetes 模型已经成为一个事实标准: 开发者理解它、大量工具主动与它对接、主流厂商也都已经原生支持它。用户认可度、互操作性 经常比其他方面更能决定一个产品能否成功。
随着 Kubernetes 资源模型越来越广泛的传播,现在已经能够 用一组 Kubernetes 资源来描述一整个软件定义计算环境。 就像用 docker run
可以启动单个程序一样,用 kubectl apply -f
就能部署和运行一个分布式应用, 而无需关心是在私有云还是公有云以及具体哪家云厂商上,Kubernetes 的 API 框架已经屏蔽了这些细节。
因此,Kubernetes 并不是关于容器的,而是关于 API。
K8s 的 API 类型
可以通过 GET/LIST/PUT/POST/DELETE
等 API 操作,来创建、查询、修改或删除集群中的资源。 各 controller 监听到资源变化时,就会执行相应的 reconcile 逻辑,来使 status 与 spec 描述相符。
标准 API(针对内置资源类型)
Namespaced 类型
这种类型的资源是区分 namespace,也就是可以用 namespace 来隔离。 大部分内置资源都是这种类型,包括:
pods
services
networkpolicies
API 格式:
格式:
/api/{version}/namespaces/{namespace}/{resource}
举例:
/api/v1/namespaces/default/pods
Un-namespaced 类型
这种类型的资源是全局的,不能用 namespace 隔离,例如:
nodes
clusterroles (
clusterxxx
一般都是,表示它是 cluster-scoped 的资源)
API 格式:
格式:
/api/{version}/{resource}
举例:
/api/v1/nodes
扩展 API(apiextension
)
Namespaced 类型
API 格式:
格式:
/apis/{apiGroup}/{apiVersion}/namespaces/{namespace}/{resource}
举例:
/apis/cilium.io/v2/namespaces/kube-system/ciliumnetworkpolicies
Un-namespaced 类型
略。
CRD
用户发现了 k8s 的强大之后,希望将越来越多的东西(数据)放到 k8s 里面, 像内置的 Pod、Service、NetworkPolicy 一样来管理,因此出现了两个东西:
CRD:用来声明用户的自定义资源,例如它是 namespace-scope 还是 cluster-scope 的资源、有哪些字段等等,K8s 会自动根据这个定义生成相应的 API;
官方文档的例子[6], 后文也将给出一个更简单和具体的例子。
CRD 是资源类型定义,具体的资源叫 CR。
Operator 框架:“operator” 在这里的字面意思是“承担运维任务的程序”, 它们的基本逻辑都是一样的:时刻盯着资源状态,一有变化马上作出反应(也就是 reconcile 逻辑)。
这就是扩展 API 的(最主要)声明和使用方式。
至此,我们讨论的都是一些比较抽象的东西,接下来通过一些例子和类比来更直观地理解一下。
直观类比:K8s 是个数据库,CRD 是一张表,API 是 SQL
在本节中,我们将创建一个名为 fruit
的 CRD,它有 name/sweet/weight
三个字段, 完整 CRD 如下,
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:name: fruits.example.org ## CRD 名字
spec:conversion:strategy: Nonegroup: example.org ## REST API: /apis/<group>/<version>names:kind: FruitlistKind: FruitListplural: fruitssingular: fruitscope: Namespaced ## Fruit 资源是区分 namespace 的versions:- name: v1 ## REST API: /apis/<group>/<version>schema:openAPIV3Schema:properties:spec:properties:comment: ## 字段 1,表示备注type: stringsweet: ## 字段 2,表示甜否type: booleanweight: ## 字段 3,表示重量type: integertype: objecttype: objectserved: true ## 启用这个版本的 API(v1)storage: trueadditionalPrinterColumns: ## 可选项,配置了这些 printer columns 之后,- jsonPath: .spec.sweet ## 命令行 k get <crd> <cr> 时,能够打印出下面这些字段,name: sweet ## 否则,k8s 默认只打印 CRD 的 NAME 和 AGEtype: boolean- jsonPath: .spec.weightname: weighttype: integer- jsonPath: .spec.commentname: commenttype: string
后面会解释每个 section 都是什么意思。在此之前,先来做几个(直观而粗糙的)类比。
K8s 是个数据库
像其他数据库技术一样,它有自己的持久存储引擎(etcd),以及构建在存储引擎之上的 一套 API 和语义。这些语义允许用户创建、读取、更新和删除(CURD)数据库中的数据。 下面是一些概念对应关系:
关系型数据库 | Kubernetes (as a database) | 说明 |
---|---|---|
DATABASE
|
cluster | 一套 K8s 集群就是一个 database 【注 1】 |
TABLE
|
Kind
|
每种资源类型对应一个表;分为内置类型和扩展类型 【注 2】 |
COLUMN
|
property | 表里面的列,可以是 string、boolean 等类型 |
rows | resources | 表中的一个具体 record |
❝
【注 1】 如果只考虑 namespaced 资源的话,也可以说一个 namespace 对应一个 database。
【注 2】 前面已经介绍过,
内置
Kind
:Job、Service、Deployment、Event、NetworkPolicy、Secret、ConfigMap 等等;扩展
Kind
:各种 CRD,例如 CiliumNetworkPolicy。
所以,和其他数据库一样,本质上 Kubernetes 所做的不过是以 schema 规定的格式来处理 records。
另外,Kubernetes 的表都有自带文档:
$ kubectl explain fruits
KIND: Fruit
VERSION: example.org/v1DESCRIPTION:<empty>FIELDS:apiVersion <string>APIVersion defines the versioned schema of this representation of anobject. Servers should convert recognized schemas to the latest internalvalue, and may reject unrecognized values. More info:https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resourceskind <string>Kind is a string value representing the REST resource this objectrepresents. Servers may infer this from the endpoint the client submitsrequests to. Cannot be updated. In CamelCase. More info:https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kindsmetadata <Object>Standard object's metadata. More info:https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadataspec <Object>
另外,Kubernetes API 还有两大特色:
极其可扩展:声明 CRD 就会自动创建 API;
支持事件驱动。
CRD 是一张表
CRD 和内置的 Pod、Service、NetworkPolicy 一样,不过是数据库的一张表。 例如,前面给出的 fruit
CRD,有 name/sweet/weight
列,以及 “apple”, “banana” 等 entry,
用户发现了 k8s 的强大,希望将越来越多的东西(数据)放到 k8s 里面来管理。数据类 型显然多种多样的,不可能全部内置到 k8s 里。因此,一种方式就是允许用户创建自己的 “表”,设置自己的“列” —— 这正是 CRD 的由来。
定义表结构(CRD spec)
CRD(及 CR)描述格式可以是 YAML 或 JSON。CRD 的内容可以简单分为三部分:
常规 k8s metadata:每种 K8s 资源都需要声明的字段,包括
apiVersion
、kind
、metadata.name
等。apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata:name: fruits.example.org ## CRD 名字
Table-level 信息:例如表的名字,最好用小写,方便以后命令行操作;
spec:conversion:strategy: Nonegroup: example.org ## REST API: /apis/<group>/<version>names:kind: FruitlistKind: FruitListplural: fruitssingular: fruitscope: Namespaced ## Fruit 资源是区分 namespace 的
Column-level 信息:列名及类型等等,遵循 OpenAPISpecification v3 规范。
versions:- name: v1 ## REST API: /apis/<group>/<version>schema:openAPIV3Schema:properties:spec:properties:comment: ## 字段 1,表示备注type: stringsweet: ## 字段 2,表示甜否type: booleanweight: ## 字段 3,表示重量type: integertype: objecttype: objectserved: true ## 启用这个版本的 API(v1)storage: trueadditionalPrinterColumns: ## 可选项,配置了这些 printer columns 之后,- jsonPath: .spec.sweet ## 命令行 k get <crd> <cr> 时,能够打印出下面这些字段,name: sweet ## 否则,k8s 默认只打印 CRD 的 NAME 和 AGEtype: boolean- jsonPath: .spec.weightname: weighttype: integer- jsonPath: .spec.commentname: commenttype: string
测试:CR 增删查改 vs. 数据库 SQL
创建 CRD:这一步相当于
CREATE TABLE fruits ...;
,
$ kubectl create -f fruits-crd.yamlcustomresourcedefinition.apiextensions.k8s.io/fruits.example.org created
创建 CR:相当于
INSERT INTO fruits values(...);
,
apple-cr.yaml
:
apiVersion: example.org/v1kind: Fruitmetadata:name: applespec:sweet: falseweight: 100comment: little bit rotten
banana-cr.yaml
:
apiVersion: example.org/v1kind: Fruitmetadata:name: bananaspec:sweet: trueweight: 80comment: just bought
创建:
$ kubectl create -f apple-cr.yamlfruit.example.org/apple created$ kubectl create -f banana-cr.yamlfruit.example.org/banana created
查询 CR:相当于
SELECT \* FROM fruits ... ;
或SELECT \* FROM fruits WHERE name='apple';
。
$ k get fruits.example.org ## or kubectl get fruitsNAME SWEET WEIGHT COMMENTapple false 100 little bit rottenbanana true 80 just bought$ kubectl get fruits appleNAME SWEET WEIGHT COMMENTapple false 100 little bit rotten
删除 CR:相当于
DELETE FROM fruits WHERE name='apple';
,
$ kubectl delete fruit apple
可以看到,CRD/CR 的操作都能对应到常规的数据库操作。
API 是 SQL
上一节我们是通过 kubectl
命令行来执行 CR 的增删查改,它其实只是一个外壳,内部 调用的是 Kubernetes 为这个 CRD 自动生成的 API —— 所以 又回到了本文第一节论述的内容:K8s 的核心是其 API 框架。
只要在执行 kubectl
命令时指定一个足够大的 loglevel,就能看到 背后的具体 API 请求。例如,
$ kubectl create -v 10 -f apple-cr.yaml...Request Body: {"apiVersion":"example.org/v1","kind":"Fruit",\"spec\":{\"comment\":\"little bit rotten\",\"sweet\":false,\"weight\":100}}\n"},"name":"apple","namespace":"default"},"spec":{"comment":"little bit rotten","sweet":false,"weight":100}}curl -k -v -XPOST 'https://127.0.0.1:6443/apis/example.org/v1/namespaces/default/fruits?fieldManager=kubectl-client-side-apply'POST https://127.0.0.1:6443/apis/example.org/v1/namespaces/default/fruits?fieldManager=kubectl-client-side-apply 201 Created in 25 milliseconds...
其他
给 CR 打标签(label),根据 label 过滤
和内置资源类型一样,K8s 支持对 CR 打标签,然后根据标签做过滤:
## 查看所有 frutis
$ k get fruits
NAME SWEET WEIGHT COMMENT
apple false 100 little bit rotten
banana true 80 just bought## 给 banana 打上一个特殊新标签
$ k label fruits banana tastes-good=true
fruit.example.org/banana labeled## 按标签筛选 CR
$ k get fruits -l tastes-good=true
NAME SWEET WEIGHT COMMENT
banana true 80 just bought## 删除 label
$ k label fruits banana tastes-good-
fruit.example.org/banana labeled
K8s API 与鉴权控制(RBAC)
不管是内置 API,还是扩展 API,都能用 K8s 强大的 RBAC 来做鉴权控制。
关于如何使用 RBAC 网上已经有大量文档;但如果想了解其设计,可参考 Cracking Kubernetes RBAC Authorization Model[7], 它展示了如何从零开始设计出一个 RBAC 鉴权模型(假设 K8s 里还没有)。
参考资料
Kubernetes isn’t about containers[8],2021
Kubernetes is a Database[9], 2019
CRD is just a table in Kubernetes[10], 2020
Cracking Kubernetes RBAC Authorization Model[11], 2022
引用链接
[1]
Kubernetes isn’t about containers: https://blog.joshgav.com/2021/12/16/kubernetes-isnt-about-containers.html
[2]
Kubernetes is a Database: https://github.com/gotopple/k8s-for-users-intro/blob/master/database.md
[3]
CRD is just a table in Kubernetes: https://itnext.io/crd-is-just-a-table-in-kubernetes-13e15367bbe4
[4]
Operator Framework: https://operatorframework.io/
[5]
SIG API Machinery: https://github.com/kubernetes/community/tree/master/sig-api-machinery
[6]
例子: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#create-a-customresourcedefinition
[7]
Cracking Kubernetes RBAC Authorization Model: https://arthurchiao.art/blog/cracking-k8s-authz-rbac/
[8]
Kubernetes isn’t about containers: https://blog.joshgav.com/2021/12/16/kubernetes-isnt-about-containers.html
[9]
Kubernetes is a Database: https://github.com/gotopple/k8s-for-users-intro/blob/master/database.md
[10]
CRD is just a table in Kubernetes: https://itnext.io/crd-is-just-a-table-in-kubernetes-13e15367bbe4
[11]
Cracking Kubernetes RBAC Authorization Model: https://arthurchiao.art/blog/cracking-k8s-authz-rbac/
你可能还喜欢
点击下方图片即可阅读
使用自动化工作流聚合信息,实现个人数字生活的信息聚合
2022-06-01
使用 Packer 构建虚拟机镜像踩的坑
2022-05-30
使用 Finalizers 来删除那些死活删除不掉的 K8s资源
2022-05-29
K3s 工具进阶完全指南
2022-05-25
云原生是一种信仰
本文论述了 Kubernetes 的核心价值是其通用.跨厂商和平台.可灵活扩展的声明式 API 框架, 而不是容器(虽然容器是它成功的基础):然后手动创建一个 API Extension(CRD), ... K8S中的pod.services.容器的概念和区别 k8s的部署架构 kubernetes中有两类资源,分别是master和nodes,master和nodes上跑的服务如下图: 1 kube-ap ... kubeadm部署K8S集群并使用containerd做容器运行时(内容全部实战验证有任何问题欢迎留言咨询讨论) 前言 去年12月份,当Kubernetes社区宣布1.20版本之后会逐步弃用docke ... 核心C API SQLite3 有八个函数用于实际处理连接.处理查询以及断开数据库连接的.其余都是为了完成特定的任务 一.查询封装 通过封装查询对数据库进行连接.断开.以及查询. 1.连接和断开 执行 ... 一:核心配置分为三大部分 必须的配置 .可选的配置和引入映射文件. 1.必须的配置 连接数据库的参数:驱动类 url路径 用户名 密码 方言 <property name=" ... 1.java使用到的图形类主要在java.awt 与javax.swing包中. 2.java.awt 与 javax.swing包的区别: ① java.awt中使用的图形类都是依赖于系统的图形库的 ... 文章目录 一.多目标优化算法简介 1.基本知识 二.NSGA2算法 1.基本原理 2.快速非支配排序 2.1快速非支配排序 python实现 3.拥挤距离 3.1 拥挤距离python 实现 4.精英 ... #Windows核心编程 - API HOOK应用 如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:129518033 目录 文章目录 #Windows核 ... 关于P-OSV中非公开API(非 SDK 接口)的限制及搜索方式,做了以下整理. 1.背景 Android 9(API 级别 28)引入了针对非 SDK 接口的使用限制,无论是直接使用还是通过反射或 ...K8s 的核心是 API 而非容器:从理论到 CRD 实践相关推荐
最新文章
热门文章