为了方便查阅 API 接口的详细定义,Kubernetes 使用了 swagger-ui 提供 API 在线查询功能,其官网为http://kubernetes.kansea.com/docs/reference/, Kubernetes开发团队会定期更新、生成 UI 及文档。

运行在 Master 节点上的 API Server 进程同时提供了 swagger-ui 的访问地址:http://: /swagger-ui/。假设我们的 API Server 安装在 192.168.1.128 服务器上,绑定了 8080 端口,则可以通过访问 http://192.168.1.128:8080/swagger-ui/ 来查看 API 信息。

Swagger UI 是一款 REST API 文档在线自动生成和功能测试软件,关于 Swagger 的内容请访问官网 http://swagger.io。

API设计哲学

api对象

k8s的api采用了声明式的api设计,并且在在etcd中都有完整的资源路径。

所以一个api对象完整的资源路径是由:Group(API组)、Version(API版本)和Resource(API资源类型)三个部分组成的。

对应我们yaml文件就是

apiVersion: batch/v2alpha1
kind: CronJob
...

在这个 YAML 文件中,“CronJob”就是这个 API 对象的资源类型(Resource),“batch”就是它的组(Group),v2alpha1 就是它的版本(Version)。

声明式风格的API设计

声明性API,就是通过api对象声明一个期望的状态,系统将不断向该状态驱动,最终达到一致。直接点就是kubectl apply。声明式api必须具有

  • 首先,所谓“声明式”,指的就是我只需要提交一个定义好的 API 对象来“声明”,我所期望的状态是什么样子。
  • 其次,“声明式 API”允许有多个 API 写端,以 PATCH 的方式对 API 对象进行修改,而无需关心本地原始 YAML 文件的内容。
  • 最后,也是最重要的,有了上述两个能力,Kubernetes 项目才可以基于对 API 对象的增、删、改、查,在完全无需外界干预的情况下,完成对“实际状态”和“期望状态”的调谐(Reconcile)过程。

声明式 API,才是 Kubernetes 项目编排能力“赖以生存”的核心所在

命令式API的设计

命令式API中,直接发出服务器要执行的命令,例如: “运行容器”、“停止容器”等。

api基本解析过程

当我们提交了这个 YAML 文件之后,Kubernetes 就会把这个 YAML 文件里描述的内容,转换成 Kubernetes 里的一个 API对象。

Kubernetes对Resource、Group 和 Version 进行解析,从而找到对象的定义,比如上面创建CronJob过程如下:

  • 发起了创建 CronJob 的 POST 请求之后,我们编写的 YAML 的信息就被提交给了 APIServer。而 APIServer 的第一个功能,就是过滤这个请求,并完成一些前置性的工作,比如授权、超时处理、审计等。
  • 请求会进入 MUX 和 Routes 流程。MUX 和 Routes 是 APIServer 完成 URL 和 Handler 绑定的场所。而 APIServer 的 Handler 要做的事情,就是下面三步找到对应的api对象
    • 首先,Kubernetes 会匹配 API 对象的组。需要明确的是,对于 Kubernetes 里的核心 API 对象,比如:Pod、Node 等,是不需要 Group 的(即:它们的 Group 是“”)。所以,对于这些 API 对象来说,Kubernetes 会直接在 /api 这个层级进行下一步的匹配过程。而对于 CronJob 等非核心 API 对象来说,Kubernetes 就必须在 /apis 这个层级里查找它对应的 Group,进而根据“batch”这个 Group 的名字,找到 /apis/batch。这些 API Group 的分类是以对象功能为依据的,比如 Job 和 CronJob 就都属于“batch” (离线业务)这个 Group。
    • 然后,Kubernetes 会进一步匹配到 API 对象的版本号。对于 CronJob 这个 API 对象来说,Kubernetes 在 batch 这个 Group 下,匹配到的版本号就是 v2alpha1。
    • 最后,Kubernetes 会匹配 API 对象的资源类型。在前面匹配到正确的版本之后,Kubernetes 就知道,我要创建的原来是一个 /apis/batch/v2alpha1 下的 CronJob 对象。
  • 根据这个 CronJob 类型定义,使用用户提交的 YAML 文件里的字段,创建一个 CronJob 对象。APIServer 会进行一个 Convert 工作,即:把用户提交的 YAML 文件,转换成一个叫作 Super Version 的对象
  • APIServer 会先后进行 Admission(就是补充yaml文件,比如注入一个envoy的容器,可以先将配置放在configmap中) 和 Validation(负责验证这个对象里的各个字段是否合法) 操作。
  • API 对象都保存在了 APIServer 里一个叫作 Registry 的数据结构中,只要一个 API 对象的定义能在 Registry 里查到,它就是一个有效的 Kubernetes API 对象。
  • APIServer 会把验证过的 API 对象转换成用户最初提交的版本,进行序列化操作,并调用 Etcd 的 API 把它保存起来。

这一整个完整的过程可以使用下图来表示。

API

版本和资源对象

curl localhost:8080/api  #查看kubernetes API的版本信息
curl localhost:8080/api/v1  #查看kubernetes API支持的所有的资源对象

具体的资源操作

1、首先要找到具体的资源

localhost:8080/api/v1/资源对象(ns,pod,service)

2、然后不同的资源需要不同的处理

node

node是以name来进行资源划分的。

分类          说明                      方法  API
查   list or watch objects of kind Node      GET     /api/v1/nodes
    read the specified Node                 GET     /api/v1/nodes/{name}
增   create a Node                           POST    /api/v1/nodes
删   delete a Node                           DELETE  /api/v1/nodes/{name}
改   replace the specified Node              PUT     /api/v1/nodes/{name}
    partially update the specified Node P   ATCH    /api/v1/nodes/{name}
    replace status of the specified Node    PUT     /api/v1/nodes/{name}/status

namespace

namespace也是以name来进行资源划分的。

分类      说明                                          方法  API
查       list or watch objects of kind Namespace             GET     /api/v1/namespaces
        read the specified Namespace                        GET     /api/v1/namespaces/{name}
增       create a Namespace                                  POST    /api/v1/namespaces
删       delete a Namespace                                  DELETE  /api/v1/namespaces/{name}
改       replace the specified Namespace                     PUT     /api/v1/namespaces/{name}
        partially update the specified Namespace            PATCH   /api/v1/namespaces/{name}
        replace finalize of the specified Namespace         PUT     /api/v1/namespaces/{name}/finalize
        replace status of the specified Namespace           PUT     /api/v1/namespaces/{name}/status

endpoint

Endpoints是以Namespace维度划分资源,然后结合name来区分。

分类      说明                                          方法  API
查       list or watch objects of kind Endpoints             GET     /api/v1/endpoints
        list or watch objects of kind Endpoints             GET     /api/v1/namespaces/{namespace}/endpoints
        read the specified Endpoints                        GET     /api/v1/namespaces/{namespace}/endpoints/{name}
增       create a Endpoints                                  POST    /api/v1/namespaces/{namespace}/endpoints
删       delete a Endpoints                                  DELETE  /api/v1/namespaces/{namespace}/endpoints/{name}
改       replace the specified Endpoints                     PUT     /api/v1/namespaces/{namespace}/endpoints/{name}
        partially update the specified Endpoints            PATCH   /api/v1/namespaces/{namespace}/endpoints/{name}

pod

pod是以Namespace维度划分资源,然后结合name来区分。

分类          说明                              方法  API
查           list or watch objects of kind Pod       GET /api/v1/namespaces/{namespace}/pods
            read the specified Pod                  GET /api/v1/namespaces/{namespace}/pods/{name}
增           create a Pod                            POST    /api/v1/namespaces/{namespace}/pods
删           delete a Pod                            DELETE  /api/v1/namespaces/{namespace}/pods/{name}
改           replace the specified Pod               PUT /api/v1/namespaces/{namespace}/pods/{name}
            partially update the specified Pod      PATCH   /api/v1/namespaces/{namespace}/pods/{name}

            connect GET requests to attach of Pod   GET /api/v1/namespaces/{namespace}/pods/{name}/attach
            connect POST requests to attach of Pod  POST    /api/v1/namespaces/{namespace}/pods/{name}/attach
            create binding of a Binding             POST    /api/v1/namespaces/{namespace}/pods/{name}/binding
            connect GET requests to exec of Pod     GET /api/v1/namespaces/{namespace}/pods/{name}/exec
            connect POST requests to exec of Pod    POST    /api/v1/namespaces/{namespace}/pods/{name}/exec
            read log of the specified Pod           GET /api/v1/namespaces/{namespace}/pods/{name}/log
            connect GET requests to portforward of Pod  GET /api/v1/namespaces/{namespace}/pods/{name}/portforward
            connect POST requests to portforward of Pod POST    /api/v1/namespaces/{namespace}/pods/{name}/portforward

Service

分类              说明                                      方法          API
查               list or watch objects of kind Service           GET             /api/v1/namespaces/{namespace}/services
                read the specified Service                      GET             /api/v1/namespaces/{namespace}/services/{name}
增               create a Service                                POST            /api/v1/namespaces/{namespace}/services
删               delete a Service                                DELETE          /api/v1/namespaces/{namespace}/services/{name}
改               replace the specified Service                   PUT             /api/v1/namespaces/{namespace}/services/{name}
                partially update the specified Service          PATCH           /api/v1/namespaces/{namespace}/services/{name}

ReplicationController

分类              说明                                                  方法      API
查                   list or watch objects of kind ReplicationController     GET         /api/v1/namespaces/{namespace}/replicationcontrollers
                    read the specified ReplicationController                GET         /api/v1/namespaces/{namespace}/replicationcontrollers/{name}
增                   create a ReplicationController                          POST        /api/v1/namespaces/{namespace}/replicationcontrollers
删                   delete a ReplicationController                          DELETE      /api/v1/namespaces/{namespace}/replicationcontrollers/{name}
改                   replace the specified ReplicationController             PUT         /api/v1/namespaces/{namespace}/replicationcontrollers/{name}
                    partially update the specified ReplicationController    PATCH       /api/v1/namespaces/{namespace}/replicationcontrollers/{name}
                    replace status of the specified ReplicationController   PUT         /api/v1/namespaces/{namespace}/replicationcontrollers/{name}/status

其实大多都差不多相似,照着规律就行

proxy接口

kubernetes API server还提供了一类很特殊的rest接口—proxy接口,这个结构就是代理REST请求,即kubernetes API server把收到的rest请求转发到某个node上的kubelet守护进程的rest端口上,由该kubelet进程负责相应。

node
    masterIP:8080/api/v1/proxy/nodes/{node_name}/pods  #某个节点下所有pod信息
    masterIP:8080/api/v1/proxy/nodes/{node_name}/stats  #某个节点内物理资源的统计信息
    masterIP:8080/api/v1/proxy/nodes/{node_name}/spec  #某个节点的概要信息
pod
    masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/{pod_name}/{path:*} #访问pod的某个服务接口
    masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/{pod_name}  #访问pod
service
    masterIP:8080/api/v1/proxy/namespaces/{namespace}/services/{service_name}

可以参考上面的资源操作方式,同样的可以进行操作。

实例

  • GET /< 资源名的复数格式 >:获得某一类型的资源列表,例如 GET /pods 返回一个 Pod 资源列表。
  • POST /< 资源名的复数格式 >:创建一个资源,该资源来自用户提供的 JSON 对象。
  • GET /< 资源名复数格式 >/< 名字 >:通过给出的名称(Name)获得单个资源,例如 GET /pods/first 返回一个名称为“first”的 Pod。
  • DELETE /< 资源名复数格式 >/< 名字 >:通过给出的名字删除单个资源,删除选项(DeleteOptions)中可以指定的优雅删除(Grace Deletion)的时间(GracePeriodSeconds),该可选项表明了从服务端接收到删除请求到资源被删除的时间间隔(单位为秒)。不同的类别(Kind)可能为优雅删除时间(Grace Period)申明默认值。用户提交的优雅删除时间将覆盖该默认值,包括值为 0 的优雅删除时间。
  • PUT /< 资源名复数格式 >/< 名字 >:通过给出的资源名和客户端提供的 JSON 对象来更新或创建资源。
  • PATCH /< 资源名复数格式 >/< 名字 >:选择修改资源详细指定的域。

patch

对于 PATCH 操作,目前 Kubernetes API 通过相应的 HTTP 首部“Content-Type”对其进行识别。

目前支持以下三种类型的 PATCH 操作。

  • JSON Patch, Content-Type: application/json-patch+json。
  • Merge Patch, Content-Type: application/merge-json-patch+json。
  • Strategic Merge Patch, Content-Type: application/strategic-merge-patch+json。

在标准的 JSON Merge Patch 中,JSON 对象总是被合并(merge)的,但是资源对象中的列表域总是被替换的。通常这不是用户所希望的。例如,我们通过下列定义创建一个 Pod 资源对象:

spec:
  containers:
    - name: nginx
      image: nginx-1.0

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

PATCH /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”域的值。

其实patch在使用过程中还是比较难以操作的,所以在修改的时候,我们更多的是使用直接修改文件的方式来进行更新

1、在线编辑

kubectl edit

2、离线编辑

导出yaml文件,然后修改文件,进行apply

kubectl apply

watch

此外,Kubernetes API 添加了资源变动的“观察者”模式的 API 接口。

  • GET /watch/< 资源名复数格式 >:随时间变化,不断接收一连串的 JSON 对象,这些 JSON 对象记录了给定资源类别内所有资源对象的变化情况。
  • GET /watch/< 资源名复数格式 >/:随时间变化,不断接收一连串的 JSON 对象,这些 JSON 对象记录了某个给定资源对象的变化情况。

上述接口改变了返回数据的基本类别,watch 动词返回的是一连串的 JSON 对象,而不是单个的 JSON 对象。并不是所有的对象类别都支持“观察者”模式的 API 接口,在后续的章节中将会说明哪些资源对象支持这种接口。

顶级API对象

API对象也就是我们常用的post的body,也就是我们使用的yaml文件或json文件的组成,符合声明式api的设计。

所以也可以直接是说是我们使用的yaml文件的组成。其实最后yaml文件都是转化为结构体进行操作的,所以最终都是符合api的设计,重这边就能知道对应的yaml组成了。

在 Kubernetes API 中,一个 API 的顶层(Top Level)元素由 kind、apiVersion、metadata、spec 和 status 等几个部分组成,接下来,我们分别对这几个部分进行说明。

kind

kind 表明对象有以下三大类别。

  • 对象(objects):代表在系统中的一个永久资源(实体),例如 Pod、RC、Service、Namespace 及 Node 等。通过操作这些资源的属性,客户端可以对该对象做创建、修改、删除和获取操作。
  • 列表(list):一个或多个资源类别的集合。列表有一个通用元数据的有限集合。所有列表(lists)通过“items”域获得对象数组。例如 PodLists、ServiceLists、NodeLists。
  • 简单类别(simple):该类别包含作用在对象上的特殊行为和非持久实体。该类别限制了使用范围,它有一个通用元数据的有限集合,例如 Binding、 Status。

我们最常用的就是objects。

apiversion

apiVersion 表明 API 的版本号,为了在兼容旧版本的同时不断升级新的 API,Kubernetes 提供了多版本 API 的支持能力,每个版本的 API 通过一个版本号路径前缀进行区分,例如 /api/v1beta3。

Metadata

Metadata 是资源对象的元数据定义,包含一组由不同名称定义的属性。在 Kubernetes 中每个资源对象都必须包含以下 3 种 Metadata。

  • namespace:对象所属的命名空间,如果不指定,系统则会将对象置于名为“default”的系统命名空间中。
  • name:对象的名字,在一个命名空间中名字应具备唯一性。
  • uid:系统为每个对象生成的唯一 ID,所以不需要我们在yaml文件中配置。

此外,每种对象还应该包含以下几个重要元数据。

  • labels:用户可定义的“标签”,键和值都为字符串的 map,是对象进行组织和分类的一种手段,通常用于标签选择器(Label Selector),用来匹配目标对象。
  • annotations:用户可定义的“注解”,键和值都为字符串的 map,被 Kubernetes 内部进程或者某些外部工具使用,用于存储和获取关于该对象的特定元数据。Annotations,它专门用来携带 key-value 格式的内部信息。所谓内部信息,指的是对这些信息感兴趣的,是 Kubernetes 组件本身,而不是用户。所以大多数 Annotations,都是在 Kubernetes 运行过程中,被自动加在这个 API 对象上。
  • resourceVersion:用于识别该资源内部版本号的字符串,在用于 Watch 操作时,可以避免在 GET 操作和下一次 Watch 操作之间造成的信息不一致,客户端可以用它来判断资源是否改变。该值应该被客户端看作不透明,且不做任何修改就返回给服务端。客户端不应该假定版本信息具有跨命名空间、跨不同资源类别、跨不同服务器的含义。
  • creationTimestamp:系统记录创建对象时的时间戳。
  • deletionTimestamp:系统记录删除对象时的时间戳。
  • selfLink:通过 API 访问资源自身的 URL,例如一个 Pod 的 link 可能是 /api/v1/namespaces/ default/pods/frontend-o8bg4。

spec

spec 是集合类的元素类型,用户对需要管理的对象进行详细描述的主体部分都在 spec 里给出,一般在这里我们会使用podtemplate来描述所创建的pod。

status

Status 用于记录对象在系统中的当前状态信息,它也是集合类元素类型,status 在一个自动处理的进程中被持久化,可以在流转的过程中生成。如果观察到一个资源丢失了它的状态(Status),则该丢失的状态可能被重新构造。以 Pod 为例,Pod 的 status 信息主要包括 conditions、containerStatuses、hostIP、phase、podIP、startTime 等。其中比较重要的两个状态属性如下。

  • phase:描述对象所处的生命周期阶段,phase 的典型值是“Pending”(创建中)“Running”“Active”(正在运行中)或“Terminated”(已终结),这几种状态对于不同的对象可能有轻微的差别,此外,关于当前 phase 附加的详细说明可能包含在其他域中。
  • condition:表示条件,由条件类型和状态值组成,目前仅有一种条件类型 Ready,对应的状态值可以为 True、False 或 Unknown。一个对象可以具备多种 condition,而 condition 的状态值也可能不断发生变化,condition 可能附带一些信息,例如最后的探测时间或最后的转变时间。

实例

说了这么多,我们来看一个api对象的yaml,详细说明都在上面了。

# kubectl get po testnew-145897-0 -n testnew -o yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    io.kubernetes.container.lxcfsEnable: "true"
    io.kubernetes.pod.pidLimit: "10000"
  creationTimestamp: "2021-01-11T08:37:18Z"
  generateName: testnew3-sit-suengine-145897-
  labels:
    app: testnew-145897-0
  name: testnew-145897-0
  namespace: testnew
  ownerReferences:
  resourceVersion: "343911023"
  selfLink: /api/v1/namespaces/testnew3/pods/testnew-145897-0
  uid: 3ea833a4-4087-4dfa-9b1c-61ffb3103d11
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: WEB
            operator: In
            values:
            - "true"
          - key: netType
            operator: In
            values:
            - inter
  automountServiceAccountToken: false
  containers:
    image: xgharborsit01.sncloud.com/sncloudmw/suengine_1.0.1_101:20200828_01
    imagePullPolicy: IfNotPresent
    lifecycle:
      preStop:
        exec:
          command:
          - /bin/bash
          - -c
          - /script/prestop.sh
    name: testnew3-sit-suengine-145897
    resources:
      limits:
        cpu: "1"
        memory: 1Gi
      requests:
        cpu: 125m
        memory: 1Gi
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /opt/logs
      mountPropagation: None
      name: log
    - mountPath: /var/lib/extrastorage
      mountPropagation: None
      name: extrastorage
      subPathExpr: $(KUBERNETES_POD_UUID)
  enableServiceLinks: true
  hostname: testnew-145897-0
  nodeName: xgpccsit02n010243129140.sncloud.com
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 1
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - emptyDir:
      sizeLimit: 8704Mi
    name: log
  - hostPath:
      path: /localstorage/pods
      type: DirectoryOrCreate
    name: extrastorage
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2021-01-11T08:37:18Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2021-03-24T11:44:48Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2021-03-24T11:44:48Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2021-01-11T08:37:18Z"
    status: "True"
    type: PodScheduled
  phase: Running
  podIP: 10.100.5.35
  podIPs:
  - ip: 10.100.5.35
  qosClass: Burstable
  startTime: "2021-01-11T08:37:18Z"

CRD(自定义资源类型)

Aggregation API和crd都可以在不修改k8s核心代码前提下扩展k8s api,先说说crd。

当你创建一个新的CustomResourceDefinition (CRD)时,Kubernetes API服务器将为你指定的每个版本创建一个新的RESTful资源路径,我们可以根据该api路径来创建一些我们自己定义的类型资源。

CRD可以是命名空间的,也可以是集群范围的,由CRD的作用域(scpoe)字段中所指定的,与现有的内置对象一样,删除名称空间将删除该名称空间中的所有自定义对象。customresourcedefinition本身没有名称空间,所有名称空间都可以使用。

通过crd资源创建自定义资源,即自定义一个Restful API和资源类型contab

创建自定义contab资源类型

$ kubectl create -f resourcedefinition.yaml

resourcedefinition.yaml

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # 名称必须与下面的spec字段匹配,格式为: <plural>.<group>
  name: crontabs.stable.example.com
spec:
  # 用于REST API的组名称: /apis/<group>/<version>
  group: stable.example.com
  # 此CustomResourceDefinition支持的版本列表
  versions:
    - name: v1
      # 每个版本都可以通过服务标志启用/禁用。
      served: true
      # 必须将一个且只有一个版本标记为存储版本。
      storage: true
  # 指定crd资源作用范围在命名空间或集群
  scope: Namespaced
  names:
    # URL中使用的复数名称: /apis/<group>/<version>/<plural>
    plural: crontabs
    # 在CLI(shell界面输入的参数)上用作别名并用于显示的单数名称
    singular: crontab
    # kind字段使用驼峰命名规则. 资源清单使用如此
    kind: CronTab
    # 短名称允许短字符串匹配CLI上的资源,意识就是能通过kubectl 在查看资源的时候使用该资源的简名称来获取。
    shortNames:
    - ct

然后在以下位置创建一个新的带有名称空间的RESTful API端点:

/apis/stable.example.com/v1/namespaces/*/crontabs/...然后我们可以使用该url来创建和管理自定义对象资源。

查看自定义contab资源的信息

$ kubectl get contab(ct)

创建

刚刚自定义了ct类型,我们可以创建这种kind的资源实例了。

my-crontab.yaml

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image

创建自定义资源contab资源的对象

$ kubectl create -f my-crontab.yaml

查看

$ kubectl get contab(ct)

验证

修改resourcedefinition.yaml

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 is the schema for validating custom objects.
    openAPIV3Schema:
      properties:
        spec:
          properties:
            cronSpec: #--必须是字符串,并且必须是正则表达式所描述的形式
              type: string
              pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
            replicas: #----必须是整数,最小值必须为1,最大值必须为10
              type: integer
              minimum: 1
              maximum: 10

可以看到validation就是验证规则。

这个适合如果创建下面这个类型的实例就会失败

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * *"
  image: my-awesome-cron-image
  replicas: 15

新增打印信息

在crd文件中添加“additionalPrinterColumns:”字段声明

resourcedefinition.yaml:

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

查看自定义资源对象基本信息,可以发现多了Spec,Replicas,Age。

$ kubectl get crontab my-new-cron-object

NAME                       SPEC        REPLICAS   AGE
my-new-cron-object   * * * * *            1         7s
注意:name列不需要定义默认会有的

为自定义的资源添加状态和伸缩配置

subresources: 字段来声明状态和伸缩信息。

resourcedefinition.yaml

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子资源
    scale:
      specReplicasPath: .spec.replicas
      statusReplicasPath: .status.replicas
      labelSelectorPath: .status.labelSelector

然后就可以通过scale来扩缩副本数量了。

$ kubectl scale --replicas=5 crontabs/my-new-cron-object

上面就是crd的基本用法,其实crd主要是用于自定义controller的开发的第一步,只是定义了一个资源,如何能够控制这个crd,我们还需要控制器开发

kubernetes Aggregated API Servers

使用聚合层(Aggregation Layer),用户可以通过额外的 API 扩展 Kubernetes, 而不局限于 Kubernetes 核心 API 提供的功能。

Aggregated API是允许k8s的开发人员编写一个自己的服务,可以把这个服务注册到k8s的api里面,这样,就像k8s自己的api一样,你的服务只要运行在k8s集群里面,k8s 的Aggregate通过service名称就可以转发到你写的service里面去了。

这时候,我们需要创建一个新的组件,名为kube-aggregator,它需要负责以下几件事:

  • 提供用于注册API server的API
  • 汇总所有的API server信息
  • 代理所有的客户端到API server的请求

注意:这里说的API server是一组“API Server”,而不是说我们安装集群时候的那个APIserver组件,而且这组API server是可以横向扩展的。

这种设计理念有着很多的好处:

  • 第一是增加了api的扩展性,这样k8s的开发人员就可以编写自己的API服务器来公开他们想要的API。集群管理员应该能够使用这些服务,而不需要对核心库存储库进行任何更改。
  • 第二是丰富了APIs,核心kubernetes团队阻止了很多新的API提案。通过允许开发人员将他们的API作为单独的服务器公开,并使集群管理员能够在不对核心库存储库进行任何更改的情况下使用它们,这样就无须社区繁杂的审查了
  • 第三是开发分阶段实验性API的地方,新的API可以在单独的聚集服务器中开发,当它稳定之后,那么把它们封装起来安装到其他集群就很容易了。
  • 第四是确保新API遵循kubernetes约定:如果没有这里提出的机制,社区成员可能会被迫推出自己的东西,这可能会或可能不遵循kubernetes约定。

有两种方式来启用kube-aggregator:

  • 使用test mode/single-user mode,作为一个独立的进程来运行

    kube-aggregator二进制文件已经包含在kubernetes release里面了。
    
  • 使用gateway mode,kube-apiserver集成了kube-aggregator组件。

    开启 Aggregator,只要修改apiserver的启动参数就好
    
        --requestheader-client-ca-file=<path to aggregator CA cert>
        --requestheader-allowed-names=aggregator
        --requestheader-extra-headers-prefix=X-Remote-Extra-
        --requestheader-group-headers=X-Remote-Group
        --requestheader-username-headers=X-Remote-User
        --proxy-client-cert-file=<path to aggregator proxy cert>
        --proxy-client-key-file=<path to aggregator proxy key>
        --enable-aggregator-routing=true
    
    Kubeadm 搭建的集群默认已经开启了,minikube也是。其实就是配置了一些证书,用于自定义的apiservice和kube-apiserver进行通信,因为是机密的需要证书认证。
    

APIService 的最常见实现方式是在集群中某 Pod 内运行 扩展 API 服务器。 如果你在使用扩展 API 服务器来管理集群中的资源,该扩展 API 服务器(也被写成“extension-apiserver”) 一般需要和一个或多个控制器一起使用。 apiserver-builder 库同时提供构造扩展 API 服务器和控制器框架代码。

扩展 API 服务器与 kube-apiserver 之间需要存在低延迟的网络连接。 发现请求需要在五秒钟或更短的时间内完成到 kube-apiserver 的往返。

基础概览

apiservice

我们来看看基本使用,先看一个资源配置清单

apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
  name: v1alpha1.custom-metrics.metrics.k8s.io
spec:
  insecureSkipTLSVerify: true
  group: custom-metrics.metrics.k8s.io
  groupPriorityMinimum: 1000
  versionPriority: 15
  service:
    name: api
    namespace: custom-metrics
  version: v1alpha1

上面就定义了资源类型为APIService,service名称为api,空间为custom-metrics的一个资源聚合接口。

  • 使用apiregistration.k8s.io/v1beta1 版本的APIService,在metadata.name中定义该API的名字。
  • insecureSkipTLSVerify:当与该服务通信时,禁用TLS证书认证。强加建议不要设置这个参数,默认为 false。应该使用CABundle代替。
  • service:与该APIService通信时引用的service,其中要注明service的名字和所属的namespace,如果为空的话,则所有的服务都会该API groupversion将在本地443端口处理所有通信。
  • groupPriorityMinimum:该组API的处理优先级,主要排序是基于groupPriorityMinimum,该数字越大表明优先级越高,客户端就会与其通信处理请求。次要排序是基于字母表顺序,例如v1.bar比v1.foo的优先级更高。
  • versionPriority:VersionPriority控制其组内的API版本的顺序。必须大于零。主要排序基于VersionPriority,从最高到最低(20大于10)排序。次要排序是基于对象名称的字母比较。 (v1.foo在v1.bar之前)由于它们都是在一个组内,因此数字可能很小,一般都小于10。

可以看看官方的模版

apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  name: < 注释对象名称 >
spec:
  group: < 拓展 Apiserver 的 API group 名称 >
  version: < 拓展 Apiserver 的 API version>
  groupPriorityMinimum: < APIService 对对应 group 的优先级, 参考 API 文档 >
  versionPriority: < 优先考虑 version 在 group 中的排序, 参考 API 文档 >
  service:
    namespace: < 拓展 Apiserver 服务的 namespace >
    name: < 拓展 Apiserver 服务的 name >
  caBundle: < PEM 编码的 CA 证书,用于对 Webhook 服务器的证书签名 >

聚合层在 kube-apiserver 进程内运行。在扩展资源注册之前,聚合层不做任何事情。要注册 API,用户必须添加一个 APIService 对象,用它来申领 Kubernetes API 中的 URL 路径。自此以后,聚合层将会把发给该 API 路径的所有内容(例如 /apis/myextension.mycompany.io/v1/…)代理到已注册的 APIService。

查看我们使用上面的yaml文件创建的APIService。