关于后端:Kubernetes-Admission-Controller-简介-注入-sidacar-示例

9次阅读

共计 6011 个字符,预计需要花费 16 分钟才能阅读完成。

Admission Controller

Kubernetes Admission Controller(准入控制器)是什么?

如下图所示:

当咱们向 k8s api-server 提交了申请之后,须要通过认证鉴权、mutation admission、validation 校验等一系列过程,最初才会将资源对象长久化到 etcd 中(其它组件诸如 controller 或 scheduler 等操作的也是长久化之后的对象)。而所谓的 Kubernetes Admission Controller 其实就是在这个过程中所提供的 webhook 机制,让用户可能在资源对象被长久化之前任意地批改资源对象并进行自定义的校验。

应用 Kubernetes Admission Controller,你能够:

  • 安全性:强制施行整个命名空间或集群范畴内的平安标准。例如,禁止容器以 root 身份运行或确保容器的根文件系统始终以只读形式挂载;只容许从企业已知的特定注册核心拉取镜像,回绝未知的镜像源;回绝不合乎平安规范的部署。
  • 治理:强制恪守某些实际,例如具备良好的标签、正文、资源限度或其余设置。一些常见的场景包含:在不同的对象上强制执行标签验证,以确保各种对象应用适当的标签,例如将每个对象调配给团队或我的项目,或指定应用程序标签的每个部署;主动向对象增加正文。
  • 配置管理:验证集群中对象的配置,并避免任何显著的谬误配置影响到您的集群。准入控制器能够用于检测和修复部署了没有语义标签的镜像,例如:主动增加资源限度或验证资源限度;确保向 Pod 增加正当的标签;确保在生产部署的镜像不应用 latest tag 或带有 -dev 后缀的 tag。

Admission Controller(准入控制器)提供了两种 webhook:

  • Mutation admission webhook:批改资源对象
  • Validation admission webhook:校验资源对象

所谓的 webhook 其实就是你须要部署一个 HTTPS Server,而后 k8s 会将 admission 的申请发送给你的 server,当然你的 server 须要依照约定格局返回响应。

应用 Kubernetes Admission Controller,你须要:

  • 确保 k8s 的 api-server 开启 admission plugins
  • 筹备好 TLS/SSL 证书,用于 HTTPS,能够是自签的。
  • 构建本人的 HTTPS server,实现解决逻辑。
  • 配置 MutatingWebhookConfiguration 或者 ValidatingWebhookConfiguration,你得通知 k8s 怎么跟你的 server 通信。

注入 sidacar 示例

接下来,咱们来实现一个最简略的为 pod 注入 sidacar 的示例。

1. 确保 k8s 的 api-server 开启 admission plugins

首先须要确认你的 k8s 集群反对 admission controller。

执行 kubectl api-resources | grep admission

mutatingwebhookconfigurations       admissionregistration.k8s.io/v1     false   MutatingWebhookConfiguration
validatingwebhookconfigurations     admissionregistration.k8s.io/v1     false   ValidatingWebhookConfiguration

失去以上后果就阐明你的 k8s 集群反对 admission controller。

而后须要确认 api-server 开启 admission plugins,依据你的 api-server 的启动形式,确认如下参数:

--enable-admission-plugins=MutatingAdmissionWebhook,ValidatingAdmissionWebhook

plugins 能够有多个,用逗号分隔,本示例其实只须要 MutatingAdmissionWebhook,至于其它的 plugins 用处请参考官网文档。

2. 筹备 TLS/SSL 证书

这里咱们应用自签的证书,先创立一个证书目录,比方 ~/certs,以下操作都在这个目录下进行。

  1. 创立咱们本人的 root CA

    openssl genrsa -des3 -out rootCA.key 4096
    openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt
  2. 创立证书

    openssl genrsa -out mylocal.com.key 2048
    openssl req -new -key mylocal.com.key -out mylocal.com.csr
  3. 应用咱们本人的 root CA 去签咱们的证书
    留神:因为咱们会把 HTTPS server 部署在本地进行测试,所以咱们在签名的时候要额定指定本人的内网 IP。

    echo subjectAltName = IP:192.168.100.22 > extfile.cnf
    
    openssl x509 -req -in mylocal.com.csr \
        -CA rootCA.crt -CAkey rootCA.key \
        -CAcreateserial -out mylocal.com.crt \
        -days 500 -extfile extfile.cnf

执行完后你会失去以下文件:

  • rootCA.key:根 CA 私钥
  • rootCA.crt:根 CA 证书(前面 k8s 须要用到)
  • rootCA.srl:追踪发放的证书
  • mylocal.com.key:自签域名的私钥(HTTPS server 须要用到)
  • mylocal.com.csr:自签域名的证书签名申请文件
  • mylocal.com.crt:自签域名的证书(HTTPS server 须要用到)

3. 构建本人的 HTTPS Server

Webhook 的申请和响应都要是 JSON 格局的 AdmissionReview 对象。

留神:AdmissionReview v1 版本和 v1beta1 版本有区别,咱们这里应用 v1 版本。

// AdmissionReview describes an admission review request/response.
type AdmissionReview struct {
    metav1.TypeMeta `json:",inline"`
    // Request describes the attributes for the admission request.
    // +optional
    Request *AdmissionRequest `json:"request,omitempty" protobuf:"bytes,1,opt,name=request"`
    // Response describes the attributes for the admission response.
    // +optional
    Response *AdmissionResponse `json:"response,omitempty" protobuf:"bytes,2,opt,name=response"`
}

咱们须要解决的逻辑其实就是解析 AdmissionRequest,而后结构 AdmissionResponse 最初返回响应。

// AdmissionResponse describes an admission response.
type AdmissionResponse struct {
    // UID is an identifier for the individual request/response.
    // This must be copied over from the corresponding AdmissionRequest.
    UID types.UID `json:"uid" protobuf:"bytes,1,opt,name=uid"`

    // Allowed indicates whether or not the admission request was permitted.
    Allowed bool `json:"allowed" protobuf:"varint,2,opt,name=allowed"`

    // The patch body. Currently we only support "JSONPatch" which implements RFC 6902.
    // +optional
    Patch []byte `json:"patch,omitempty" protobuf:"bytes,4,opt,name=patch"`

    // The type of Patch. Currently we only allow "JSONPatch".
    // +optional
    PatchType *PatchType `json:"patchType,omitempty" protobuf:"bytes,5,opt,name=patchType"`

    // ...
}

AdmissionResponse 中的 PatchType 字段必须是 JSONPatchPatch 字段必须是 rfc6902 JSON Patch 格局。

咱们应用 go 编写一个最简略的 HTTPS Server 示例如下,该示例会批改 pod 的 spec.containers 数组,向其中追加一个 sidecar 容器:

package main

import (
    "encoding/json"
    "log"
    "net/http"

    v1 "k8s.io/api/admission/v1"
    corev1 "k8s.io/api/core/v1"
)

// patchOperation is an operation of a JSON patch, see https://tools.ietf.org/html/rfc6902 .
type patchOperation struct {
    Op    string      `json:"op"`
    Path  string      `json:"path"`
    Value interface{} `json:"value,omitempty"`}

var (
    certFile = "/Users/wy/certs/mylocal.com.crt"
    keyFile  = "/Users/wy/certs/mylocal.com.key"
)

func main() {http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {defer req.Body.Close()
        var admissionReview v1.AdmissionReview
        err := json.NewDecoder(req.Body).Decode(&admissionReview)
        if err != nil {log.Fatal(err)
        }

        var patches []patchOperation
        patches = append(patches, patchOperation{
            Op:   "add",
            Path: "/spec/containers/-",
            Value: &corev1.Container{
                Image: "busybox",
                Name:  "sidecar",
            },
        })
        patchBytes, err := json.Marshal(patches)
        if err != nil {log.Fatal(err)
        }

        var PatchTypeJSONPatch v1.PatchType = "JSONPatch"
        admissionReview.Response = &v1.AdmissionResponse{
            UID:       admissionReview.Request.UID,
            Allowed:   true,
            Patch:     patchBytes,
            PatchType: &PatchTypeJSONPatch,
        }
        // Return the AdmissionReview with a response as JSON.
        bytes, err := json.Marshal(&admissionReview)
        if err != nil {log.Fatal(err)
        }
        w.Write(bytes)
    })

    log.Printf("About to listen on 8443. Go to https://127.0.0.1:8443/")
    err := http.ListenAndServeTLS(":8443", certFile, keyFile, nil)
    log.Fatal(err)
}

4. 配置 MutatingWebhookConfiguration

咱们须要通知 k8s 往哪里发送申请以及其它信息,这就须要配置 MutatingWebhookConfiguration

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: test-sidecar-injector
webhooks:
  - name: sidecar-injector.mytest.io
    admissionReviewVersions:
      - v1  # 版本肯定要与 HTTPS Server 解决的版本统一
    sideEffects: "NoneOnDryRun"
    reinvocationPolicy: "Never"
    timeoutSeconds: 30
    objectSelector: # 抉择特定资源触发 webhook
      matchExpressions:
        - key: run
          operator: In
          values:
            - "nginx"
    rules:  # 触发规定
      - apiGroups:
          - ""
        apiVersions:
          - v1
        operations:
          - CREATE
        resources:
          - pods
        scope: "*"
    clientConfig:
      caBundle: ${CA_PEM_B64}
      url: https://192.168.100.22:8443/   # 指向我本地的 IP 地址
      # service:  # 如果把 server 部署到集群外部则能够通过 service 援用 

其中的 ${CA_PEM_B64} 须要填入第一步的 rootCA.crt 文件的 base64 编码,咱们能够执行以下命令失去:

openssl base64 -A -in rootCA.crt

在上例中,咱们还配置了 webhook 触发的资源要求和规定,比方这里的规定是创立 pods 并且 pod 的 labels 标签必须满足 matchExpressions

最初测试,咱们能够执行 kubectl run nginx --image=nginx,胜利之后再查看提交的 pod,你会发现 containers 中蕴含有咱们注入的 sidecar。

结语

通过本文置信你曾经理解了 Admission Controller 的根本应用过程,诸多开源框架,比方 Istio 等也宽泛地应用了 Admission Controller。

正文完
 0