本章详细讲解Kubernetes API方面的内容。
Kubernetes API是集群系统中的重要组成部分,Kubernetes中各种资源(对象)的数据都通过该API接口被提交到后端的持久化存储(etcd)中,Kubernetes集群中的各部件之间通过该API接口实现解耦合,同时Kubernetes集群中一个重要且便捷的管理工具kubectl也是通过访问该API接口实现其强大的管理功能的。Kubernetes API中的资源对象都拥有通用的元数据,资源对象也可能存在嵌套现象,比如在一个Pod里面嵌套多个Container。创建一个API对象是指通过API调用创建一条有意义的记录,该记录一旦被创建,Kubernetes就将确保对应的资源对象会被自动创建并托管维护。
在Kubernetes系统中,在大多数情况下,API定义和实现都符合标准的HTTP REST格式,比如通过标准的HTTP操作(POST、PUT、GET、DELETE)来完成对相关资源对象的查询、创建、修改、删除等操作。但同时,Kubernetes也为某些非标准的REST行为实现了附加的API接口,例如Watch某个资源的变化、进入容器执行某个操作等。另外,某些API接口可能违背严格的REST模式,因为接口返回的不是单一的JSON对象,而是其他类型的数据,比如JSON对象流或非结构化的文本日志数据等。
Kubernetes开发人员认为,任何成功的系统都会经历一个不断成长和不断适应各种变更的过程,因此他们期望Kubernetes API是不断变更和增长的,并在设计和开发时,有意识地兼容已存在的客户需求。通常,我们不希望将新的API资源和新的资源域频繁地加入系统中,资源或域的删除需要一个严格的审核流程。
在Kubernetes 1.13及之前的版本中,Kubernetes API Server服务提供了Swagger格式自动生成的API接口文档。Swagger UI是一款REST API文档在线自动生成和功能测试软件,通过设置kube-apiserver服务的启动参数--enable-swagger-ui=true来启用Swagger UI页面,其访问地址为http://<master-ip>:<master-port>/swagger-ui/。假设API Server启动了192.168.18.3服务器上的8080端口(非安全端口),则可以通过访问http://192.168.18.3:8080/swagger-ui/来查看API列表。
Kubernetes从1.14版本开始使用OpenAPI文档的格式生成API接口文档,并且不再提供Swagger UI)。OpenAPI文档规范始于Swagger规范,Swagger 2.0也是OpenAPI文档规范的第1个标准版本(OpenAPI v2)。相对于Swagger 1.2,Open API v2版本对REST接口的定义更精确化,也更容易利用代码生成各种语言版本的接口源码。
Kubernetes的OpenAPI访问地址为http://<master-ip>:<master-port>/openapi/v2,其内容为JSON格式的API说明文档,可以使用命令行工具curl进行查询:



整个OpenAPI文档非常大,以文本方式不易查看和检索,可以通过其他Swagger UI工具使用浏览器查看,这样更加直观、方便,步骤如下。
首先将OpenAPI文档导出为JSON文件,名称为k8s-swagger.json:

接下来启动一个swaggerapi/swagger-ui容器,导入k8s-swagger.json文件:

然后就可以通过浏览器查看Swagger UI了,如图9.1所示。

图9.1 查看效果
单击某个API链接,即可查看该API的详细信息,包括请求和应答的参数说明,也可以直接在页面上进行测试。以创建Pod的API为例,其REST API的访问路径为“/api/v1/namespaces/{namespace}/pods”,如图9.2所示。

图9.2 创建Pod的API
单击链接展开,即可查看详细的API接口说明,如图9.3和9.4所示。

图9.3 API接口说明——请求说明

图9.4 API接口说明——应答说明
可以看到,在Kubernetes API中,一个API的顶层(Top Level)元素由kind、apiVersion、metadata、spec和status这5部分组成,接下来分别对这5部分进行说明。
kind表明对象有以下三大类别。
(1)对象(objects):代表系统中的一个永久资源(实体),例如Pod、RC、Service、Namespace及Node等。通过操作这些资源的属性,客户端可以对该对象进行创建、修改、删除和获取操作。
(2)列表(list):一个或多个资源类别的集合。所有列表都通过items域获得对象数组,例如PodLists、ServiceLists、NodeLists。大部分被定义在系统中的对象都有一个返回所有资源集合的端点,以及零到多个返回所有资源集合的子集的端点。某些对象有可能是单例对象(singletons),例如当前用户、系统默认用户等,这些对象没有列表。
(3)简单类别(simple):该类别包含作用在对象上的特殊行为和非持久实体。该类别限制了使用范围,它有一个通用元数据的有限集合,例如Binding、Status。
apiVersion表明API的版本号,当前版本默认只支持v1。
metadata是资源对象的元数据定义,是集合类的元素类型,包含一组由不同名称定义的属性。在Kubernetes中,每个资源对象都必须包含以下3种metadata。
(1)namespace:对象所属的命名空间,如果不指定,系统则会将对象置于名为default的系统命名空间中。
(2)name:对象的名称,在一个命名空间中名称应具备唯一性。
(3)uid:系统为每个对象都生成的唯一ID,符合RFC 4122规范的定义。
此外,每种对象都还应该包含以下几个重要元数据。
(1)labels:用户可定义的“标签”,键和值都为字符串的map,是对象进行组织和分类的一种手段,通常用于标签选择器,用来匹配目标对象。
(2)annotations:用户可定义的“注解”,键和值都为字符串的map,被Kubernetes内部进程或者某些外部工具使用,用于存储和获取关于该对象的特定元数据。
(3)resourceVersion:用于识别该资源内部版本号的字符串,在用于Watch操作时,可以避免在GET操作和下一次Watch操作之间造成信息不一致,客户端可以用它来判断资源是否改变。该值应该被客户端看作不透明,且不做任何修改就返回给服务端。客户端不应该假定版本信息具有跨命名空间、跨不同资源类别、跨不同服务器的含义。
(4)creationTimestamp:系统记录创建对象时的时间戳,符合RFC 3339规范。
(5)deletionTimestamp:系统记录删除对象时的时间戳,符合RFC 3339规范。
(6)selfLink:通过API访问资源自身的URL,例如一个Pod的link可能是“/api/v1/namespaces/default/pods/frontend-o8bg4”。
spec是集合类的元素类型,用户对需要管理的对象进行详细描述的主体部分都在spec里给出,它会被Kubernetes持久化到etcd中保存,系统通过spec的描述来创建或更新对象,以达到用户期望的对象运行状态。spec的内容既包括用户提供的配置设置、默认值、属性的初始化值,也包括在对象创建过程中由其他相关组件(例如schedulers、auto-scalers)创建或修改的对象属性,比如Pod的Service IP地址。如果spec被删除,那么该对象将被从系统中删除。
status用于记录对象在系统中的当前状态信息,也是集合类元素类型。status在一个自动处理的进程中被持久化,可以在流转的过程中生成。如果观察到一个资源丢失了它的状态,则该丢失的状态可能被重新构造。以Pod为例,Pod的status信息主要包括conditions、containerStatuses、hostIP、phase、podIP、startTime等,其中比较重要的两个状态属性如下。
(1)phase:描述对象所处的生命周期阶段,phase的典型值是Pending(创建中)、Running、Active(正在运行中)或Terminated(已终结),这几种状态对于不同的对象可能有轻微的差别,此外,关于当前phase附加的详细说明可能被包含在其他域中。
(2)condition:表示条件,由条件类型和状态值组成,目前仅有一种条件类型:Ready,对应的状态值可以为True、False或Unknown。一个对象可以具备多种condition,而condition的状态值也可能不断发生变化,condition可能附带一些信息,例如最后的探测时间或最后的转变时间。
为了在兼容旧版本的同时不断升级新的API,Kubernetes提供了多版本API的支持能力,每个版本的API都通过一个版本号路径前缀进行区分,例如/api/v1beta3。在通常情况下,新旧几个不同的API版本都能涵盖所有的Kubernetes资源对象,在不同的版本之间,这些API接口存在一些细微差别。Kubernetes开发团队基于API级别选择版本而不是基于资源和域级别,是为了确保API能够清晰、连续地描述一个系统资源和行为的视图,能够控制访问的整个过程和控制实验性API的访问。
API的版本号通常用于描述API的成熟阶段,例如:
◎ v1表示GA稳定版本;
◎ v1beta3表示Beta版本(预发布版本);
◎ v1alpha1表示Alpha版本(实验性的版本)。
当某个API的实现达到一个新的GA稳定版本时(如v2),旧的GA版本(如v1)和Beta版本(例如v2beta1)将逐渐被废弃,Kubernetes建议废弃的时间如下。
◎ 对于旧的GA版本(如v1),Kubernetes建议废弃的时间应不少于12个月或3个大版本Release的时间,选择最长的时间。
◎ 对旧的Beta版本(如v2beta1),Kubernetes建议废弃的时间应不少于9个月或3个大版本Release的时间,选择最长的时间。
◎ 对旧的Alpha版本,则无须等待,可以直接废弃。
完整的API更新和废弃策略请参考官方网站的说明。
为了更容易扩展、升级和演进API,Kubernetes将API分组为多个逻辑集合,称之为API Groups,它们支持单独启用或禁用,在不同的API Groups中使用不同的版本,允许各组以不同的速度演进,例如apps/v1、apps/v1beta2、apps/v1beta1等。API Groups以REST URL中的路径进行定义并区别彼此,每个API Group群组都表现为一个以/apis为根路径的rest路径,不过核心群组Core有个专用的简化路径/api/v1,当前支持以下两类API Groups。
(1)Core Groups(核心组),也可以称之为Legacy Groups。其作为Kubernetes核心的API,在资源对象的定义中被表示为“apiVersion:v1”,我们常用的资源对象大部分都在这个组里,例如Container、Pod、ReplicationController、Endpoint、Service、ConfigMap、Secret、Volume等。
(2)具有分组信息的API,以/apis/$GROUP_NAME/$VERSION URL路径进行标识,例如apiVersion:batch/v1、apiVersion:extensions:v1beta1、apiVersion:apps/v1beta1等。比如/apis/apps/v1在apiversion字段中的格式为“$GROUP_NAME/$VERSION”。下面是常见的一些分组说明。
◎ apps/v1:是Kubernetes中最常见的API组,其中包含许多核心对象,主要与用户应用的发布、部署有关,例如Deployments,RollingUpdates和ReplicaSets。
◎ extensions/VERSION:扩展API组,例如DaemonSets、ReplicaSet和Ingresses都在此版本中有重大更改。
◎ batch/VERSION:包含与批处理和类似作业的任务相关的对象,例如Job,包括v1与v1beta1两个版本。
◎ autoscaling/VERSION:包含与HPA相关的资源对象,目前有稳定的v1版本。
◎ certificates.k8s.io/VERSION:包含集群证书操作相关的资源对象。
◎ rbac.authorization.k8s.io/v1:包含RBAC权限相关的资源对象。
◎ policy/VERSION:包含Pod安全性相关的资源对象。
如果需要实现自定义的资源对象及相应的API,则使用CRD进行扩展是最方便的。
例如,Pod的API说明如图9.5所示,由于Pod属于核心资源对象,所以不存在某个扩展API Group,页面显示为Core,在Pod的定义中为“apiVersion:v1”。
StatefulSet则属于名为apps的API组,版本号为v1,在StatefulSet的定义中为“apiVersion:apps/v1”,如图9.6所示。

图9.5 Pod的API说明

图9.6 StatefulSet的API说明
如果要启用或禁用特定的API组,则需要在API Server的启动参数中设置--runtime-config进行声明,例如,--runtime-config=batch/v2alpha1表示启用API组batch/v2alpha1;也可以设置--runtime-config=batch/v1=false表示禁用API组batch/v1。多个API组的设置以逗号分隔。在当前的API Server服务中,DaemonSets、Deployments、HorizontalPodAutoscalers、Ingress、Jobs和ReplicaSets所属的API组是默认启用的。
API资源使用REST模式,对资源对象的操作方法如下。
(1)GET/<资源名的复数格式>:获得某一类型的资源列表,例如GET/pods返回一个Pod资源列表。
(2)POST/<资源名的复数格式>:创建一个资源,该资源来自用户提供的JSON对象。
(3)GET/<资源名复数格式>/<名称>:通过给出的名称获得单个资源,例如GET/pods/first返回一个名为first的Pod。
(4)DELETE/<资源名复数格式>/<名称>:通过给出的名称删除单个资源,在删除选项(DeleteOptions)中可以指定优雅删除(Grace Deletion)的时间(GracePeriodSeconds),该选项表明了从服务端接收到删除请求到资源被删除的时间间隔(单位为s)。不同的类别(Kind)可能为优雅删除时间(Grace Period)声明默认值。用户提交的优雅删除时间将覆盖该默认值,包括值为0的优雅删除时间。
(5)PUT/<资源名复数格式>/<名称>:通过给出的资源名和客户端提供的JSON对象来更新或创建资源。
(6)PATCH/<资源名复数格式>/<名称>:选择修改资源详细指定的域。
对于PATCH操作,目前Kubernetes API通过相应的HTTP首部“Content-Type”对其进行识别。
目前支持以下三种类型的PATCH操作。
(1)JSON Patch,Content-Type:application/json-patch+json。在RFC6902的定义中,JSON Patch是执行在资源对象上的一系列操作,例如{"op":"add","path":"/a/b/c","value":["foo","bar"]}。详情请查看RFC6902的说明。
(2)Merge Patch,Content-Type:application/merge-json-patch+json。在RFC7386的定义中,Merge Patch必须包含对一个资源对象的部分描述,这个资源对象的部分描述就是一个JSON对象。该JSON对象被提交到服务端,与服务端的当前对象合并,从而创建一个新的对象。详情请查看RFC73862的说明。
(3)Strategic Merge Patch,Content-Type:application/strategic-merge-patch+json。Strategic Merge Patch是一个定制化的Merge Patch实现。接下来将详细讲解Strategic Merge Patch。
在标准的JSON Merge Patch中,JSON对象总被合并(Merge),但是资源对象中的列表域总被替换,用户通常不希望如此。例如,我们通过下列定义创建一个Pod资源对象:

接着,我们希望添加一个容器到这个Pod中,代码和上传的JSON对象如下:

如果我们使用标准的Merge Patch,则其中的整个容器列表将被单个log-tailer容器替换,然而我们的目的是使两个容器列表合并。
为了解决这个问题,Strategic Merge Patch添加元数据到API对象中,并通过这些新元数据来决定哪个列表被合并,哪个列表不被合并。当前这些元数据作为结构标签,对于API对象自身来说是合法的。对于客户端来说,这些元数据作为Swagger annotations也是合法的。在上述例子中向containers中添加了patchStrategy域,且它的值为merge,通过添加patchMergeKey,它的值为name。也就是说,containers中的列表将会被合并而不是被替换,合并的依据为name域的值。
Kubernetes API还增加了Watch的API接口,配合List接口(如GET/pods)可以实现高效的资源同步、缓存及实时检测处理能力。
◎ GET/watch/<资源名复数格式>:随着时间的变化,不断接收一连串的JSON对象,这些JSON对象记录了给定资源类别内所有资源对象的变化情况。
◎ GET/watch/<资源名复数格式>/<name>:随着时间的变化,不断接收一连串的JSON对象,这些JSON对象记录了某个给定资源对象的变化情况。
需要注意的是,watch接口返回的是一连串JSON对象,而不是单个JSON对象。
如果集群的规模很大,那么某些资源对象的List接口(如GET/pods)返回的数据集就很大,这对Kubernetes API Server及客户端程序都造成很大的压力,比如在集群中有上千个Pod实例的情况下,每个Pod的JSON数据都会有1~2KB,List返回的结果会有10~20MB!所以从1.9版本开始,Kubernetes又提供了分段模式的List接口(Retrieving large results sets in chunks),其使用方法也很简单,类似数据库结果集遍历,只增加了limit和continue两个参数:

另外,Kubernetes增加了HTTP Redirect与HTTP Proxy这两种特殊的API接口,前者实现资源重定向访问,后者则实现HTTP请求的代理。
API Server在响应用户请求时附带一个状态码,该状态码符合HTTP规范。表9.1列出了API Server可能返回的状态码。
表9.1 API Server可能返回的状态码

▼续表

在调用API接口发生错误时,Kubernetes将会返回一个状态类别(Status Kind)。下面是两种常见的错误场景。
(1)当一个操作不成功时(例如,当服务端返回一个非2xx HTTP状态码时)。
(2)当一个HTTP DELETE方法调用失败时。
状态对象被编码成JSON格式,同时该JSON对象被作为请求的响应体。该状态对象包含人和机器使用的域,在这些域中包含来自API的关于失败原因的详细信息。状态对象中的信息补充了对HTTP状态码的说明。例如:


其中:
◎ status域包含两个可能的值,即Success或Failure。
◎ message域包含对错误的描述信息。
◎ reason域包含对该操作失败原因的描述信息。
◎ details可能包含和reason域相关的扩展数据。每个reason域都可以定义它的扩展的details域。该域是可选的,返回数据的格式是不确定的,不同的reason类型返回的details域的内容不一样。
为了让开发人员更方便地访问Kubernetes的RESTful API,Kubernetes社区推出了针对Go、Python、Java、dotNet、JavaScript等编程语言的客户端库,这些库由特别兴趣小组(SIG)API Machinary维护。在Java客户端框架中,Fabric8 Kubernetes Java client于2015年推出,虽然不是“官方”宣布支持的Java客户端框架,但它是除Kubernetes Go client library外非常流行的客户端框架,也是非常强大的Java版本的Kubernetes客户端框架,所以本书后面主要以它为例,给出相关使用案例。