- 1. 概述
- 1.1. 背景需求
- 1.2. API机制
- 1.3. 两种API扩展机制
- 2. 使用CRD扩展API资源
- 2.1. 创建CRD的定义
- 2.2. 基于CRD的定义创建自定义资源对象
- 2.3. CRD的高级特性
- 2.4. 小结
- 3. 使用API聚合机制扩展API资源
- 3.1. 在Master的API Server中启用API聚合功能
1. 概述
1.1. 背景需求
随着Kubernetes的发展,用户对Kubernetes的扩展性也提出了越来越高的要求。
从1.7版本开始,Kubernetes引入扩展API资源的能力,使得开发人员在不修改Kubernetes核心代码的前提下可以对Kubernetes API进行扩展,仍然使用Kubernetes的语法对新增的API进行操作,这非常适用于在Kubernetes上通过其API实现其他功能(例如第三方性能指标采集服务)或者测试实验性新特性(例如外部设备驱动)。
1.2. API机制
-
资源对象的抽象和访问入口: 在Kubernetes中,所有对象都被抽象定义为某种资源对象,同时系统会为其设置一个API入口(API Endpoint),对资源对象的操作(如新增、删除、修改、查看等)都需要通过Master的核心组件API Server调用资源对象的API来完成。
-
交互方式: 与API Server的交互可以通过kubectl命令行工具或访问其RESTful API进行。
-
API的不同版本: 每个API都可以设置多个版本,在不同的API URL路径下区分,例如“/api/v1”或“/apis/extensions/v1beta1”等。
使用这种机制后,用户可以很方便地定义这些API资源对象(YAML配置),并将其提交给Kubernetes(调用RESTful API),来完成对容器应用的各种管理工作。
1.3. 两种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和API聚合这两种API扩展机制的概念和用法进行详细说明。
2. 使用CRD扩展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 )的详细说明。
2.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: v1alpha3
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/”的下一级目录,设置networking.istio.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列表,默认被设置为
List格式,例如VirtualServiceList。 - 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
customresourcedefinition.apiextensions.k8s.io/virtualservices.networking.istio.io created
在CRD创建成功后,由于本例的scope设置了Namespace限定,所以可以通过API Endpoint“/apis/networking.istio.io/v1alpha3/namespaces/
用户接下来就可以基于该CRD的定义创建相应的自定义资源对象了。
2.2. 基于CRD的定义创建自定义资源对象
基于CRD的定义,用户可以像创建Kubernetes系统内置的资源对象(如Pod)一样创建CRD资源对象。在下面的例子中,virtualservice-helloworld.yaml定义了一个类型为VirtualService的资源对象:
---
apiVersion: networking.istio.io/v1alpha3 # 重点
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
virtualservices.networking.istio.io/hello created
然后,用户就可以像操作Kubernetes内置的资源对象(如Pod、RC、Service)一样去操作CRD资源对象了,包括查看、更新、删除和watch等操作。
查看CRD资源对象:
# kubectl get virtualservice
NAME AGE
helloworld 1m
也可以通过CRD所属的categories进行查询:
# kubectl get istio-io
NAME AGE
helloworld 1m
# kubectl get networking-istio-io
NAME AGE
helloworld 1m
2.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示例:
# crd subresources
---
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:
# 定义从CRD元数据获取用户期望的副本数量的JSON路径
specReplicasPath: .spec.replicas
# 定义从CRD元数据获取当前运行的副本数量的JSON路径
statusReplicasPath: .status.replicas
# 定义从CRD元数据获取Label Selector(标签选择器)的JSON路径
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查看该资源对象的状态了:
......
2.4. 小结
CRD极大扩展了Kubernetes的能力,使用户像操作Pod一样操作自定义的各种资源对象。
CRD已经在一些基于Kubernetes的第三方开源项目中得到广泛应用,包括CSI存储插件、Device Plugin(GPU驱动程序)、Istio(Service Mesh管理)等,已经逐渐成为扩展Kubernetes能力的标准。
3. 使用API聚合机制扩展API资源
API聚合机制是Kubernetes 1.7版本引入的特性,能够将用户扩展的API注册到kube-apiserver上,仍然通过API Server的HTTP URL对新的API进行访问和操作。为了实现这个机制,Kubernetes在kube-apiserver服务中引入了一个API聚合层(API Aggregation Layer),用于将扩展API的访问请求转发到用户服务的功能。
设计API聚合机制的主要目标如下。
- 增加API的扩展性:使得开发人员可以编写自己的API Server来发布他们的API,而无须对Kubernetes核心代码进行任何修改。
- 无须等待Kubernetes核心团队的繁杂审查:允许开发人员将其API作为单独的API Server发布,使集群管理员不用对Kubernetes核心代码进行修改就能使用新的API,也就无须等待社区繁杂的审查了。
- 支持实验性新特性API开发:可以在独立的API聚合服务中开发新的API,不影响系统现有的功能。
- 确保新的API遵循Kubernetes的规范:如果没有API聚合机制,开发人员就可能会被迫推出自己的设计,可能不遵循Kubernetes规范。
总的来说,API聚合机制的目标是提供集中的API发现机制和安全的代理功能,将开发人员的新API动态地、无缝地注册到Kubernetes API Server中进行测试和使用。
下面对API聚合机制的使用方式进行详细说明。
3.1. 在Master的API Server中启用API聚合功能
为了能够将用户自定义的API注册到Master的API Server中,首先需要配置kube-apiserver服务的以下启动参数来启用API聚合功能。
- --requestheader-client-ca-file=/etc/kubernetes/ssl_keys/ca.crt:客户端CA证书。
- --requestheader-allowed-names=:允许访问的客户端common names列表,通过header中--requestheader-username-headers参数指定的字段获取。客户端common names的名称需要在client-ca-file中进行设置,将其设置为空值时,表示任意客户端都可访问。
- --requestheader-extra-headers-prefix=X-Remote-Extra-:请求头中需要检查的前缀名。
- --requestheader-group-headers=X-Remote-Group:请求头中需要检查的组名。
- --requestheader-username-headers=X-Remote-User:请求头中需要检查的用户名。
- --proxy-client-cert-file=/etc/kubernetes/ssl_keys/kubelet_client.crt:在请求期间验证Aggregator的客户端CA证书。
- --proxy-client-key-file=/etc/kubernetes/ssl_keys/kubelet_client.key:在请求期间验证Aggregator的客户端私钥。
如果kube-apiserver所在的主机上没有运行kube-proxy,即无法通过服务的ClusterIP进行访问,那么还需要设置以下启动参数:
--enable-aggregator-routing=true
在设置完成重启kube-apiserver服务,就启用API聚合功能了。
.......