【k8s基础篇】k8s基础2之GVK与GVR

参考

  • 初识 Kubernetes API 的组织结构

  • Kubernetes CRD 系列:Api Server 和 GVK®

  • K8S API核心原理介绍:K8S学习系列5

  • Groups、Versions 和 Kinds 之间的关系

理解

1 | GVK 和 GVR 是什么?

  • GVK 就是 group、verison、kind
  • GVR 就是 group、version、resource

Q1 | 为什么有 kind 和 resouce 两个相似概念

  • 首先我们要明确几个概念:
    • 在编码过程中,资源数据的存储都是以结构体存储(称为 Go type)
      • 由于多版本version的存在(alpha1,beta1,v1等),不同版本中存储结构体的存在着差异,但是我们都会给其相同的 Kind 名字(比如 Deployment)
      • 因此,我们编码中只用 Kind 名(如 Deployment),并不能准确获取到其使用哪个版本结构体
      • 所以,采用 GVK 获取到一个具体的 存储结构体,也就是 GVK 的三个信息(group/verion/kind) 确定一个 Go type(结构体)
        • 如何获取呢? —— 通过 Scheme, Scheme 存储了 GVK 和 Go type 的映射关系
    • 在创建资源过程中,我们编写 yaml,提交请求:
      • 编写 yaml 过程中,我们会写 apiversion 和 kind,其实就是 GVK
      • 而客户端(也就是我们)与 apiserver 通信是 http 形式,就是将请求发送到某一 http path
      • 发送到哪个 http path 呢?
        • 这个 http path 其实就是 GVR
          • /apis/batch/v1/namespaces/default/job 这个就是表示 default 命名空间的 job 资源
          • 我们 kubectl get po 时 也是请求的路径 也可以称之为 GVR
        • 其实 GVR 是由 GVK 转化而来 —— 通过REST映射的RESTMappers实现
  • 总结
    • 同 Kind 由于多版本会存在 多个数据结构(Go type)
    • GVK 可以确定一个具体的 Go Type(映射关系由 Scheme 维护)
    • GVK 可以转换 http path 请求路径(也就是 GVR)(映射由RESTMappers实现)
    • GVK和GVR是相关的。GVK在GVR标识的HTTP路径下提供服务。将GVK映射到GVR的过程称为REST映射。我们将在“ REST Mapping”中看到在Golang中实现REST映射的RESTMappers。

Q2 | 相同名称 Kind 可以存在不同组中吗?

相同名称的Kind不仅可以同时存在于不同的Version中(这种很常见),而且可以同时存在于不同的API Group(这种情况相对少)。例如,Deployment作为一个kind,在 extensions group中以alpha类型开始,最终在apps.k8s.io这个API Group 中提升为Stable。我们常见的有:

  • Ingress, NetworkPolicy同时在这个两个API Group: extensions、 networking.k8s.io

  • Deployment, DaemonSet, ReplicaSet同时在这些API Group中:extensions、 apps

  • Event同时在这些API Group中: core group and events.k8s.io

2 |GVK vs GVR

Kubernetes API 通过 HTTP 协议以 RESTful 的形式提供,API 资源的序列化方式主要是以 JSON 格式进行,但为了内部通信也支持 Protocol Buffer 格式。为了方便扩展与演进,kubernetes API 支持分组与多版本,这体现在不同的 API 访问路径上。有了分组与多版本支持,即使要在新版本中去掉 API 资源的特定字段或者重构 API 资源的展现形式,也可以保证版本之间的兼容性。

API-group

将整个 kubernetes API 资源分成各个组,可以带来很多好处:

  • 各组可以单独打开或者关闭[7]

  • 各组可以有独立的版本,在不影响其他组的情况下单独向前衍化

  • 同一个资源可以同时存在于多个不同组中,这样就可以同时支持某个特定资源稳定版本与实验版本

关于 kubernetes API 资源的分组信息可以在序列化的资源定义中有所体现,例如:

apiVersion: apps/v1kind: Deploymentmetadata:  name: example-deployspec:...

其中 apiVersion 字段中 apps 即为 Deployment 资源的分组,实际上,Deployment 不止出现在 apps 分组里,也出现在 extensions 分组中,不同的分组可以实验不同的特性;另外,kubernetes 中的核心资源如 pod、namespace、configmap、node、service 等存在于 core 分组中,但是由于历史的原因,core 不出现在 apiVersion 字段中,例如以下定义一个 pod 资源的序列化对象:

apiVersion: v1kind: Podmetadata:  name: example-pod  labels:    app: exp...

API 分组也体现在访问资源的 RESTful API 路径上,core 组中的资源访问路径一般为 /api/$VERSION,其他命名组的资源访问路径则是 /apis/$GROUP_NAME/$VERSION,此外还有一些系统级别的资源,如集群指标信息 /metrics,以上这些就基本构成了 kubernetes API 的树结构:

【k8s基础篇】k8s基础2之GVK与GVR_第1张图片

API-version

为了支持独立的演进,kubernetes API 也支持不同的版本,不同的版本代表不同的成熟度。注意,这里说的是 API 而非资源支持多版本。因为多版本支持是针对 API 级别,而不是特定的资源或者资源的字段。一般来说,我们根据 API 分组、资源类型、namespace 以及 name 来区分不同的资源对象,对于同一个资源对象的不同版本,API-Server 负责不同版本之间的无损切换,这点对于客户端来说是完全透明的(无感知)。事实上,不同版本的同类型的资源在持久化层的数据可能是相同的。例如,对于同一种资源类型支持 v1v1beta1 两个 API 版本,以 v1beta1 版本创建该资源的对象,后续可以以v1 或者 v1beta1 来更新或者删除该资源对象。

API 多版本支持一般通过将资源分组置于不同的版本中来实现,例如,batch 同时存在 v2alph1v1 版本。一般来说,新的资源分组先出现 v1alpha1 版本,随着稳定性的提高被推进到 v1beta1 ,最后从 v1 版本毕业。

随着新的用户场景出现,kubernetes API 需要不断变化,可能是新增一个字段,也可能是删除旧的字段,甚至是改变资源的展现形式。为了保证兼容性,kubernetes 制定了一系列的策略[8]。总的来说,对于已经 GA 的 API,API,kubernetes 严格维护其兼容性,终端用户可以放心食用,beta 版本的 API 则尽量维护,保证不打破版本跨版本之间的交互,而对于 alpha 版本的 API 则很难保证兼容性,不太推荐生产环境使用。

GVK 与 GVR 映射

在 kubernetes API 宇宙中,我们经常使用属于 GVK 或者 GVR 来区分特定的 kubernetes 资源。其中 GVK 是 Group Version Kind 的简称,而 GVR 则是 Group Version Resource 的简称。

通过上面对于 kubernetes API 分组和多版本的介绍中我们已经了解了 Group 与 Version,那么 Kind 与 Resource 又分别是指什么呢?

Kind 是 API “顶级”资源对象的类型,每个资源对象都需要 Kind 来区分它自身代表的资源类型,例如,对于一个 pod 的例子:

apiVersion: v1kind: Podmetadata:  name: example-pod  labels:    app: exp...

其中 kind 字段即代表该资源对象的类型。一般来说,在 kubernetes API 中有三种不同的 Kind:

  1. 单个资源对象的类型,最典型的就是刚才例子中提到的 Pod

  2. 资源对象的列表类型,例如 PodList 以及 NodeList 等

  3. 特殊类型以及非持久化操作的类型,很多这种类型的资源是 subresource, 例如用于绑定资源的 /binding、更新资源状态的 /status 以及读写资源实例数量的 /scale

需要注意的是,同 Kind 不止可以出现在同一分组的不同版本中,如 apps/v1beta1apps/v1,它还可能出现在不同的分组中,例如 Deployment 开始以 alpha 的特性出现在 extensions 分组,GA 之后被推进到 apps 组,所以为了严格区分不同的 Kind,需要组合 API Group、API Version 与 Kind 成为 GVK

Resource 则是通过 HTTP 协议以 JSON 格式发送或者读取的资源展现形式,可以以单个资源对象展现,例如 .../namespaces/default,也可以以列表的形式展现,例如 .../jobs。要正确的请求资源对象,API-Server 必须知道 apiVersion 与请求的资源,这样 API-Server 才能正确地解码请求信息,这些信息正是处于请求的资源路径中。一般来说,把 API Group、API Version 以及 Resource 组合成为 GVR 可以区分特定的资源请求路径,例如 /apis/batch/v1/jobs 就是请求所有的 jobs 信息。

GVR 常用于组合成 RESTful API 请求路径。例如,针对应用程序 v1 部署的 RESTful API 请求如下所示:

GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}

通过获取资源的 JSON 或 YAML 格式的序列化对象,进而从资源的类型信息中可以获得该资源的 GVK;相反,通过 GVK 信息则可以获取要读取的资源对象的 GVR,进而构建 RESTful API 请求获取对应的资源。这种 GVK 与 GVR 的映射叫做 RESTMapper。Kubernetes 定义了 RESTMapper 接口[9]并带默认带有实现 DefaultRESTMapper[10]。

关于 kubernetes API 的详细规范请参考 API Conventions[11]

3 | 如何储存

经过上一章节的研究,我们已经知道了 kubernetes API 的组织结构以及背后的设计原理,那么,Kubernetes API 的资源对象最终是怎么提供可靠存储的。之前也提到了 API-Server 是无状态的,它需要与分布式存储系统 etcd[12] 交互来实现资源对象的持久化操作。从概念上讲,etcd 支持的数据模型是键值(key-value)存储。在 etcd2 中,各个 key 是以层次结构存在,而在 etcd3 中这个就变成了平级模型,但为了保证兼容性也保持了层次结构的方式。

在 Kubernetes 中 etcd 是如何使用的呢?实际上,前面也提到了,etcd 被部署为独立的部分,甚至多个 etcd 可以组成集群,API-Server 负责与 etcd 交互来完成资源对象的持久化。从 1.5.x 之后,Kubernetes 开始全面使用 etcd3。可以在 API-Server 的相关启动项参数中配置使用 etcd 的方式:

$ kube-apiserver -h
...
Etcd flags:
 
      --etcd-cafile string
                SSL Certificate Authority file used to secure etcd communication.
      --etcd-certfile string
                SSL certification file used to secure etcd communication.
      ...
      --etcd-keyfile string
                SSL key file used to secure etcd communication.
      --etcd-prefix string
                The prefix to prepend to all resource paths in etcd. (default "/registry")
      ...
      --storage-backend string
                The storage backend for persistence. Options: 'etcd3' (default).
      --storage-media-type string
                The media type to use to store objects in storage. Some resources or storage backends may only support a specific media type and will ignore this setting. (default
                "application/vnd.kubernetes.protobuf")
...

Kubernetes 资源对象是以 JSON 或 Protocol Buffers 格式存储在 etcd 中,这可以通过配置 kube-apiserver 的启动参数 --storage-media-type 来决定想要序列化数据存入 etcd 的格式,默认情况下为 application/vnd.kubernetes.protobuf 格式;另外也可以通过配置 --storage-versions 启动参数来配置每个 API 分组的资源对象的持久化存储的默认版本号。

下面通过一个简单的例子来看,创建一个 pod,然后使用 etcdctl 工具来查看存储在 etcd 中数据:

$ cat << EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: webserver
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
EOF
pod/webserver created
$ etcdctl --endpoints=$ETCD_URL \
  --cert /etc/kubernetes/pki/etcd/server.crt \
  --key /etc/kubernetes/pki/etcd/server.key \
  --cacert /etc/kubernetes/pki/etcd/ca.crt \
  get /registry/pods/default/webserver --prefix -w simple
/registry/pods/default/webserver
...
10.244.0.5"

使用各种客户端工具创建资源对象到然后存储到 etcd 的流程大致如下图所示:

【k8s基础篇】k8s基础2之GVK与GVR_第2张图片

  1. 客户端工具(例如 kubectl)提供一个期望状态的资源对象的序列化表示,该例子使用 YAML 格式提供

  2. kubectl 将 YAML 转换为 JSON 格式,并发送给 API-Server

  3. 对应同类型对象的不同版本,API-Server 执行无损转换。对于老版本中不存在的字段则存储在 annotations 中

  4. API-Server 将接收到的对象转换为规范存储版本,这个版本由 API-Server 启动参数指定,一般是最新的稳定版本

  5. 最后将资源对象通过 JSON 或 protobuf 方式解析并通过一个特定的 key 存入 etcd 当中

上面提到的无损转换是如何进行的?下面使用 Kubernetes 资源对象对象 Horizontal Pod Autoscaling (HPA) 来举例说明:

$ kubectl proxy --port=8080 &
$ cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webserver
spec:
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      labels:
        app: webserver
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
EOF
$ kubectl autoscale deployment webserver --min=2 --max=5 --cpu-percent=80
$ curl http://127.0.0.1:8001/apis/autoscaling/v2beta1/namespaces/default/horizontalpodautoscalers/webserver > hpa-v2beta1.json
$ curl http://127.0.0.1:8001/apis/autoscaling/v2beta2/namespaces/default/horizontalpodautoscalers/webserver > hpa-v2beta2.json
$ diff hpa-v2beta1.json hpa-v2beta2.json
3c3
<   "apiVersion": "autoscaling/v2beta1",
---
>   "apiVersion": "autoscaling/v2beta2",
42c42,45
<           "targetAverageUtilization": 80
---
>           "target": {
>             "type": "Utilization",
>             "averageUtilization": 80
>           }

通过上面命令的输出能够看出,即使 HorizontalPodAutoscale 的版本从 v2beta1 变为了 v2beta2,API-Server 也能够在不同的版本之前无损转换,不论在 etcd 中实际存的是哪个版本。实际上,API-Server 将所有已知的 Kubernetes 资源类型保存在名为 Scheme 的注册表(registry)中。在此注册表中,定义了每种 Kubernetes 资源的类型、分组、版本以及如何转换它们,如何创建新对象,以及如何将对象编码和解码为 JSON 或 protobuf 格式的序列化形式。

你可能感兴趣的:(Kubernetes学习笔记,kubernetes,docker,go)