为了在兼容旧版本的同时不断升级新的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更新和废弃策略请参考官方网站https://kubernetes.io/docs/reference/usingapi/deprecation-policy/的说明。
为了更容易对API进行扩展,Kubernetes使用API Groups(API组)进行标识。API Groups以REST URL中的路径进行定义。当前支持两类API groups。
◎ Core Groups(核心组),也可以称之为Legacy Groups,作为Kubernetes最核心的API,其特点是没有“组”的概念,例如“v1”,在资源对象的定义中表示为“apiVersion:v1”。
◎ 具有分组信息的API,以/apis/$GROUP_NAME/$VERSION URL路径进行标识,在资源对象的定义中表示为“apiVersion: $GROUP_NAME/$VERSION”,例如:“apiVersion: batch/v1”“apiVersion: extensions:v1beta1”“apiVersion: apps/v1beta1”等,详细的API列表请参见官网https://kubernetes.io/docs/reference,目前根据Kubernetes的不同版本有不同的API说明页面。
例如,Pod的API说明,由于Pod属于核心资源对象,所以不存在某个扩展API Group,页面显示为Core,在Pod的定义中为“apiVersion: v1”。
StatefulSet则属于名为apps的API组,版本号为v1,在StatefulSet的定义中为“apiVersion: apps/v1"。
如果要启用或禁用特定的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说明,网址为https://tools.ietf.org/html/rfc6902。
(2)Merge Patch, Content-Type: application/merge-json-patch+json。在RFC7386的定义中,Merge Patch必须包含对一个资源对象的部分描述,这个资源对象的部分描述就是一个JSON对象。该JSON对象被提交到服务端,并和服务端的当前对象合并,从而创建一个新的对象。详情请查看RFC73862说明,网址为https://tools.ietf.org/html/rfc7386。
(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资源对象:
spec:
containers:
- name: bgubx
image: nginx-1.0
接着,我们希望添加一个容器到这个Pod中,代码和上传的JSON对象如下:
PATH: /api/v1/namespaces/default/pods/pod-name
spec:
containers:
- name: log-tailer
image: log-tailer-1.0
如果我们使用标准的Merge Patch,则其中的整个容器列表将被单个“log-tailer”容器所替换,然而我们的目的是使两个容器列表合并。
为了解决这个问题,Strategic Merge Patch添加元数据到API对象中,并通过这些新元数据来决定哪个列表被合并,哪个列表不被合并。当前这些元数据作为结构标签,对于API对象自身来说是合法的。对于客户端来说,这些元数据作为Swagger annotations也是合法的。在上述例子中向containers中添加了patchStrategy域,且它的值为merge,通过添加patchMergeKey,它的值为name。也就是说,containers中的列表将会被合并而不是替换,合并的依据为name域的值。
此外,Kubernetes API添加了资源变动的观察者模式的API接口。
◎ GET /watch/<资源名复数格式>:随时间变化,不断接收一连串的JSON对象,这些JSON对象记录了给定资源类别内所有资源对象的变化情况。
◎ GET /watch/<资源名复数格式>/
上述接口改变了返回数据的基本类别,watch动词返回的是一连串JSON对象,而不是单个JSON对象。并不是所有对象类别都支持观察者模式的API接口,在后续的章节中将会说明哪些资源对象支持这种接口。
另外,Kubernetes还增加了HTTP Redirect与HTTP Proxy这两种特殊的API接口,前者实现资源重定向访问,后者则实现HTTP请求的代理。
API Server在响应用户请求时附带一个状态码,该状态码符合HTTP规范。
状态码 | 编码 | 描述 |
200 | OK | 表明请求完全成功 |
201 | Created | 表明创建类的请求完全成功 |
204 | NoContent | 表明请求完全成功,同时HTTP响应不包含响应体。 在响应OPTIONS方法的HTTP请求时返回。 |
307 | TemporayRedirect | 表面请求资源的地址改变,建议客户端使用Location首部给出的临时URL来定位资源 |
400 | BadRequest | 表明请求是非法的,建议用户不要重试,修改该请求 |
401 | Unauthorized | 表明请求能够到达服务端,且服务端能够理解用户请求,但是拒绝做更多的事情,因为客户端必须提供认证信息。如果客户端提供了认证信息,则返回该状态码,表明服务端指出所提供的认证信息不合适或非法 |
403 | Forbidden | 表明请求能够到达服务端,且服务端能够理解用户请求,但是拒绝做更多的事情,因为该请求被设置为拒绝访问,建议用户不要重试,修改该请求。 |
404 | NotFound |
表明所请求的资源不存在。 |
405 | MethodNotAllowed | 表明在请求中有该资源不支持的方法。 |
409 | Conflict | 表明客户端尝试创建的资源已存在,或者由于冲突,请求的更新操作不能被完成 |
422 | UnprocessableEntity | 表明由于所提供的作为请求部分的数据非法,创建或修改操作不能被完成 |
429 | TooManyRequeste | 表明超出了客户端访问频率的限制或者服务端接收到多于它能处理的请求。建议客户端读取相应的Retry-After首部,然后等待该首部指出的时间后再重试 |
500 | InternalServerError | 表明服务端被请求访问到,但是不能理解用户的请求;或者在服务端内产生非预期的一个错误,而且该错误无法被认知;或者服务端不能再一个合理的时间内完成处理(这可能是服务器临时负载过重造成的,或和其他服务器通信时的一个临时通信故障造成的) |
503 | ServuceUnavailable | 表面被请求的服务无效。 |
504 | ServerTimeout | 表明请求在给定的时间内无法完成。客户端仅在为请求指定超时(Timeout)参数时得到该响应。 |
在调用API接口发生错误时,Kubernetes将会返回一个状态类别(Status Kind)。下面是两种常见的错误场景。
(1)当一个操作不成功时(例如,当服务端返回一个非2xx HTTP状态码时)。
(2)当一个HTTP DELETE方法调用失败时。
状态对象被编码成JSON格式,同时该JSON对象被作为请求的响应体。该状态对象包含人和机器使用的域,在这些域中包含来自API的关于失败原因的详细信息。状态对象中的信息补充了对HTTP状态码的说明。例如:
curl -v -k -H "Authorization: Bearer WhCDV4q.............." HTTPS://10.240.122.184:443/api/v1/namespaces/default/pods/grafana
◎ status域包含两个可能的值:Success或Failure。
◎ message域包含对错误的描述信息。
◎ reason域包含说明该操作失败原因的描述。
◎ details可能包含和reason域相关的扩展数据。每个reason域都可以定义它的扩展的details域。该域是可选的,返回数据的格式是不确定的,不同的reason类型返回的details域的内容不一样。
为了让开发人员更方便地访问Kubernetes的RESTful API,Kubernetes社区推出了针对Go、Python、Java、dotNet、JavaScript等编程语言的客户端库,这些库由特别兴趣小组(SIG)API Machinary维护,其官方网站为https://github.com/kubernetes/community/tree/master/sig-api-machinery。
随着Kubernetes的发展,用户对Kubernetes的扩展性也提出了越来越高的要求。从1.7版本开始,Kubernetes引入扩展API资源的能力,使得开发人员在不修改Kubernetes核心代码的前提下可以对Kubernetes API进行扩展,仍然使用Kubernetes的语法对新增的API进行操作,这非常适用于在Kubernetes上通过其API实现其他功能(例如第三方性能指标采集服务)或者测试实验性新特性(例如外部设备驱动)。
在Kubernetes中,所有对象都被抽象定义为某种资源对象,同时系统会为其设置一个API入口(API Endpoint),对资源对象的操作(如新增、删除、修改、查看等)都需要通过Master的核心组件API Server调用资源对象的API来完成。与API Server的交互可以通过kubectl命令行工具或访问其RESTful API进行。每个API都可以设置多个版本,在不同的API URL路径下区分,例如“/api/v1”或“/apis/extensions/v1beta1”等。使用这种机制后,用户可以很方便地定义这些API资源对象(YAML配置),并将其提交给Kubernetes(调用RESTful API),来完成对容器应用的各种管理工作。
Kubernetes系统内置的Pod、RC、Service、ConfigMap、Volume等资源对象已经能够满足常见的容器应用管理要求,但如果用户希望将其自行开发的第三方系统纳入Kubernetes,并使用Kubernetes的API对其自定义的功能或配置进行管理,就需要对API进行扩展了。目前Kubernetes提供了以下两种机制供用户扩展API。
(1)使用CRD机制:复用Kubernetes的API Server,无须编写额外的API Server。用户只需要定义CRD,并且提供一个CRD控制器,就能通过Kubernetes的API管理自定义资源对象了,同时要求用户的CRD对象符合API Server的管理规范。
(2)使用API聚合机制:用户需要编写额外的API Server,可以对资源进行更细粒度的控制(例如,如何在各API版本之间切换),要求用户自行处理对多个API版本的支持。
CRD是Kubernetes从1.7版本开始引入的特性,在Kubernetes早期版本中被称为TPR(ThirdPartyResources,第三方资源)。TPR从Kubernetes 1.8版本开始被停用,被CRD全面替换。
CRD本身只是一段声明,用于定义用户自定义的资源对象。但仅有CRD的定义并没有实际作用,用户还需要提供管理CRD对象的CRD控制器(CRD Controller),才能实现对CRD对象的管理。CRD控制器通常可以通过Go语言进行开发,并需要遵循Kubernetes的控制器开发规范,基于客户端库client-go进行开发,需要实现Informer、ResourceEventHandler、Workqueue等组件具体的功能处理逻辑,详细的开发过程请参考官方示例(https://github.com/kubernetes/sample-controller)和client-go库(https://github.com/kubernetes/sample-controller/blob/master/docs/controller-client-go.md)的详细说明。
1.创建CRD的定义
与其他资源对象一样,对CRD的定义也使用YAML配置进行声明。以Istio系统中的自定义资源VirtualService为例,配置文件crd-virtualservice.yaml的内容如下:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: virtualservices.networking.istio.io
annotations:
"helm.sh/hook": crd-install
labels:
app: istio-pilot
spec:
group: networking.istio.io
scope: Namespaced
versions:
- name: vlalpha3
served: true
storage: true
names:
kind: VirtualService
listKind: VirtualServiceList
singular: virtualservice
plural: virtualservices
categories:
- istio-io
- networking-istio-io
CRD定义中的关键字段如下。
(1)group:设置API所属的组,将其映射为API URL中“/apis/”的下一级目录,设置netwirking.stio.io生成的API URL路径为“/apis/networking.istio.io”。
(2)scope:该API的生效范围,可选项为Namespaced(由Namespace限定)和Cluster(在集群范围全局生效,不局限于任何Namespace),默认值为Namespaced。
(3)versions:设置此CRD支持的版本,可以设置多个版本,用列表形式表示。目前还可以设置名为version的字段,只能设置一个版本,在将来的Kubernetes版本中会被弃用,建议使用versions进行设置。如果该CRD支持多个版本,则每个版本都会在API URL“/apis/networking.istio.io”的下一级进行体现,例如“/apis/networking.istio.io/v1”或“/apis/networking.istio.io/v1alpha3”等。每个版本都可以设置下列参数。
◎ name:版本的名称,例如v1、v1alpha3等。
◎ served:是否启用,在被设置为true时表示启用。
◎ storage:是否进行存储,只能有一个版本被设置为置为true。
(4)names:CRD的名称,包括单数、复数、kind、所属组等名称的定义,可以设置如下参数。
◎ kind:CRD的资源类型名称,要求以驼峰式命名规范进行命名(单词的首字母都大写),例如VirtualService。
◎ listKind:CRD列表,默认被设置为
◎ singular:单数形式的名称,要求全部小写,例如virtualservice。
◎ plural:复数形式的名称,要求全部小写,例如virtualservices。
◎ shortNames:缩写形式的名称,要求全部小写,例如vs。
◎ categories:CRD所属的资源组列表。例如,VirtualService属于istio-io组和networking-istio-io组,用户通过查询istio-io组和networking-istio-io组,也可以查询到该CRD实例。
使用kubectl create命令完成CRD的创建:
kubectl create -f crd-virtualservice.yaml
在CRD创建成功后,由于本例的scope设置了Namespace限定,所以可以通过API Endpoint“/apis/networking.istio.io/v1alpha3/namespaces/
用户接下来就可以基于该CRD的定义创建相应的自定义资源对象了。
2.基于CRD的定义创建自定义资源对象
基于CRD的定义,用户可以像创建Kubernetes系统内置的资源对象(如Pod)一样创建CRD资源对象。在下面的例子中,virtualservice-helloworld.yaml定义了一个类型为VirtualService的资源对象:
apiVersion: networking.istio.io/vlalpha3
kind: VirtualService
metadata:
name: helloworld
spec:
hosts:
- "*"
gateways:
- helloworld-gateway
http:
- match:
- uri:
exact: /hello
route:
- destination:
host: helloworld
port:
number: 5000
除了需要设置该CRD资源对象的名称,还需要在spec段设置相应的参数。在spec中可以设置的字段是由CRD开发者自定义的,需要根据CRD开发者提供的手册进行配置。这些参数通常包含特定的业务含义,由CRD控制器进行处理。
使用kubectl create命令完成CRD资源对象的创建:
kubectl create -f virtualservice-helloworld.yaml
然后,用户就可以像操作Kubernetes内置的资源对象(如Pod、RC、Service)一样去操作CRD资源对象了,包括查看、更新、删除和watch等操作。
查看CRD资源对象:
kubectl get virtualservice
也可以通过CRD所属的categories进行查询:
kubectl get istio-io
kubectl get networking-istio-io
3.CRD的高级特性
随着Kubernetes的演进,CRD也在逐步添加一些高级特性和功能,包括subresources子资源、校验(Validation)机制、自定义查看CRD时需要显示的列,以及finalizer预删除钩子。
(1)CRD的subresources子资源
Kubernetes从1.11版本开始,在CRD的定义中引入了名为subresources的配置,可以设置的选项包括status和scale两类。
◎ stcatus:启用/status路径,其值来自CRD的.status字段,要求CRD控制器能够设置和更新这个字段的值。
◎ scale:启用/scale路径,支持通过其他Kubernetes控制器(如HorizontalPodAutoscaler控制器)与CRD资源对象实例进行交互。用户通过kubectl scale命令也能对该CRD资源对象进行扩容或缩容操作,要求CRD本身支持以多个副本的形式运行。
下面是一个设置了subresources的CRD示例:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
subresources:
status: {}
scale:
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicas
labelSelectorPath: .status.labelSelector
基于该CRD的定义,创建一个自定义资源对象my-crontab.yaml:
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image
replicas: 3
之后就能通过API Endpoint查看该资源对象的状态了:
/apis/stable.example.com/v1/namespaces//contabs/status
并查看该资源对象的扩缩容(scale)信息:
/apis/stable.example/com/v1/namespaces//contabs/scale
用户还可以使用kubectl scale命令对Pod的副本数量进行调整,例如:
kubectl scale --replicas=5 cibtavs/my-new-cron-object
(2)CRD的校验(Validation)机制
Kubernetes从1.8版本开始引入了基于OpenAPI v3 schema或validatingadmissionwebhook的校验机制,用于校验用户提交的CRD资源对象配置是否符合预定义的校验规则。该机制到Kubernetes 1.13版本时升级为Beta版。要使用该功能,需要为kube-apiserver服务开启--feature-gates=CustomResourceValidation=true特性开关。
下面的例子为CRD定义中的两个字段(cronSpec和replicas)设置了校验规则:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
version: v1
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
validation:
openAPIV3Schema:
properties:
spec:
properties:
cronSpec:
type: string
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
replicas:
type: integer
minimum: 1
maximum: 10
校验规则如下。
◎ spec.cronSpec:必须为字符串类型,并且满足正则表达式的格式。
◎ spec.replicas:必须将其设置为1~10的整数。
对于不符合要求的CRD资源对象定义,系统将拒绝创建。
如,下面的my-crontab.yaml示例违反了CRD中validation设置的校验规则,即cronSpec没有满足正则表达式的格式,replicas的值大于10:
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * *"
image: my-awesome-cron-image
replicas: 15
创建时,系统将报出validation失败的错误信息。
(3)自定义查看CRD时需要显示的列
从Kubernetes 1.11版本开始,通过kubectl get命令能够显示哪些字段由服务端(API Server)决定,还支持在CRD中设置需要在查看(get)时显示的自定义列,在spec.additionalPrinterColumns字段设置即可。
在下面的例子中设置了3个需要显示的自定义列Spec、Replicas和Age,并在JSONPath字段设置了自定义列的数据来源:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
version: v1
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
additionalPrinterColumns:
- name: Spec
type: string
description: The cron spec defining the interval a CronJob is run
JSONPath: .spec.cronSpec
- name: Replicas
type: integer
description: The number of jobs launched by the CronJob
JSONPath: .spec.replicas
- name: Age
type: date
JSONPath: .metadata.creationTimestamp
通过kubectl get命令查看CronTab资源对象,会显示出这3个自定义的列。
(4)Finalizer(CRD资源对象的预删除钩子方法)
Finalizer设置的方法在删除CRD资源对象时进行调用,以实现CRD资源对象的清理工作。
在下面的例子中为CRD“CronTab”设置了一个finalizer(也可以设置多个),其值为URL“finalizer.stable.example.com”:
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
finalizers:
- finalizer.stable.example.com
在用户发起删除该资源对象的请求时,Kubernetes不会直接删除这个资源对象,而是在元数据部分设置时间戳“metadata.deletionTimestamp”的值,标记为开始删除该CRD对象。然后控制器开始执行finalizer定义的钩子方法“finalizer.stable.example.com”进行清理工作。对于耗时较长的清理操作,还可以设置metadata.deletionGracePeriodSeconds超时时间,在超过这个时间后由系统强制终止钩子方法的执行。在控制器执行完钩子方法后,控制器应负责删除相应的finalizer。当全部finalizer都触发控制器执行钩子方法并都被
CRD极大扩展了Kubernetes的能力,使用户像操作Pod一样操作自定义的各种资源对象。CRD已经在一些基于Kubernetes的第三方开源项目中得到广泛应用,包括CSI存储插件、Device Plugin(GPU驱动程序)、Istio(Service Mesh管理)等,已经逐渐成为扩展Kubernetes能力的标准。
小结:
本节内容到此结束,谢谢大家的浏览!