概述

在上一篇中,我介绍了如何创建一个 CRD 以及通过这个 CRD 来创建 CR。这些关于 CR 的操作都是通过 kubectl 命令来完成的,但是,事实上,到目前位置,kubectl 是如何操作这些 CR 的我们还是不清楚。

对于后面还有很多内容都是需要代码和 CR 关联的,所以,我觉得在开始一些代码的工作之前,有必要先对 Kubernetes 的 Resource 机制进行一些介绍。在 Kubernetes 入门第二篇 中,我介绍了 API Server 是用于资源操作的组件,事实上,之前我是说可以认为是作为 etcd 的一个接口来理解,但是,实际上 API Server 完成的工作不仅仅这么简单,从上一篇的这张图可以看到:

API Server 完成了相当多的事情,但是,就目前而言,这些事情基本上都还不需要关心,这一篇要关心的就是,到底 kubectl 是如何操作 API Server 中的资源的。

GVK

从上一篇中的 CRD 定义中,可以发现在 Kubernetes 中要想完成一个 CRD,需要指定 group/kindversion,这个在 Kubernetes 的 API Server 中简称为 GVK。GVK 是定位一种类型的方式,例如,daemonsets 就是 Kubernetes 中的一种资源,当我们跟 Kubernetes 说我想要创建一个 daemonsets 的时候,kubectl 是如何知道该怎么向 API Server 发送呢?是所有的不同资源都发向同一个 URL,还是每种资源都是不同的?

这就得看回我定义 daemonsets 的描述文件了:

[[email protected]]# head -4 00-sample-daemonsets.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter

这里我就声明了 apiVersion 是 apps/v1,其实就是隐含了 Group 是 apps,Version 是 v1,Kind 就是定义的 DaemonSet,而 kubectl 接收到这个声明之后,就可以根据这个声明去调用 API Server 对应的 URL 去获取信息,例如这个就是 /api/apps/v1/daemonset(kubetl 怎么知道 DaemonSet 对应的就是 daemonset?第一篇有说到)。

可能到这里你已经猜到了,Kubernetes 组织资源的方式是以 REST 的 URI 的形式来的,而组织的路径就是:

额,不对,不是说是 G(roup)V(ersion)K(ind) 的吗,怎么这里又是 G(roup)V(ersion)R(ource) ?嘿,是的,这就是 API Server 中的第二个概念,GVR。

GVR

其实,理解了 GVK 之后再理解 GVR 就很容易了,这就是面向对象编程里面的类和对象的概念是一样的:

Kubernetes OOP
Kind Class
Resource Object

好理解吧,Kind 其实就是一个类,用于描述对象的;而 Resource 就是具体的 Kind,可以理解成类已经实例化成对象了。

REST Mapping

当我们要定义一个 GVR 的时候,那么怎么知道这个 GVR 是属于哪个 GVK 的呢?也就是前面说的,kubectl 是如何从 YAML 描述文件中知道该请求的是哪个 GVR URL?这就是 REST Mapping 的功能,REST Mapping 可以指定一个 GVR(例如 daemonset 的这个例子),然后它返回对应的 GVK 以及支持的操作等。

在代码中,其实就对应于这个接口:

这样,就把 GVK 和 GVR 联系起来了。

API Server

既然,前面都说了,API Server 可以通过 URI 的形式访问到资源,那么这里我就来实战一下,一般来说,如果是正常安装的 Kubernetes 集群,那么肯定会有 http 证书以及认证证书,这会让我的尝试很麻烦,所以,为了去掉这重麻烦,我会通过 kubectl proxy 来代理一个本地的 API Server 端口,这样就可以绕过认证了,直接可以以 http 的形式进行:

[[email protected]]# kubectl proxy --port=9090
Starting to serve on 127.0.0.1:9090

然后直接访问 API Server:

[[email protected]]# GET http://127.0.0.1:9090/apis/apps/v1/daemonsets/
{
  "kind": "DaemonSetList",
  "apiVersion": "apps/v1",
  "metadata": {
    "selfLink": "/apis/apps/v1/daemonsets/",
    "resourceVersion": "209282"
  },
  "items": [
    {
      "metadata": {
        "name": "svclb-traefik",
        "namespace": "kube-system",
        "selfLink": "/apis/apps/v1/namespaces/kube-system/daemonsets/svclb-traefik",
        "uid": "bd42f48c-42de-4df6-96bd-15e56bdfaec7",
        "resourceVersion": "176921",
        "generation": 2,
... ...

因为内容太多,就不一一展示了。

核心资源

如果想看系统支持哪些 GVK,那么可以通过 kubectl 的命令查看:

[[email protected]]# k api-resources
NAME SHORTNAMES APIGROUP NAMESPACED KIND
pods po true Pod
services svc true Service
mutatingwebhookconfigurations admissionregistration.k8s.io false MutatingWebhookConfiguration
validatingwebhookconfigurations admissionregistration.k8s.io false ValidatingWebhookConfiguration
customresourcedefinitions crd,crds apiextensions.k8s.io false CustomResourceDefinition
apiservices apiregistration.k8s.io false APIService
controllerrevisions apps true ControllerRevision
daemonsets ds apps true DaemonSet

这里可能你会发现一些特别的地方,就是对于 PodService,它的 API GROUP 居然是空的,这又是什么意思?这其实就是 Kubernetes 核心资源的含义,也就是所谓的 Kubernetes 中的基础资源(Kubernetes Resource),他们不需要 Group,只有 Version 和 Kind,其实,我认为这是历史原因导致的,在 Kubernetes 的开始之初还不支持自定义类型的时候就没考虑过 Group。

Ref