程序印象

Kubernets深入系列: API Server - part 3a(译)

2018/04/07 Share

作者:STEFAN SCHIMANSKI AND MICHAEL HAUSENBLAS AUGUST 15, 2017

翻译:狄卫华

原文:Kubernetes deep dive: API Server – part 3a

原文链接:https://blog.openshift.com/kubernetes-deep-dive-api-server-part-3a/


本系列翻译链接:


在本系列的前两部分中,我们回顾了API Server 中的一般流程以及如何使用 etcd 管理对象状态。 现在我们正在讨论如何扩展核心 API。

最初,扩展 API Server 的唯一方法是 Fork 和 Patch kube-apiserver 源代码并集成自己的资源。 或者可以尝试游说将新类型放入上游核心对象集合中。 但是这导致了一个问题:核心 API 将随着时间的推移而增长,可能导致 API 变得臃肿。 为了不让核心 API 变得太笨重,项目提出了两种扩展核心的方法:

  1. 使用的自定义资源定义(CRD),此前被称做第三方资源(TPR)。 通过这些 CRD,您可以轻松而灵活地定义自己的对象类型,并让 API 服务器处理整个生命周期。
  2. 使用与主 API Server 并行运行的用户 API Server(UAS)。 虽然涉及更多的先期投入与研发工作,但可以达到更细粒度控制对象行为。

此外,在扩展的上下文中,我们将讨论对象生命周期(从初始化到入场到终结)。 我们将在两篇文章中介绍 CRD的主题,因为它相当复杂(elaborate),我们正在处理 1.7 到 1.8 版本中的迁移工作。 在这篇文章中,我们关注 CRD,第二部分我们将向您展示如何为它编写自定义控制器,包括使用 kube-gen 生成代码。

声明和创建 CRD

正如我们在第1部分中讨论的那样,资源按 API Groups 分组,每个版本都有相应的 HTTP 路径。 现在,如果您要实现 CRD,首先要命名一个不能与现有核心组重叠的新 API Group。 在您自己的 API Group 中,您可以拥有尽可能多的资源,并且可以使用与其他组中可能存在的名称相同的名称。 我们来看一个具体的例子:

img

我们区分 CRD 与自定义资源 CR,CRD 就像面向对象编程中的类定义,而 CR 则可以看做是对应的类实例。 首先我们看一下类定义:在上面的第 1 行中,您会看到在 CRD 中必须使用 apiVersion ; kube-apiserver 1.7 和更高版本都支持该功能。 在第 5 行及以下,您使用 spec 字段来定义:

  • 第 6 行:CRD API 组(在我们的案例中,一个好的做法是使用组织的完整域名,如 example.com。
  • 第 7 行:CRD 对象的版本。 每个资源只有一个版本,但 API Group 中可能有多个版本不同的资源。

spec.names 字段有两个必需的子项:

  • 第 9 行 Kind按照惯例是大写单数(Database
  • 第 10 行:plural,按照惯例是小写复数(这里是 databases)。 您可以使用有趣的命名字段 plural 来定义资源HTTP路径,我们的示例为:https://<server/apis/example.com/v1/namespaces/default/databases。 还有一个可选的 singular 字段,默认为小写的类型值,可以在 kubectl 的上下文中使用。

此外,spec.names 还有许多可选字段,由 API Server 自动派生和填充。

请记住从第1部分可以看出,该类型描述了对象的类型,而资源则对应于 HTTP 路径。 大多数时候,这两个一致; 但在某些情况下,API Server 可能会在相同的 API HTTP 路径上返回不同的类型(例如:Status 错误对象明显是另一种类型)。

需注意资源名称(示例中的 database)和组(example.com)必须匹配元数据字段 name(请参见上面的第4 行)。

现在我们可以基于上述 YAML 规范创建 CRD:

1
2
$ kubectl create -f databases-crd.yaml
customresourcedefinition "databases.example.com" created

需注意,创建过程是异步过程,这意味着您必须检查 CRD 状态以显示检查指定的名称是否已被接受(即与其他资源没有名称冲突),并且 API Server 已经建立了要处理的 API 处理程序添加了新的资源。 在脚本或代码中,轮询是处理这种情况比较好的方式。

最终,我们得到这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ kubectl get crd databases.example.com -o yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: 2017-08-09T09:21:43Z
name: databases.example.com
resourceVersion: "792"
selfLink: /apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/databases.example.com
uid: 28c94a05-7ce4-11e7-888c-42010a9a0fd5
spec:
group: example.com
names:
kind: Database
listKind: DatabaseList
plural: databases
singular: database
scope: Namespaced
version: v1
status:
acceptedNames:
kind: Database
listKind: DatabaseList
plural: databases
singular: database
conditions:
- lastTransitionTime: null
message: no conflicts found
reason: NoConflicts
status: "True"
type: NamesAccepted
- lastTransitionTime: 2017-08-09T09:21:43Z
message: the initial names have been accepted
reason: InitialNamesAccepted
status: "True"
type: Established

在上面,你可以看到 kubectl已经获取到了我们之前定义的 CRD 并且能够提供关于它的状态信息;所有的名字都被接受而没有冲突,并且 CRD 已经被创建,也就是说,API Server 现在就提供对于该 CRD 的服务。

发现和使用CRD

在使用 kubectl proxy 在本地代理 Kubernetes API 后,我们可以发现我们在上一步中定义的数据库 CRD ,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ http 127.0.0.1:8001/apis/example.com
HTTP/1.1 200 OK
Content-Length: 223
Content-Type: application/json
Date: Wed, 09 Aug 2017 09:25:44 GMT

{
"apiVersion": "v1",
"kind": "APIGroup",
"name": "example.com",
"preferredVersion": {
"groupVersion": "example.com/v1",
"version": "v1"
},
"serverAddressByClientCIDRs": null,
"versions": [
{
"groupVersion": "example.com/v1",
"version": "v1"
}
]
}

请注意,kubectl 使用 〜/.kube/cache/discovery 目录默认缓存 10分钟内发现内容。 另外,kubectl 最多可能需要10分钟才能看到新的资源,例如我们定义的 CRD。 但是在高速缓存未命中时–也就是说,当 kubectl无法在命令中识别资源名称时—将立即被重新发现。

接下来,我们要使用 CRD 的一个实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat wordpress-database.yaml
apiVersion: example.com/v1
kind: Database
metadata:
name: wordpress
spec:
user: wp
password: secret
encoding: unicode

$ kubectl create -f wordpress-databases.yaml
database "wordpress" created

$ kubectl get databases.example.com
NAME KIND
wordpress Database.v1.example.com

要通过 API 直接监控资源创建和更新,您可以在某个 resourceVersion 上使用 watch 操作 (如第2部分所述)。首先,我们将查找定义的 Database CRD 的 resourceVersion,然后使用 curl 观察对该资源在单独的 shell 会话中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$ http 127.0.0.1:8001/apis/example.com/v1/namespaces/default/databases
HTTP/1.1 200 OK
Content-Length: 593
Content-Type: application/json
Date: Wed, 09 Aug 2017 09:38:49 GMT

{
"apiVersion": "example.com/v1",
"items": [
{
"apiVersion": "example.com/v1",
"kind": "Database",
"metadata": {
"clusterName": "",
"creationTimestamp": "2017-08-09T09:38:30Z",
"deletionGracePeriodSeconds": null,
"deletionTimestamp": null,
"name": "wordpress",
"namespace": "default",
"resourceVersion": "2154",
"selfLink": "/apis/example.com/v1/namespaces/default/databases/wordpress",
"uid": "8101a7af-7ce6-11e7-888c-42010a9a0fd5"
},
"spec": {
"encoding": "unicode",
"password": "secret",
"user": "wp"
}
}
],
"kind": "DatabaseList",
"metadata": {
"resourceVersion": "2179",
"selfLink": "/apis/example.com/v1/namespaces/default/databases"
}
}

所以在这里我们发现我们的 CRD 资源 /apis/example.com/v1/namespaces/default/databases/wordpress 实际数值 “resourceVersion" : "2154" ,这将用于带 curlWatch 的操作中:

1
$ curl -f 127.0.0.1:8001/apis/example.com/v1/namespaces/default/databases?watch=true&resourceVersion=2154

现在我们打开一个新的 shell 会话并删除 wordpress CRD 资源; 我们可以在原始会话中看到 watch 的通知:

1
$ kubectl delete databases.example.com/wordpress

注意:我们也可以使用 kubectl delete database wordpress,因为 Kubernetes 中没有预定义的 database 资源。 此外,单数字 database是我们 CRD 中的 spec.name.singular 字段,它自动根据英语语法派生。

在原始会话中,您启动了curl watch命令,现在可以从 API Server 看到实时更新:

1
2
3
{"type":"DELETED","object":{"apiVersion":"example.com/v1","kind":"Database","metadata":{"clusterName":"","creationTimestamp":"2017-0[0/515]
:38:30Z","deletionGracePeriodSeconds":null,"deletionTimestamp":null,"name":"wordpress","namespace":"default","resourceVersion":"2154","selfLink":"/apis/example.com/v1/namespaces/
default/databases/wordpress","uid":"8101a7af-7ce6-11e7-888c-42010a9a0fd5"},"spec":{"encoding":"unicode","password":"secret","user":"wp"}}}

综合起来,使用三个 shell 会话(底部的初始kube proxy 命令的附加命令),上面的命令和相应的输出如下所示:

img

最后,让我们看看 database CRD的各个数据在 etcd 中如何存储的。 在下面我们直接通过 HTTP API 访问主节点上的 etcd:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ curl -s localhost:2379/v2/keys/registry/example.com/databases/default | jq .
{
"action": "get",
"node": {
"key": "/registry/example.com/databases/default",
"dir": true,
"nodes": [
{
"key": "/registry/example.com/databases/default/wordpress",
"value": "{\"apiVersion\":\"example.com/v1\",\"kind\":\"Database\",\"metadata\":{\"clusterName\":\"\",\"creationTimestamp\":\"2017-08-09T14:53:40Z\",\"deletionGracePeriodSeconds\":null,\"deletionTimestamp\":null,\"name\":\"wordpress\",\"namespace\":\"default\",\"selfLink\":\"\",\"uid\":\"8837f788-7d12-11e7-9d28-080027390640\"},\"spec\":{\"encoding\":\"unicode\",\"password\":\"secret\",\"user\":\"wp\"}}\n",
"modifiedIndex": 670,
"createdIndex": 670
}
],
"modifiedIndex": 670,
"createdIndex": 670
}
}

从上面的 etcd 资源转储中可以看到,CRD 数据本质上是一个未解析的 blob。

请注意,删除 CRD 时所有实例也将被删除,即它是级联删除操作。

CRD的现状、限制、未来

目前,我们有 Kubernetes 1.7 作为稳定版本,预计2017 年 10 月初 1.8 版本发布。当前 CRD 的状态如下:

  • CRD 开始取代 Kubernetes 1.7 中 ThirdPartyResources(TPRs),而 TPRs 将在 Kubernetes 1.8 中取消。
  • TPR 到 CRD 可以进行简单迁移。
  • 每个 CRD 支持单个版本,但是每个组可能有多个版本。
  • CRD 提供了一致的 API,并且从用户的角度来看与本地资源基本上没有区别。
  • CRD 在后续版本中更多功能的稳定基础功能,例如:

现在您已经看到了如何使用 CRD,接下来我们将讨论其局限性:

  • 它们不提供版本转换功能,也就是说,每个 CRD 只能有一个版本(预计不会在近期或中期看到转换支持)。
  • 目前没有验证,但可能会在 1.8 版本中出现,请参阅 Google Summer of Code 项目。并且使用即将推出的JSON 模式(不是图灵完备)有限的验证能力。
  • 没有快速的进程内入场可能(但支持初始化和入场的webhooks)
  • 您无法定义子资源,例如 scalestatus,但有一个正在进行中的 proposal
  • 最后但并非最不重要的是,CRD 目前不支持默认值,即将默认值分配给某些字段(这可能出现在后续某一个版本中)。

为了解决上述问题并以更灵活的方式扩展 Kubernetes,您可以使用与主 API Server 并行运行的 用户 API Server(UAS)。我们将在本博客文章系列的下一篇文章中详细介绍如何编写 UAS。下次我们将通过为其编写自定义控制器来完成 CRD。


除特别声明本站文章均属原创(翻译内容除外),如需要转载请事先联系,转载需要注明作者原文链接地址。


CATALOG
  1. 1. 声明和创建 CRD
  2. 2. 发现和使用CRD
  3. 3. CRD的现状、限制、未来