APIService 资源

在 Kubernetes 1.7 版本中,通过 API 服务器聚合,可以将 自定义 API 服务器 与 Kubernetes 集群中的主 API 服务器继承。

API服务器聚合

图中,自定义 API 服务器 X 使用主 API 服务器的 etcd 实例存储数据;自定义 API 服务器 Y 使用自己的 etcd 实例存储数据

当基于 API 服务器聚合,针对自定义资源对象设计了 自定义 API 服务器 时,就不必再通过创建 CRD 资源来表示我们的自定义资源对象,而是可以直接将自定义对象类型实现到自定义 API 服务器中。

🚀 自定义 API 服务器可以通过 APIService 资源进行定义,生成对象后,主服务器将为其分配 URL,所有请求该 URL 的数据会被转发至自定义 API 服务器中。假设 APIService 中的 spec.group=extensions.example.comspec.version=v1alpha1,则该自定义 API 服务器的 URL 为 /apis/extensions.example.com/v1alpha1/

🍓 光定义 APIService 对象是不够的,还需要将自定义 API 服务器运行在集群中,可以将其部署为一个 Pod 并通过 Service 暴露。

在注册完自定义 API 服务器后,可以通过 HTTP 请求或构建自定义 CLI 客户端的方式,向自定义 API 服务器提交请求,实现具体功能。

官方部署自定义 APIService步骤

官方指导的部署自定义 API Service 步骤如下:

  1. 确保 API Service 已启用,这需要通过 kube-apiserver 组件的启动参数 --runtime-config 进行设置,默认是启用的

  2. 建议创建一个 RBAC 规则,允许添加 APIService 资源对象,因为 API 扩展 对整个Kubernetes 集群都生效,所以不推荐在生产环境中对API扩展进行开发或测试

  3. 创建一个新的 Namespace 用于运行 APIService

  4. 创建一个 CA 证书 用于对 自定义 API Server 的 HTTPS 安全访问进行签名

  5. 创建 服务端证书和秘钥 用于 自定义API Server 的 HTTPS 安全访问「服务端证书应该由上面提及的 CA证书 进行签名,也应该包含含有DNS域名格式的CN名称」

  6. 在新的 Namespace 中使用服务端证书和秘钥创建 Kubernetes Secret 对象

  7. 部署自定义 API Server 实例,通常可以以 Deployment 形式进行部署,并且将之前创建的 Secret 挂载到容器内部「该Deployment也应被部署在新的 Namespace 中」

  8. 确保自定义的 API Server 通过 Volume 加载了 Secret 中的证书,这将用于后续的 HTTPS 握手校验

  9. 在新的 Namespace 中创建一个 ServiceAccount 对象

  10. 创建一个 ClusterRole 用于对 自定义 API 资源 进行操作

  11. 使用之前创建的 ServiceAccount 为刚刚创建的 ClusterRole 创建一个 ClusterRolebinding

  12. 使用之前创建的 ServiceAccount 为系统 ClusterRole "system:auth-delegator" 创建一个ClusterRolebinding,以使其可以将认证决策代理转发给 Kubernetes 核心 API Server

  13. 使用之前创建的 ServiceAccount 为系统 Role "extension-apiserver-authenticationreader" 创建一个 Rolebinding,以允许 自定义 API Server 访问名为 "extension-apiserverauthentication" 的系统 ConfigMap

  14. 创建 APIService 资源对象

  15. 访问 APIService 提供的 API URL 路径,验证对资源的访问能否成功

案例

在本案例中,创建一个没有实际功能的自定义 API Server。

自定义 API Server 实例中运行的代码如下,比较简单,监听了 /apis/example.k8s.io/v1//apis/example.k8s.io/v1/pods 这些路径:

package main

import (
    "fmt"
    "net/http"
)

var prefix = "/apis/example.k8s.io/v1"

func main()  {
    http.HandleFunc(prefix + "/pods/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hit '/pods/' and URL is '" + r.URL.Path + "'")
    })

    http.HandleFunc(prefix + "/pods", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hit '/pods' and URL is '" + r.URL.Path + "'")
    })

    http.HandleFunc(prefix + "/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hit '/' and URL is '" + r.URL.Path + "'")
    })

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hit '/' and URL is '" + r.URL.Path + "'")
    })

    // 监听的是 HTTPS 协议
    http.ListenAndServeTLS("", "server.crt", "server.key", nil)
}

自定义 API Server 实例由于使用的是 HTTPS 协议,因此需要生成相应的服务端证书和密钥文件,这里我们直接通过下面两句签发即可:

openssl genrsa -out server.key 2048
openssl req -new -x509 -key server.key -out server.crt -days 365

在一切准备就绪后,创建相应的 Dockerfile 文件,并生成 Docker 镜像,上传至镜像仓库中:

FROM corfr/tcpdump

RUN apk --no-cache add curl

ADD example-server /
ADD server.crt /
ADD server.key /

ENTRYPOINT [ "/example-server" ]

在准备好自定义 API Server 实例镜像后,即可以开始进行部署,部署所需的资源如下图所示:

案例图

使用的 YAML 配置文件如下,其中使用 Deployment 部署了自定义 API Server 实例:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-server
  namespace: example-namespace
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-server
  namespace: example-namespace
  labels:
    k8s-app: example-server
spec:
  selector:
    matchLabels:
      k8s-app: example-server
  template:
    metadata:
      name: example-server
      labels:
        k8s-app: example-server
    spec:
      serviceAccountName: example-server
      containers:
      - name: example-server
        image: ysj/example-server:https
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - name: tmp-dir
          mountPath: /tmp
      volumes:
      - name: tmp-dir
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: example-server
  namespace: example-namespace
  labels:
    kubernetes.io/name: "example-server"
spec:
  selector:
    k8s-app: example-server
  ports:
  - port: 443
    protocol: TCP
    targetPort: 443
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:aggregated-metrics-reader
  labels:
    rbac.authorization.k8s.io/aggregate-to-view: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
rules:
- apiGroups: ["example.k8s.io"]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: system:example-server
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - services
  verbs:
  - get
  - list
  - watch
  - create
  - delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:example-server
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:example-server
subjects:
- kind: ServiceAccount
  name: example-server
  namespace: example-namespace
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: example-server:system:auth-delegator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: example-server
  namespace: example-namespace
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: example-server-auth-reader
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
  name: example-server
  namespace: example-namespace
---
apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
  name: v1.example.k8s.io
spec:
  service:
    name: example-server
    namespace: example-namespace
  group: example.k8s.io
  version: v1
  insecureSkipTLSVerify: true
  groupPriorityMinimum: 100
  versionPriority: 100
  • insecureSkipTLSVerify 当与该服务通信时,禁用 TLS 证书认证。强烈加建议不要设置这个参数为 true,默认为 false。应该使用 CABundle 代替

  • service:与该 APIService 通信时引用的 Service 对象,其中要注明 Service 的名字和所属的 Namespace

  • groupPriorityMinimum:该组 API 的处理优先级,主要排序基于 groupPriorityMinimum,该数字越大表明优先级越高,客户端就会与其通信处理请求。次要排序是基于字母表顺序,例如 v1.bar 比 v1.foo 的优先级更高

  • versionPriority:控制其组内的 API 版本的顺序。必须大于零。主要排序基于 versionPriority,从最高到最低(20 大于 10)排序。次要排序是基于对象名称的字母比较。 (v1.foo 在 v1.bar 之前)由于它们都是在一个组内,因此数字可能很小,一般都小于 10

🐙 根据 APIService 的配置可以看出访问自定义 API Server 的 URL 路径为:/apis/example.k8s.io/v1/

验证

通过访问自定义 API Server 的 URL 路径,并通过其返回值确认是否访问成功。

在访问之前,首先通过 kubectl proxy 将原生 API Server 的端口映射至本地,然后开始访问:

测试结果

从测试结果中可以看出,部署成功。

Last updated

Was this helpful?