Skip to main content

从Kubernetes API谈起

在正式深入apimachinery源码之前,我们首先需要介绍与Kubernetes API相关的一些重要术语,这是我们理解apimachinery库的前提。

这些术语是本书论述的基础,它们与理解自定义控制器的实现以及Kubernetes二次开发直接相关。 同时这些专有名词在概念上非常相似,也请你尽量保持耐心。

我们所说的Kubernetes API是指Kubernetes组件kube-apiserver(Kubernetes API server)暴露的HTTP API。 Kubernetes API大体上1是基于资源(Resource)2RESTful3风格的编程接口,它支持通过标准HTTP动词(POST、PUT、PATCH、DELETE、GET)来检索、创建、更新和删除集群资源

根据文档Declarative application management in Kubernetes ,Kubernetes API Server被设计成系统唯一的对外交互接口。 不管是客户端工具(kubectl),UI(dashboard),还是Kubernetes系统内部其他组件(kubelet,内置的控制器(Controller)4等)在背后,它们最终仍然指向Kubernetes API。

你可以在Kubernetes API的OpenAPI specification文件 中找到所有API的schema。 另外,Kubernetes API reference 以文档的形式记录所有了所有API接口的详细说明。

Kubernetes资源

我们介绍的第一个API术语是Kubernetes资源(Resource)

HTTP与Resource

在介绍Kubernetes资源之前,我想首先引入一个与它极为相似的概念——HTTP(或是Web)语境下的资源,私以为这有助于我们理解Kubernetes资源

HTTP语境下Resource的定义

HTTP语境下的Resource的概念首次在RFC 1630中伴随URI(Uniform Resource Identifier)的概念而引入,关于它的明确说明直到RFC 3986中才正式出现,不过仍然以URI的角度阐述:

Resource

This specification does not limit the scope of what might be a resource; rather, the term "resource" is used in a general sense for whatever might be identified by a URI.

意思是说,术语Resource可以是URI所指代的任何内容;

RFC 3986之前,RESTful风格的提出者Roy T. Fielding在其关于REST的学位论文也给出了Resource的定义:

5.2.1.1 Resources and Resource Identifiers

The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"), a collection of other resources, a non-virtual object (e.g. a person), and so on.

意思是说,REST风格中的Resource是指可以被命名的任何信息:它可以是一个文档,一张图片,(甚至可以是)洛杉矶的当日温度等等;

总体上来说,不管是RFCs还是REST的论文中关于Resource的说明或者定义是抽象的。 不过,基于上述略显晦涩的说明,网络或者书籍中上也有对HTTP资源更为通俗的解释。例如:MDN Web Docs资源给出的解释:

The target of an HTTP request is called a "resource", whose nature isn't defined further; it can be a document, a photo, or anything else.

也就是:HTTP请求的"目标"即为资源,它可以是文档、图片或者是其他(存储在服务端的)任何"内容"

这些"内容"也可以是数据库中的一行数据,也可以是算法的执行结果等5

HTTP资源的表现形式

尽管RFC 3986或者MDN Web Docs都强调了资源没有任何形式上的限制,但通常,HTTP资源应当可以被表示成比特流(字节序列)5。 服务端可以为资源提供不同的表现格式,例如,使用JSON或者XML(编码后以在网络中以字节流的形式传输)5。 客户端也可以使用HTTP内容协商来与服务端协商资源的具体表现形式。

从HTTP资源到Kubernetes资源——初识Kubernetes资源

在了解了HTTP语境下的资源之后,我们再来认识Kubernetes资源

在文档API Conventions 中,我们可以找到官方社区对Kubernetes资源的一种描述:

Resource a representation of a system entity, sent or retrieved as JSON via HTTP to the server.

意思是说:Kubernetes资源代表了集群中的一个"存在物"6,通过HTTP,它以JSON的形式在客户端与Kubernetes API Server间传输。

这个描述其实与HTTP资源的概念极为相似。 只不过对于Kubernetes集群来说,在API URI的另一侧并不是Web语境下的文档,图片等,而是集群中的一个"存在物"——常见的podsdeploymentsservices等都是Kubernetes资源,它们也都有对应的JSON格式在网络中传输。

当然,文档API Conventions Kubernetes资源的描述还是稍显模糊。 我们能否像RFCs的风格一般对Kubernetes资源这个术语作出严格的定义呢? 幸运的是,在Kubernetes API Concepts 文档中,官方其实对Kubernetes API相关术语也有更为严谨的描述:

Kubernetes API terminology

Kubernetes generally leverages common RESTful terminology to describe the API concepts:

  • A resource type is the name used in the URL
  • All resource types have a concrete representation (their object schema) which is called a kind
  • A single instance of a resource type is called a resource, and also usually represents an object

事情似乎看起来并不简单,为了定义Kubernetes资源,官方还引入了resource typekind的概念。 本节接下来的内容,便是对上述定义的详细解读。请跟随我一起开始了解这些术语吧。

API分组(group)和版本化(version)

为了解释清楚resource typekind、以及resource这三个术语,我们需要从Kubernetes API URL的结构说起。

为了便于扩展和迭代API,Kubernetes对所有API进行分组(API groups)和版本化(version)7。 具体来说,所有Kubernetes API路径都会带上分组和版本信息,它们通常符合形式apis/{group}/{version}/...并以此形式作为API的根路径。

例如:API/apis/batch/v1/... 的分组是batch,版本是v1

需要注意的是,API分组的名称并不局限于一个单词,一个常见到的命名模式是(伪)子域名8风格,例如: API/apis/networking.k8s.io/v1alpha1的分组是networking.k8s.io,版本是v1alpha1。 特别是通过CRD扩展Kubernetes API时,使用组织的伪子域名作为API分组名称似乎已经成为一种约定,例如: istio组织使用networking.istio.io作为分组名来扩展Kubernetes API。

API分组的历史

我们在这里有必要提及一下Kubernetes API引入分组的历史。

Kubernetes v1.1.0以前,Kubernetes API只有版本的概念,每个版本对应不同的API路径前缀,例如api/v1//api/v1beta3等。 在Kubernetes v1.1.0-beta,官方开发者正式发起API分组的提案: Pull Request #12951。 在该提案中,官方开发者认为API引入分组的目的和相应的好处有:

  1. Breaking the monolithic v1 API into modular groups and allowing groups to be enabled/disabled individually. This allows us to break the monolithic API server to smaller components in the future.
  2. Supporting different versions in different groups. This allows different groups to evolve at different speed.

意思是说:

  1. 将单个"整块"的v1 API分组模块化,并且允许单独启用/禁用某个分组。这使我们能够将API服务端分解为更小的组件。
  2. 在同一个分组中可以有不同的版本。这使得不同的分组间可以以不同的速度并行迭代。

在操作细节上,作者提出:

  • 对于已有的API,为了向前兼容,它们的API路径维持不变;
  • 对于未来可能出现的新API分组,根路径需要为apis/而非原先的api/,同时将组名及版本信息依次置于apis/后,即apis/{group}/{version}/

因此,并不是所有的Kubernetes API都带有分组信息。例如:在分组之前就存在的APIapi/v1/namespaces/default/pods仍然维持不变。

对于新的分组,在Kubernetes v1.2.0-alpha.2正式迎来了第一个新的API分组extensions,它的API REST路径是apis/extensions/。 像后来大家熟知的deploymentsdaemonsetsjobs等资源类型最开始都被放置在这个实验性质的分组中"孵化"。 在Kubernetes v1.2.0-beta.0jobsextensions分组正式迁移到batch分组: Pull Request #21434; 在Kubernetes v1.6.0-beta.1deploymentsextensions分组正式迁移到apps分组: Pull Request #39683

API版本的历史

对于Kubernetes API版本的历史,可以说自Kubernetes源码公开以来kube-apiserver的API就已经带有版本信息。 根据(公开的)Kubernetes源码的第一个提交2c4b3a5

cmd/apiserver/apiserver.go
var apiPrefix = flag.String("api_prefix", "/api/v1beta1", "The prefix for API requests on the server. Default '/api/v1beta1'")

可以看出公开的Kubernetes源码中已知的第一个API版本为/api/v1beta1

资源类型(resource type)

Kubernetes API URI9按照是否含有命名空间(namespace)10的信息又可以分为两类:

  • /apis/{group}/{version}/...
  • /apis/{group}/{version}/namespaces/{namespace}/...

在上述两类的URL模式中,紧跟其后的路径片段被称为资源类型(resource type)。 即,

  • /apis/{group}/{version}/{resource type}/...
  • /apis/{group}/{version}/namespaces/{namespace}/{resource type}

例如:

  • API/apis/authorization.k8s.io/v1/subjectaccessreviews对应的资源类型是subjectaccessreviews;
  • API/api/batch/v1/namespaces/default/jobs对应的资源类型是jobs;

另外,资源类型作为API URI中的一个标识,它们是小写的且为复数形式的。

上述的定义来自于:

Kubernetes API terminology

A resource type is the name used in the URL

官方的描述其实还是略显模糊,因为此描述并没有解释name又是什么。但是至少透露出一个信息:resource type是基于Kubernetes API URL的一个概念。 因此我们根据官方想要表达的意思,基于Kubernetes API URI的规则,给出了我们对resource type的定义。

需要在这里提醒的是,依据官方的描述,资源类型是Kubernetes API URI中一个特定位置的标识——我们其实已经在没有引入资源的前提下定义了资源类型

初识11kind

我们现在已经知道资源类型是Kubernetes API URI中特定位置中使用的一个标识(identifier)。 在Kubernetes源码中,每种资源类型Go语言中都对应一个具体的类型(Golang type),这些类型被统称为kind。例如,资源类型cronjobs对应的Go类型是Cronjob

k8s.io/api/batch/v1/types.go
type CronJob struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

Spec CronJobSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
Status CronJobStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}

与其他语言一样,Go语言也有命名约定(name conventions)。对于类型(type),Go中的约定是使用 lower camel case(小驼峰式)或者upper camel case(大驼峰式)来命名多词名称。 同时,如果一个类型需要在包外可见,它的第一个字母应该是大写的。Kubernetes源码遵循这种命名约定,kind作为Go类型,类型名称自然符合驼峰形式。 此外,相比于资源类型是复数,kind名称则是单数形式。

所有Kubernetes原生的kind被定义在k8s.io/api库中。另外,不同于分组和版本在URL上以路径片段存在的形式,在k8s.io/api库中,每个分组和版本都对应一个目录(包):

api库目录结构
|-- admission
| |-- v1
| `-- v1beta1
|-- admissionregistration
| |-- v1
| |-- v1alpha1
| `-- v1beta1
|-- apidiscovery
| `-- v2beta1
|-- apiserverinternal
| `-- v1alpha1
|-- apps
| |-- v1
| |-- v1beta1
| `-- v1beta2
|-- authentication
| |-- v1
| |-- v1alpha1
| `-- v1beta1
|-- authorization
| |-- v1
| `-- v1beta1
|-- autoscaling
| |-- v1
| |-- v2
| |-- v2beta1
| `-- v2beta2
|-- batch
| |-- v1
| `-- v1beta1
|-- certificates
| |-- v1
| |-- v1alpha1
| `-- v1beta1
|-- coordination
| |-- v1
| `-- v1beta1
|-- core
| `-- v1
|-- discovery
| |-- v1
| `-- v1beta1
|-- events
| |-- v1
| `-- v1beta1
|-- extensions
| `-- v1beta1
|-- flowcontrol
| |-- v1alpha1
| |-- v1beta1
| |-- v1beta2
| `-- v1beta3
|-- imagepolicy
| `-- v1alpha1
|-- networking
| |-- v1
| |-- v1alpha1
| `-- v1beta1
|-- node
| |-- v1
| |-- v1alpha1
| `-- v1beta1
|-- policy
| |-- v1
| `-- v1beta1
|-- rbac
| |-- v1
| |-- v1alpha1
| `-- v1beta1
|-- resource
| `-- v1alpha2
|-- scheduling
| |-- v1
| |-- v1alpha1
| `-- v1beta1
|-- storage
| |-- v1
| |-- v1alpha1
| `-- v1beta1

kind被定义在不同的目录中以作区分。

分组和版本除了在包结构上的体现以外,它们也被记录在每个kindapiVersion字段中,例如:JobapiVersion字段为batch/v1DeploymentapiVersion字段为apps/v1

至于为什么用一个版本字段同时记录分组以及版本信息的原因最早可以追溯到引入API分组的提案: Pull Request #12951。 其实在没有API分组以前,kindapiVersion字段仅用于记录API版本信息。例如v1。 在提案中,作者为了加快开发进度12决定不新增字段而是继续使用apiVersion字段,只是以{version}/{group}字符串拼接的方式把分组和版本信息一并放入apiVersion字段。 同时,对于已经存在的kind,例如PodapiVersion字段也还是保留为v1。 在Kubernetes v1.2.0-alpha.2,社区在迎来第一个新的分组extensions的同时,原先就存在的kind也拥有了一个正式的分组名——core(核心)13

从Kubernetes kind到Kubernetes资源——再识Kubernetes资源

kind作为Kubernetes源码中的Go类型,站在集群运行的角度,Kubernetes组件(kube-apiserverkubelet等)在运行时可以在内存中创建kind的具体实例(instance),这些实例也可以通过序列化以字节的形式或在网络中传输(JSON格式)或写入磁盘中(etcd)。 简而言之,kind的具体实例被称作资源(resource)。在Kubernetes中,每个资源都有属于自己的一个名字。

同时,每个资源也独立地对应一个API URI——将资源的名字(作为路径片段)追加在资源类型后即可: /apis/{group}/{version}/namespaces/{namespace}/{resource type}/{name}。 例如:一个资源类型jobs且名字为my-job资源对应的API URI为/api/batch/v1/namespaces/default/jobs/my-job

有时,在相关技术文章和博客中,我们甚至也可以见到用资源的API URL指代资源本身的情况。例如: Kubernetes社区核心贡献者 Jordan Liggitt 在StackOverflow上的一个回答:

A specific URL used to obtain the object is a resource. For example, a list of v1 Pod objects can be obtained from the /api/v1/pods resource. A specific v1 Pod object can be obtained from the /api/v1/namespaces/<namespace-name>/pods/<pod-name> resource.

总之,术语Kubernetes资源被广泛用于各种文档和博客中,但是它的实际意义需要根据不同的上下文具体分析得出。

小结

总结

从Kubernetes API的规则到资源类型,从资源类型再到kind的定义,从kind再到资源, 我们按照文档Kubernetes API terminology 的思路严格地定义了这些术语。 经验告诉我们,越是严格的定义往往越是晦涩。

当然这种严格也相应地带来一些好处——我们可以更为全面地引申Kubernetes资源的所指。 Kubernetes资源既然是kind的实例,那么它们将并不局限于以JSON的形式存在,它们也可以是:YAML格式甚至是(实例序列化后的)字节流(bytes)的形式等。 同时,在Kubernetes系统中,这些数据形式又存在于多种媒介中:内存(kubectlkube-apiserverkubelet等)、网络(HTTP)、文件(YAML)、磁盘(etcd)。

在绝大多数时候,我们对Kubernetes资源的第一种认知已经足够(即便它可能是模糊的)。 我们其实也可以基于第一种认知它去理解资源类型以及kind

  • 资源类型Kubernetes资源所属种类的名称,它们(以小写和复数的形式)作为API URL中特定位置的一个标识。
  • kindKubernetes资源JSON格式在序列化前/反序列化后对应的Go类型。当然,如果不局限于具体语言,广义上,我们也可以把kind认为是Kubernetes API的JSON Schema14

其它API术语

到现在,我们已经了解了资源资源类型kind 这些API术语。其实还有一些其他术语,它们同样与Kubernetes二次开发息息相关;

这些术语包括:虚拟资源Kubernetes对象,以及子资源

初识Kubernetes虚拟资源

在正式介绍Kubernetes虚拟资源之前,我想先从一个特殊的Kubernetes API说起:

/apis/authorization.k8s.io/v1/selfsubjectaccessreviews

这个API可以用来判断我们对某个资源类型是否有相应的权限15。 例如:假如我们想判断自己是否在default命名空间下创建deployments资源类型的权限,我们可以通过如下POST请求:

POST /apis/authorization.k8s.io/v1/selfsubjectaccessreviews
{
"apiVersion": "authorization.k8s.io/v1",
"kind": "SelfSubjectAccessReview",
"spec": {
"resourceAttributes": {
"group": "apps",
"resource": "deployments",
"verb": "create",
"namespace": "default"
}
}
}

此API请求的一个返回是:

{
"apiVersion": "authorization.k8s.io/v1",
"kind": "SelfSubjectAccessReview",
"metadata": {
"creationTimestamp": null
},
"spec": {
"resourceAttributes": {
"group": "apps",
"resource": "deployments",
"namespace": "dev",
"verb": "create"
}
},
"status": {
"allowed": true,
"denied": false
}
}

我们可以通过返回体中status字段的allowed来判断我们是否含有相应的权限。

根据我们先前的定义,这个API的分组为authorization.k8s.io版本为v1,资源类型为selfsubjectaccessreviews。 另外,这个资源类型在Kubernetes源码k8s.io/api库中对应的Go类型是SelfSubjectAccessReview

k8s.io/authorization/v1/types.go
type SelfSubjectAccessReview struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

Spec SelfSubjectAccessReviewSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`

Status SubjectAccessReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}

SelfSubjectAccessReview类型的实例(经过序列化/反序列化)以JSON的形式在客户端与Kubernetes API Server间传输。

因此,如果根据定义,SelfSubjectAccessReview类型的实例仍然属于Kubernetes资源,只是它相比于我们熟知的资源pod等有所不同——它们并不代表集群中的任何"实体"16。 它们仅作为Kubernetes API Server中某些特殊API与客户端间HTTP通信的"消息载体"(payload)。这类Kubernetes资源被称为虚拟资源

在这里,我们通过一个例子引入虚拟资源。 在后面的小节中,我们还会用更为严谨的叙述来定义Kubernetes虚拟资源。 就像我们定义资源一样,严谨往往意味着我们需要引入额外的概念来辅助解释它本身。

从Kubernetes API的声明式配置说起

为了定义什么是虚拟资源,我们还需要从Kubernetes API的设计说起。 不过在介绍本小节内容之前,我们要事先了解两个基础概念:命令式系统声明式系统

命令式系统与声明式系统
  • 命令式系统中,用户知道系统的期望状态,由用户来决定系统达到期望状态的命令序列,并向系统提供命令序列。
  • 声明式系统中,用户知道系统的期望状态,用户仅向系统提供期望状态的描述,由系统来确定从当前状态达到期望状态所需要的动作序列。
一个简单的例子
我们以泡一壶茶为例。 在声明式系统中,用户只要提供一个期望状态的描述即可:
- 请给我一杯40度的绿茶

而在命令式系统中,用户则需要向系统达到目标状态的完整的命令序列:

- 将水烧至100度
- 把水倒入茶杯中
- 放入一个绿茶包
- 等待水温降至40度

关于声明式系统和命令式系统完整的介绍以及与Kubernetes的联系可以参考Kubernetes文档特别兴趣小组成员Andrew Chen的博文: 。

根据Kubernetes设计文档The Kubernetes Resource Model (KRM) ,Kubernetes被设计成一个声明式资源管理系统。 而Kubernetes API作为系统唯一的对外交互接口也因此被设计成支持资源的声明式配置(Declarative Configuration)。 所谓资源的声明式配置是指用户向Kubernetes API仅提供资源的期望状态的描述,接下来交由系统控制以使资源达到其期望状态。

例如,当我们通过Kubernetes API创建一个Deployment资源时,

POST apis/apps/v1/deployments
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "test",
"labels": {
"app": "test"
}
},
"spec": {
"replicas": 3,
"template": {
"metadata": {
"name": "test",
"labels": {
"app": "test"
}
},
"spec": {
"containers": [
{
"name": "test",
"image": "busybox",
"imagePullPolicy": "IfNotPresent"
}
],
"restartPolicy": "Always"
}
},
"selector": {
"matchLabels": {
"app": "test"
}
}
}
}

其中,JSON格式的请求体(资源)可以理解成是一种对集群期望状态的描述。 用户将replicas字段设置为3其实表示用户期望此部署要有3个实例运行在集群中。 Kubernetes则会采取一系列动作来确保集群中始终有3个实例在运行——例如,当某个实例运行的节点发生故障,kubelet则会在另一个节点中创建新的实例以确保资源的当前状态与用户的期望状态保持一致。 显然,Kubernetes系统需要记录资源的期望状态才能在之后不断将资源的当前状态与期望状态"对齐"。

Kubernetes对象(Kubernetes Object)

正如在上一小节中所说的那样,Kubernetes需要记录资源的期望状态。 而在Kubernetes中,那些需要持久化(存入etcd)的资源实例(persistent entity)被称为Kubernetes对象(Kubernetes Object)。 同时,Kubernetes对象也可以理解成一种对"资源期望状态的记录(record of intent)"。

Kubernetes对象的定义来自于对三个官方文档的整合:

官方文档中关于Kubernetes对象的解释
  • Objects In Kubernetes

    Understanding Kubernetes objects

    Kubernetes objects are persistent entities in the Kubernetes system. Kubernetes uses these entities to represent the state of your cluster.

  • Kubernetes API Concepts

    Kubernetes API terminology

    Most Kubernetes API resource types are objects – they represent a concrete instance of a concept on the cluster, like a pod or namespace.

  • API Conventions

    [Types (Kinds)](https: //github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds)

    Objects represent a persistent entity in the system. Creating an API object is a record of intent - once created, the system will work to ensure that resource exists. All API objects have common metadata.

根据定义,Kubernetes对象不仅可以是deploymentsdaemonsets这样"实打实"的集群工作负载(workloads), 它们也可以是servicesconfigmapsclusterrolesclusterrolebindingsserviceaccounts——只要它们被Kubernetes存入etcd中。

另外,通常所说的Kubernetes"实体"就是指Kubernetes对象

spec和status

几乎所有的Kubernetes对象都包含两个字段:specstatus17。用户/客户端在创建对象时,spec字段用以描述资源的期望状态; status字段则用于记录和更新资源的当前状态——这个字段通常是由Kubernetes系统内部组件或者控制器(Controller)4更新;

并不是所有的Kubernetes对象都包含这两个字段,例如:configmapsecret,它们并不包含specstatus字段,但它们有一个data字段用于记录要存储的数据; 所以,我们也可以认为并不是所有的Kubernetes对象都必须带有"状态"的属性,它们有的单纯只是需要存储的数据;

再识Kubernetes虚拟资源

基于Kubernetes对象的定义,换言之,Kubernetes中存在部分资源并不需要被存入etcd中。这些资源被成为虚拟资源(virtual resource)

对于虚拟资源,即便它们并不具有"状态"的属性,也不需要被存入etcd中,但是它们往往也会包含specstatus字段(例如subjectaccessreview)。 按照社区的说法18,这样做的原因是为了在风格上保持一致;

小结

至此,我们知道Kubernetes资源可以分为两类:Kubernetes对象以及Kubernetes虚拟资源

尽管资源包括Kubernetes对象资源的概念更倾向于在Kubernetes API上下文中使用19, 而Kubernetes对象更被侧重用于对集群期望状态的一种描述。 在不严格区分定义的情况下,资源Kubernetes对象两个概念往往可以被互换使用。

下图总结了Kubernetes资源Kubernetes对象的区别和联系:

;

子资源(subresource)

在Kubernetes API中,还存在一类特殊的API。它们的结构为在普通资源对应的URI后继续追加一个路径片段得到的URI,即: /apis/{group}/{version}/namespaces/{namespace}/{resource type}/{name}/{subresource}, 其中最后追加的标识被称为为子资源。 需要注意的是,子资源并不对应集群中任何实体或虚拟资源,它们旨在提供与资源资紧密耦合的某种操作或视角。 例如,

  • API /api/v1/namespaces/{namespace}/pods/{name}/log可以检索某个pod的日志;
  • API /api/v1/namespaces/{namespace}/pods/{name}/portforward可以将本地的端口转发到某个pod中监听的一个或多个端口上;
  • API /api/v1/namespaces/{namespace}/pods/{name}/exec可以远程在某个pod的容器中执行命令;
  • API /api/v1/namespace/{namespace}/pods/{name}/status 可以检索或者更新某个pod的当前状态。

除了API URI Path上的特殊性外,这些API的特殊性主要体现在以下两点:

  1. 不符合Kubernetes API RESTful风格,它们中有的(例如execportforward)甚至采用了Websocket协议
  2. 不符合Kubernetes API声明式配置(declarative configuration)规范。

需要注意的是,在Kubernetes中所有Kubernetes对象都有status子资源用于检索或者更新资源的当前状态。 我们在后续Controller小节中还会再次提及status子资源。

子资源的历史

关于引入子资源的历史最早可以追溯到Kubernetes社区的一个讨论Issue #2776。 这个讨论涉及到应该如何恰当地更新Kubernetes对象的当前状态。 这个问题本质上是由于在没有引入子资源以前, 客户端/用户对资源的修改以及Kubernetes系统内组件对资源当前状态的更新会存在并发控制和一致性问题 ,尤其是当更新频繁的时候,这种并发冲突会愈发地严重。 基于此,开发者认为需要将对资源spec字段的更新与status字段的更新分离。 在这个讨论里,官方开发者们提出了很多想法,他们最后采纳了子资源的提议:在普通资源对应的API URL后追加一个路径片段/status暴露一个资源的"子API", 这个"子API"的请求体仍然是一个完整的资源,但是这个"子API"仅能用于更新资源的status字段而会忽略掉资源的其他字段(spec)。 最终,子资源的实现 Pull Request #4779被合并,并在Kubernetes v0.13.0中发布;

小结

小结

在本节中,我们给出了很多似乎有些晦涩而又相似的概念,这些概念又好像在描述同一个东西。 但是区分和了解这些API术语是Kubernetes二次开发的基础并且有助于我们理解自定义控制器的诸多细节。

总之,本节的各个术语确实容易混淆,为了方便读者区分这些概念,本节的内容可以被总结成下图。


  1. 并不是所有的Kubernetes API都严格基于RESTful风格。Kubernetes API Server部分API基于HTTP增强协议WebSocketSPDY。 例如,当你使用kubectl exec命令时,kubectlkube-apiserver之间基于WebSocket协议,kube-apiserverkubelet间的通信基于SPDY协议。

  2. Kubernetes资源在各类文章和博客中被广泛使用,但大多数时候对这个专有名词的使用并不准确。本节的一个使命就是基于Kubernetes文档就对这些广泛使用的术语给出精确的表述。

  3. 关于什么是RESTful请参考提出者Roy T. Fielding的学位论文

  4. 控制器(Controller)的概念我们会在后续controller章节详细介绍它。

  5. Leonard Richardson, Sam Ruby. RESTful Web Services Ch. 4.

    A resource is anything that’s important enough to be referenced as a thing in itself. If your users might “want to create a hypertext link to it, make or refute assertions about it, retrieve or cache a representation of it, include all or part of it by reference into another representation, annotate it, or perform other operations on it”, then you should make it a resource.

    Usually, a resource is something that can be stored on a computer and represented as a stream of bits: a document, a row in a database, or the result of running an algorithm.

  6. 我们并没有把"entity"翻译为"实体"是因为Kubernetes资源也可能是集群中一个"虚拟"的"内容"。我们接下来还会在本节其他API术语详细介绍它们;

  7. API Overview

    API groups

    API groups make it easier to extend the Kubernetes API.

  8. 伪子域名表示并非一定真实存在这样的域名,它仅指代一种风格。

  9. API URI也可以理解成API endpointAPI path或者API REST path。这些术语在Kubernetes官方文档中等价并被交换使用。

  10. 命名空间的概念属于读者需要掌握的预备知识之一,我们不在这里多做赘述。

  11. 在这里,我们仅仅是引入了kind的概念,在后续章节中我们还会继续对kind作出更为全面的阐述。

  12. Supporting multiple API groups

    Bookkeeping for groups

    For a fast implementation targeting the v1.1 deadline, we will concatenate group with version, in the form of "group/version", and use it where a version string is expected, so that many code can be reused.

  13. 这是我们能够找到关于core分组名称由来最早的记录:API Conventions(v1.2.0-alpha.2)

    API groups

    Currently there are two API groups in use:

    1. the "core" group, which is at REST path /api/v1 and is not specified as part of the apiVersion field, e.g. apiVersion: v1.

    2. the "extensions" group, which is at REST path /apis/extensions/$VERSION, and which uses apiVersion: extensions/$VERSION (e.g. currently apiVersion: extensions/v1beta1).

    另外,core分组在文档API Overview 中也被称为历史遗留组(legacy group)

  14. JSON Schema

  15. Kubernetes权限管理本身并不在本书讨论范围之内,请参考文档authentication

  16. 在本节的kubernetes对象我们会明确说明Kubernetes"实体"的具体含义。

  17. Objects In Kubernetes

    Object spec and status

    Almost every Kubernetes object includes two nested object fields that govern the object's configuration: the object spec and the object status. For objects that have a spec, you have to set this when you create the object, providing a description of the characteristics you want the resource to have: its desired state.

    The status describes the current state of the object, supplied and updated by the Kubernetes system and its components.

  18. API Conventions

    Spec and Status

    Some objects which are not persisted in the system - such as SubjectAccessReview and other webhook style calls - may choose to add spec and status to encapsulate a "call and response" pattern. The spec is the request (often a request for information) and the status is the response.

  19. The API Object Lifecycle

    The Kubernetes API (and related APIs in the ecosystem) defines "objects" (also called resources in REST context) which are created, managed, and deleted over time.