换个角度看Kubernetes 的核心

举报
在逃coder 发表于 2022/06/27 10:02:29 2022/06/27
【摘要】 本文来自微信公共号:分布式实验室。文章观点独特,比喻形象,分享给大家~ 论述了 Kubernetes 的核心价值是其通用、跨厂商和平台、可灵活扩展的声明式 API 框架, 而不是容器(虽然容器是它成功的基础);然后手动创建一个 API Extension(CRD), 通过测试和类比来对这一论述有一个更直观的理解。

本文来自微信公共号:分布式实验室。论述了 Kubernetes 的核心价值是其通用、跨厂商和平台、可灵活扩展的声明式 API 框架, 而不是容器(虽然容器是它成功的基础);然后手动创建一个 API Extension(CRD), 通过测试和类比来对这一论述有一个更直观的理解。

例子及测试基于 Kubernetes v1.21.0。


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


容器是基础

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

  • 越来越多的开发者开始采用容器作为一种标准构建和运行方式。

  • 业界也意识到,很容易将这种封装方式引入计算集群,通过 Kubernetes 或 Mesos 这样的编排器来调度计算任务 —— 自此,容器便成为这些调度器最重要的 Workload 类型。

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

API 才是核心


640.jpg


“等等 —— 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 发行版中包含越来越多的自定义资源类型。

小结

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

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

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


 2 
Kubernetes 的 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)

1、Namespaced 类型

API 格式:

  • 格式:/apis/{apiGroup}/{apiVersion}/namespaces/{namespace}/{resource}

  • 举例:/apis/cilium.io/v2/namespaces/kube-system/ciliumnetworkpolicies

2、Un-namespaced 类型

CRD

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

  • CRD:用来声明用户的自定义资源,例如它是 namespace-scope 还是 cluster-scope 的资源、有哪些字段等等,Kubernetes 会自动根据这个定义生成相应的 API;官方文档的例子[1], 后文也将给出一个更简单和具体的例子。CRD 是资源类型定义,具体的资源叫 CR。

  • Operator 框架:“Operator” 在这里的字面意思是“承担运维任务的程序”, 它们的基本逻辑都是一样的:时刻盯着资源状态,一有变化马上作出反应(也就是 reconcile 逻辑)。

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

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


 3 
直观类比:Kubernetes 是个数据库,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: 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,表示重量
                typeinteger
            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
      typeinteger
    - jsonPath: .spec.comment
      name: comment
      type: string

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

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 的表都有自带文档‍:

$ 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. 支持事件驱动。

CRD 是一张表

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

640.png

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

1、定义表结构(CRD spec)

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

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

 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   name: fruits.example.org        # CRD 名字

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

 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 规范。

   versions:
   - name: v1                      # REST API: /apis/<group>/<version>
     schema:
       openAPIV3Schema:
         properties:
           spec:
             properties:
               comment:            # 字段 1,表示备注
                 type: string
               sweet:              # 字段 2,表示甜否
                 type: boolean
               weight:             # 字段 3,表示重量
                 typeinteger
             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
       typeinteger
     - jsonPath: .spec.comment
       name: comment
       type: string

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

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

 $ kubectl create -f fruits-crd.yaml
 customresourcedefinition.apiextensions.k8s.io/fruits.example.org created

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

apple-cr.yaml‍:

 apiVersion: example.org/v1
 kind: Fruit
 metadata:
   name: apple
 spec:
   sweet: false
   weight: 100
   comment: little bit rotten

banana-cr.yaml:

 apiVersion: example.org/v1
 kind: Fruit
 metadata:
   name: banana
 spec:
   sweet: true
   weight: 80
   comment: just bought

创建‍:

 $ 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';。

 $ 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';。

 $ kubectl delete fruit apple

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

API 是 SQL

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

 4 
其他


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

和内置资源类型一样,Kubernetes 支持对 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

Kubernetes API 与鉴权控制(RBAC)

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

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

相关链接:

  1. https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#create-a-customresourcedefinition

  2. https://arthurchiao.art/blog/cracking-k8s-authz-rbac/

参考链接:

  1. https://blog.joshgav.com/2021/12/16/kubernetes-isnt-about-containers.html

  2. https://github.com/gotopple/k8s-for-users-intro/blob/master/database.md

  3. https://itnext.io/crd-is-just-a-table-in-kubernetes-13e15367bbe4


原文阅读:https://mp.weixin.qq.com/s/MEhDJYg_lmjbPEdFFLdedg

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。