Kubernetes 的核心是 API 而非容器

2022-08-29 12:08:14 浏览数 (1)

点击上方“芋道源码”,选择“设为星标”

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

  • 原创 | Java 2021 超神之路,很肝~
  • 中文详细注释的开源项目
  • RPC 框架 Dubbo 源码解析
  • 网络应用框架 Netty 源码解析
  • 消息中间件 RocketMQ 源码解析
  • 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析
  • 作业调度中间件 Elastic-Job 源码解析
  • 分布式事务中间件 TCC-Transaction 源码解析
  • Eureka 和 Hystrix 源码解析
  • Java 并发源码

来源:arthurchiao.art/blog/

k8s-is-about-apis-zh/

  • 1、Kubernetes 的核心是其 API 框架而非容器
  • 2. Kubernetes 的 API 类型
  • 3. 直观类比:Kubernetes 是个数据库,CRD 是一张表,API 是 SQL
  • 4. 其他

本文串联了以下几篇文章的核心部分,

  1. Kubernetes isn’t about containers,2021
  2. Kubernetes is a Database, 2019
  3. CRD is just a table in Kubernetes, 2020

论述了 K8s 的核心价值是其通用、跨厂商和平台、可灵活扩展的声明式 API 框架, 而不是容器(虽然容器是它成功的基础);然后手动创建一个 API extension(CRD), 通过测试和类比来对这一论述有一个更直观的理解。

例子及测试基于 K8s v1.21.0,感谢原作者们的精彩文章。

1、Kubernetes 的核心是其 API 框架而非容器

1.1 容器是基础

时间回到 2013 年。当一条简单的 docker run postgre 命令就能运行起 Postgre 这样复杂的传统服务时,开发者在震惊之余犹如受到天启;以 Docker 为代表的实用容器技术的横空出世,也预示着一扇通往敏捷基础设施的大门即将打开。此后,一切都在往好的方向迅速发展:

  • 越来越多的开发者开始采用容器作为一种标准构建和运行方式。
  • 业界也意识到,很容易将这种封装方式引入计算集群,通过 Kubernetes 或 Mesos 这样的编排器来调度计算任务 —— 自此,容器便成为这些调度器最重要的 Workload 类型。

但本文将要说明,容器并非 Kubernetes 最重要、最有价值的地方,Kubernetes 也并非仅仅是一个更广泛意义上的 Workload 调度器 —— 高效地调度不同类型的 Workload 只是 Kubernetes 提供的一种重要价值,但并不是它成功的原因。

1.2 API 才是核心

“等等 —— Kubernetes 只是一堆 API?”

“不好意思,一直都是!”

Kubernetes 的成功和价值在于,提供了一种标准的编程接口(API),可以用来编写和使用软件定义的基础设施服务(本文所说的“基础设施”,范围要大于 IaaS):

  • Specification Implementation 构成一个完整的 API 框架 —— 用于设计、实现和使用各种类型和规模的基础设施服务;
  • 这些 API 都基于相同的核心结构和语义:typed resources watched and reconciled by controllers (资源按类型划分,控制器监听相应类型的资源,并将其实际 status 校准到 spec 里期望的状态)。

为了进一步解释这一点,考虑下 Kubernetes 出现之前的场景。

Kubernetes 之前:各自造轮子,封装厂商 API 差异

Kubernetes 之前,基础设施基本上是各种不同 API、格式和语义的“云”服务组成的大杂烩:

  1. 云厂商只提供了计算实例、块存储、虚拟网络和对象存储等基础构建模块,开发者需要像拼图一样将它们拼出一个相对完整的基础设施方案;
  2. 对于其他云厂商,重复过程 1,因为各家的 API、结构和语义并不相同,甚至差异很大。

虽然 Terraform 等工具的出现,提供了一种跨厂商的通用格式,但原始的结构和语义仍然是五花八门的,—— 针对 AWS 编写的 Terraform descriptor 是无法用到 Azure 的。

Kubernetes 面世:标准化、跨厂商的 API、结构和语义

现在再来看 Kubernetes 从一开始就提供的东西:描述各种资源需求的标准 API。例如:

  1. 描述 Pod、Container 等计算需求的 API;
  2. 描述 Service、Ingress 等虚拟网络功能的 API;
  3. 描述 Volumes 之类的持久存储的 API;
  4. 甚至还包括 Service Account 之类的服务身份的 API 等等。

这些 API 是跨公有云/私有云和各家云厂商的,各云厂商会将 Kubernetes 结构和语义对接到它们各自的原生 API。因此我们可以说,Kubernetes 提供了一种管理软件定义基础设施(也就是云)的标准接口。或者说,Kubernetes 是一个针对云服务(Cloud Services)的标准 API 框架。

Kubernetes API 扩展:CRD

提供一套跨厂商的标准结构和语义来声明核心基础设施(Pod/Service/Volume/ServiceAccount/……), 是 Kubernetes 成功的基础。在此基础上,它又通过 CRD(Custom Resource Definition), 将这个结构扩展到任何/所有基础设施资源。

CRD 在 1.7 引入,允许云厂商和开发者自己的服务复用 Kubernetes 的 spec/impl 编程框架。

  1. 有了 CRD,用户不仅能声明 Kubernetes API 预定义的计算、存储、网络服务,还能声明数据库、task runner、消息总线、数字证书……任何云厂商能想到的东西!
  2. Operator Framework 以及 SIG API Machinery 等项目的出现,提供了方便地创建和管理这些 CRD 的工具,最小化用户工作量,最大程度实现标准化。

例如,Crossplane 之类的项目,将厂商资源 RDS 数据库、SQS queue 资源映射到 Kubernetes API,就像核心 Kubernetes controller 一样用自己的 controller 来管理网卡、磁盘等自定义资源。Google、RedHat 等 Kubernetes 发行商也在它们的基础 Kubernetes 发行版中包含越来越多的自定义资源类型。

1.3 小结

我们说 Kubernetes 的核心是其 API 框架,但并不是说这套 API 框架就是完美的。事实上,后一点并不(非常)重要,因为 Kubernetes 模型已经成为一个事实标准:开发者理解它、大量工具主动与它对接、主流厂商也都已经原生支持它。用户认可度、互操作性 经常比其他方面更能决定一个产品能否成功。

随着 Kubernetes 资源模型越来越广泛的传播,现在已经能够 用一组 Kubernetes 资源来描述一整个软件定义计算环境。就像用 docker run 可以启动单个程序一样,用 kubectl apply -f 就能部署和运行一个分布式应用, 而无需关心是在私有云还是公有云以及具体哪家云厂商上,Kubernetes 的 API 框架已经屏蔽了这些细节。

因此,Kubernetes 并不是关于容器的,而是关于 API。

基于 Spring Boot MyBatis Plus Vue & Element 实现的后台管理系统 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

2. Kubernetes 的 API 类型

可以通过 GET/LIST/PUT/POST/DELETE 等 API 操作,来创建、查询、修改或删除集群中的资源。各 controller 监听到资源变化时,就会执行相应的 reconcile 逻辑,来使 status 与 spec 描述相符。

2.1 标准 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

2.2 扩展 API(apiextension)

1、Namespaced 类型

API 格式:

  • 格式:/apis/{apiGroup}/{apiVersion}/namespaces/{namespace}/{resource}
  • 举例:/apis/cilium.io/v2/namespaces/kube-system/ciliumnetworkpolicies

2、Un-namespaced 类型

2.3 CRD

用户发现了 Kubernetes 的强大之后,希望将越来越多的东西(数据)放到 Kubernetes 里面, 像内置的 Pod、Service、NetworkPolicy 一样来管理,因此出现了两个东西:

  • CRD:用来声明用户的自定义资源,例如它是 namespace-scope 还是 cluster-scope 的资源、有哪些字段等等,Kubernetes 会自动根据这个定义生成相应的 API;官方文档的例子, 后文也将给出一个更简单和具体的例子。CRD 是资源类型定义,具体的资源叫 CR。
  • Operator 框架:“Operator” 在这里的字面意思是“承担运维任务的程序”, 它们的基本逻辑都是一样的:时刻盯着资源状态,一有变化马上作出反应(也就是 reconcile 逻辑)。

这就是扩展 API 的(最主要)声明和使用方式。

至此,我们讨论的都是一些比较抽象的东西,接下来通过一些例子和类比来更直观地理解一下。

基于 Spring Cloud Alibaba Gateway Nacos RocketMQ Vue & Element 实现的后台管理系统 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

3. 直观类比:Kubernetes 是个数据库,CRD 是一张表,API 是 SQL

在本节中,我们将创建一个名为 fruit 的 CRD,它有 name/sweet/weight 三个字段, 完整 CRD 如下:

代码语言:javascript复制
apiVersion: apiextensions.k8s.io/v1
        kind: CustomResourceDefinition
        metadata:
        name: fruits.example.org        # CRD 名字
        spec:
        conversion:
        strategy: None
        group: example.org              # REST API: /apis/<group>/<version>
  names:
          kind: Fruit
          listKind: FruitList
          plural: fruits
          singular: fruit
          scope: Namespaced               # Fruit 资源是区分 namespace 的
          versions:
          - name: v1                      # REST API: /apis/<group>/<version>
    schema:
            openAPIV3Schema:
            properties:
            spec:
            properties:
            comment:            # 字段 1,表示备注
            type: string
            sweet:              # 字段 2,表示甜否
            type: boolean
            weight:             # 字段 3,表示重量
            type: integer
            type: object
            type: object
            served: true                  # 启用这个版本的 API(v1)
            storage: true
            additionalPrinterColumns:     # 可选项,配置了这些 printer columns 之后,
            - jsonPath: .spec.sweet       # 命令行 k get <crd> <cr> 时,能够打印出下面这些字段,
        name: sweet                 # 否则,k8s 默认只打印 CRD 的 NAME 和 AGE
        type: boolean
        - jsonPath: .spec.weight
        name: weight
        type: integer
        - jsonPath: .spec.comment
        name: comment
        type: string

后面会解释每个 section 都是什么意思。在此之前,先来做几个(直观而粗糙的)类比。

3.1 Kubernetes 是个数据库

像其他数据库技术一样,它有自己的持久存储引擎(etcd),以及构建在存储引擎之上的 一套 API 和语义。这些语义允许用户创建、读取、更新和删除(CURD)数据库中的数据。下面是一些概念对应关系:

关系型数据库

Kubernetes(as a database)

说明

DATABASE

cluster

一套 Kubernetes 集群就是一个 database 【注 1】

TABLE

Kind

每种资源类型对应一个表;分为内置类型和扩展类型 【注 2】

COLUMN

property

表里面的列,可以是 string、boolean 等类型

rows

resources

表中的一个具体 record表中的一个具体 record

【注 1】 如果只考虑 namespaced 资源的话,也可以说一个 namespace 对应一个 database。

【注 2】 前面已经介绍过,

  • 内置 Kind:Job、Service、Deployment、Event、NetworkPolicy、Secret、ConfigMap 等等;
  • 扩展 Kind:各种 CRD,例如 CiliumNetworkPolicy。

所以,和其他数据库一样,本质上 Kubernetes 所做的不过是以 schema 规定的格式来处理 records。

另外,Kubernetes 的表都有自带文档‍:

代码语言:javascript复制
$ k explain fruits
        KIND:     Fruit
        VERSION:  example.org/v1

        DESCRIPTION:
<empty>

FIELDS:
        apiVersion   <string>
     APIVersion defines the versioned schema of this representation of an
             object. Servers should convert recognized schemas to the latest internal
             value, and may reject unrecognized values. More info:
             https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

             kind <string>
     Kind is a string value representing the REST resource this object
             represents. Servers may infer this from the endpoint the client submits
             requests to. Cannot be updated. In CamelCase. More info:
             https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

             metadata     <Object>
     Standard object's metadata. More info:
             https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

             spec <Object>

另外,Kubernetes API 还有两大特色:

  1. 极其可扩展:声明 CRD 就会自动创建 API;
  2. 支持事件驱动。

3.2. CRD 是一张表

CRD 和内置的 Pod、Service、NetworkPolicy 一样,不过是数据库的一张表。例如,前面给出的 fruit CRD,有 name/sweet/weight 列,以及 “apple”、“banana” 等 entry。

用户发现了 Kubernetes 的强大,希望将越来越多的东西(数据)放到 Kubernetes 里面来管理。数据类 型显然多种多样的,不可能全部内置到 Kubernetes 里。因此,一种方式就是允许用户创建自己的 “表”,设置自己的“列” —— 这正是 CRD 的由来。

1、定义表结构(CRD spec)

CRD(及 CR)描述格式可以是 YAML 或 JSON。CRD 的内容可以简单分为三部分:

常规 Kubernetes metadata:每种 Kubernetes 资源都需要声明的字段,包括 apiVersion、kind、metadata.name 等‍‍。

代码语言:javascript复制
 apiVersion: apiextensions.k8s.io/v1
        kind: CustomResourceDefinition
        metadata:
        name: fruits.example.org        # CRD 名字

Table-level 信息:例如表的名字,最好用小写,方便以后命令行操作‍;

代码语言:javascript复制
 spec:
        conversion:
        strategy: None
        group: example.org              # REST API: /apis/<group>/<version>
   names:
           kind: Fruit
           listKind: FruitList
           plural: fruits
           singular: fruit
           scope: Namespaced               # Fruit 资源是区分 namespace 的

Column-level 信息:列名及类型等等,遵循 OpenAPISpecification v3 规范。

代码语言:javascript复制
   versions:
        - name: v1                      # REST API: /apis/<group>/<version>
     schema:
             openAPIV3Schema:
             properties:
             spec:
             properties:
             comment:            # 字段 1,表示备注
             type: string
             sweet:              # 字段 2,表示甜否
             type: boolean
             weight:             # 字段 3,表示重量
             type: integer
             type: object
             type: object
             served: true                  # 启用这个版本的 API(v1)
             storage: true
             additionalPrinterColumns:     # 可选项,配置了这些 printer columns 之后,
             - jsonPath: .spec.sweet       # 命令行 k get <crd> <cr> 时,能够打印出下面这些字段,
        name: sweet                 # 否则,k8s 默认只打印 CRD 的 NAME 和 AGE
        type: boolean
        - jsonPath: .spec.weight
        name: weight
        type: integer
        - jsonPath: .spec.comment
        name: comment
        type: string

2、测试:CR 增删查改 vs. 数据库 SQL

创建 CRD:这一步相当于 CREATE TABLE fruits ...;。

代码语言:javascript复制
 $ kubectl create -f fruits-crd.yaml
        customresourcedefinition.apiextensions.k8s.io/fruits.example.org created

创建 CR:相当于 INSERT INTO fruits values(...);。

apple-cr.yaml‍:

代码语言:javascript复制
 apiVersion: example.org/v1
        kind: Fruit
        metadata:
        name: apple
        spec:
        sweet: false
        weight: 100
        comment: little bit rotten

banana-cr.yaml:

代码语言:javascript复制
 apiVersion: example.org/v1
        kind: Fruit
        metadata:
        name: banana
        spec:
        sweet: true
        weight: 80
        comment: just bought

创建‍:

代码语言:javascript复制
 $ kubectl create -f apple-cr.yaml
        fruit.example.org/apple created
        $ kubectl create -f banana-cr.yaml
        fruit.example.org/banana created

查询 CR:相当于 SELECT * FROM fruits ... ; 或 SELECT * FROM fruits WHERE name='apple';。

代码语言:javascript复制
 $ k get fruits.example.org # or kubectl get fruits
        NAME     SWEET   WEIGHT   COMMENT
        apple    false   100      little bit rotten
        banana   true    80       just bought

        $ kubectl get fruits apple
        NAME    SWEET   WEIGHT   COMMENT
        apple   false   100      little bit rotten

删除 CR:相当于 DELETE FROM fruits WHERE name='apple';。

代码语言:javascript复制
 $ kubectl delete fruit apple

可以看到,CRD/CR 的操作都能对应到常规的数据库操作。

3.3 API 是 SQL

上一节我们是通过 kubectl 命令行来执行 CR 的增删查改,它其实只是一个外壳,内部 调用的是 Kubernetes 为这个 CRD 自动生成的 API —— 所以又回到了本文第一节论述的内容:Kubernetes 的核心是其 API 框架。

只要在执行 kubectl 命令时指定一个足够大的 loglevel,就能看到背后的具体 API 请求。例如:

代码语言:javascript复制
$ 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
        ...

4. 其他

4.1 给 CR 打标签(label),根据 label 过滤

和内置资源类型一样,Kubernetes 支持对 CR 打标签,然后根据标签做过滤:

代码语言:javascript复制
# 查看所有 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

4.2 Kubernetes API 与鉴权控制(RBAC)

不管是内置 API,还是扩展 API,都能用 Kubernetes 强大的 RBAC 来做鉴权控制。

关于如何使用 RBAC 网上已经有大量文档;但如果想了解其设计,可参考 Cracking Kubernetes RBAC Authorization Model, 它展示了如何从零开始设计出一个 RBAC 鉴权模型(假设 Kubernetes 里还没有)。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

代码语言:javascript复制
文章有帮助的话,在看,转发吧。谢谢支持哟 (*^__^*)

0 人点赞