image.png

    1. 最近部分NE需要使用到webhook来实现注入sidecar进业务pod中。今天把这个过程梳理一下,刚好OpenKruise中也使用到此对象。
    2. 具体的内容参考:https://medium.com/ibm-cloud/diving-into-kubernetes-mutatingadmissionwebhook-6ef3c5695f74
    • 1.webhook 逻辑: ```properties 具体的webhook的定义我们这里不做赘述,这里可以从kubernetes的官网上找到对应的介绍和一张比较经典的图例。其中涉及到一些概念,我们通常只是去看了有一个大致,但是没有真是的去看它的逻辑。 1.首先webhook是会去拦截到API Server的数据,这里就有一个疑问,为什么我们能够拦截到这个数据,或是说APIServer为什么会等待webhook处理完成以后,才进行下边的处理。其实这里我们需要验证的是: [root@dev1 kubernetes]# kubectl api-versions | grep admissionregistration.k8s.io admissionregistration.k8s.io/v1 admissionregistration.k8s.io/v1beta1 [root@dev1 kubernetes]# 而此结果需要我们在API Server中做配置: [root@dev1 kubernetes]# kubectl -nkube-system get pods kube-apiserver-dev1 -o yaml
      • —enable-admission-plugins=NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook # 不过在较高的kuber版本以后,这个配置默认是打开的。 所以这里配置了以后,我们其实可以看到我们的API Server每次在处理的时候,都会被这个HOOK给“勾一下”。所以这个看起来是一个trigger。但是我们我们把整个的webhook的过程走下来,我们发现,具体的,而是依赖service来处理。找到对应的后端的endpoint list。其实在OpenKruise中也有一段描述,我们看看:

    除了 controller 之外,kruise-controller-manager-xxx 中还包含了针对 Kruise CRD 以及 Pod 资源的 admission webhook。Kruise-manager 会创建一些 webhook configurations 来配置哪些资源需要感知处理、以及提供一个 Service 来给 kube-apiserver 调用。

    $ kubectl get svc -n kruise-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kruise-webhook-service ClusterIP 172.24.9.234 443/TCP 4h9m 上述的 kruise-webhook-service 非常重要,是提供给 kube-apiserver 调用的。 # this line.

    所以我们来看webhook的第一个对象: 1.MutatingWebhookConfiguration: 2.MutatingAdmissionWebhook 本身 3.Webhook Admission Server

    下边我们就具体看看这个配置,弄清楚这个配置以后,我们就知道webhook的处理逻辑了。

    [所有的配置可以参考这里]: https://github.com/BurlyLuo/train/blob/main/Webhook.zip。

    1. - [x] **2.MutatingWebhookConfiguration**
    2. ```properties
    3. MutatingAdmissionWebhook 需要根据 MutatingWebhookConfiguration 向 apiserver 注册。在注册过程中,MutatingAdmissionWebhook 需要说明:
    4. 如何连接 webhook admission server;
    5. 如何验证 webhook admission server;
    6. webhook admission server 的 URL path;
    7. webhook 需要操作对象满足的规则;
    8. webhook admission server 处理时遇到错误时如何处理。
    9. 我们给一个demo:
    10. apiVersion: admissionregistration.k8s.io/v1beta1
    11. kind: MutatingWebhookConfiguration
    12. metadata:
    13. name: sidecar-injector-webhook-cfg
    14. labels:
    15. app: sidecar-injector
    16. webhooks:
    17. - name: sidecar-injector.morven.me
    18. clientConfig:
    19. service:
    20. name: sidecar-injector-webhook-svc
    21. namespace: webhook
    22. path: "/mutate"
    23. caBundle: ${CA_BUNDLE}
    24. rules:
    25. - operations: ["CREATE", "UPDATE"]
    26. apiGroups: [""]
    27. apiVersions: ["v1"]
    28. resources: ["pods"]
    29. namespaceSelector:
    30. matchLabels:
    31. sidecar-injection: enabled
    32. # 然后使用istio 项目的脚本去补全 caBundle: ${CA_BUNDLE} 这里的信息。
    33. caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate>...tLS0K"
    34. https://kubernetes.io/zh/docs/reference/access-authn-authz/extensible-admission-controllers/
    35. 如何连接 webhook admission server; # 这里指的是:配置service 然后解析service找到对应的admission server。
    36. 如何验证 webhook admission server;
    37. webhook admission server 的 URL path;
    38. webhook 需要操作对象满足的规则;
    39. namespaceSelector:
    40. matchLabels:
    41. sidecar-injection: enabled
    42. webhook admission server 处理时遇到错误时如何处理。
    43. rules:
    44. - operations: ["CREATE", "UPDATE"]
    45. apiGroups: [""]
    46. apiVersions: ["v1"]
    47. resources: ["pods"]
    48. #
    49. clientConfig:
    50. #用来校验服务的证书是否正确的CA证书(https的证书域名是admission-webhook.default.svc)
    51. caBundle: #base64的ca证书
    52. service:
    53. namespace: webhook #请求的pod所在的namespace
    54. name: sidecar-injector-webhook-svc #请求的service name
    55. path: /mutate #请求的url
    56. []https://github.com/YaoZengzeng/KubernetesResearch/blob/master/KubernetesAdmissionController%E8%A7%A3%E6%9E%90.md
    57. 其实我们从这个配置中结合上边的描述,我们大致也能知道个大概:
    58. 其中,由于API-Server之支持HTTPS,所以需要配置CA,这个可以参考istio的设置来处理。
    59. 所以这里我们需要创建一个service:
    60. [root@dev1 deploy]# cat service.yaml
    61. apiVersion: v1
    62. kind: Service
    63. metadata:
    64. name: sidecar-injector-webhook-svc
    65. namespace: webhook
    66. labels:
    67. app: sidecar-injector
    68. spec:
    69. ports:
    70. - port: 443
    71. targetPort: 8443
    72. selector:
    73. app: sidecar-injector
    74. [root@dev1 deploy]#
    75. 而这个service对应的backend为:
    76. [root@dev1 ~]# kubectl -nwebhook describe svc sidecar-injector-webhook-svc
    77. Name: sidecar-injector-webhook-svc
    78. Namespace: webhook
    79. Labels: app=sidecar-injector
    80. Annotations: <none>
    81. Selector: app=sidecar-injector
    82. Type: ClusterIP
    83. IP Families: <none>
    84. IP: 10.107.143.88
    85. IPs: 10.107.143.88
    86. Port: <unset> 443/TCP
    87. TargetPort: 8443/TCP
    88. Endpoints: 10.0.0.181:8443 # 这里便是我们webhook server的地址。
    89. Session Affinity: None
    90. Events: <none>
    91. [root@dev1 ~]#
    92. 也就是说我们还需要webhook server:
    93. [root@dev1 ~]# kubectl -nwebhook get pods -o wide
    94. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
    95. sidecar-injector-webhook-deployment-86b5b4fb49-vkpj2 1/1 Running 0 47h 10.0.0.181 dev2 <none> <none>
    96. [root@dev1 ~]#
    97. 这个时候就知道发给谁了,也就是webhook server的地址了。
    98. 与其对应的还有一个path,可以理解为访问对应的某一个服务,一个路径。
    99. 此时逻辑貌似就不缺少什么了。关键是webhook中的mutating,在做什么,这里我们也需要弄清楚:
    100. 在webhook server中:
    101. [root@dev1 deploy]# cat deployment.yaml
    102. apiVersion: apps/v1
    103. kind: Deployment
    104. metadata:
    105. name: sidecar-injector-webhook-deployment
    106. namespace: webhook
    107. labels:
    108. app: sidecar-injector
    109. spec:
    110. replicas: 1
    111. selector:
    112. matchLabels:
    113. app: sidecar-injector
    114. template:
    115. metadata:
    116. labels:
    117. app: sidecar-injector
    118. spec:
    119. containers:
    120. - name: sidecar-injector
    121. image: morvencao/sidecar-injector:latest
    122. imagePullPolicy: IfNotPresent
    123. args:
    124. - -sidecarCfgFile=/etc/webhook/config/sidecarconfig.yaml # 注入的内容。
    125. - -tlsCertFile=/etc/webhook/certs/cert.pem
    126. - -tlsKeyFile=/etc/webhook/certs/key.pem
    127. - -alsologtostderr
    128. - -v=4
    129. - 2>&1
    130. volumeMounts:
    131. - name: webhook-certs
    132. mountPath: /etc/webhook/certs
    133. readOnly: true
    134. - name: webhook-config
    135. mountPath: /etc/webhook/config
    136. volumes:
    137. - name: webhook-certs
    138. secret:
    139. secretName: sidecar-injector-webhook-certs
    140. - name: webhook-config
    141. configMap:
    142. name: sidecar-injector-webhook-configmap # 挂载configmap
    143. [root@dev1 deploy]# kubectl -nwebhook get cm
    144. [root@dev1 deploy]# cat configmap.yaml
    145. apiVersion: v1
    146. kind: ConfigMap
    147. metadata:
    148. name: sidecar-injector-webhook-configmap
    149. namespace: webhook
    150. data:
    151. sidecarconfig.yaml: |
    152. containers:
    153. - name: sidecar-nginx
    154. image: nginx:1.12.2
    155. imagePullPolicy: IfNotPresent
    156. ports:
    157. - containerPort: 80
    158. volumeMounts:
    159. - name: nginx-conf
    160. mountPath: /etc/nginx
    161. volumes:
    162. - name: nginx-conf
    163. configMap:
    164. name: nginx-configmap
    165. [root@dev1 deploy]#
    166. 我们需要在创建的时候:
    167. 规则:
    168. rules:
    169. - operations: ["CREATE", "UPDATE"] # 操作
    170. apiGroups: [""]
    171. apiVersions: ["v1"]
    172. resources: ["pods"] # 资源类型
    173. namespaceSelector:
    174. matchLabels:
    175. sidecar-injection: enabled # ns级别
    176. [创建apline,这里会注入我们配置的configmap信息的nginx:]
    177. kubectl run alpine --image=alpine --restart=Never -n webhook --overrides='{"apiVersion":"v1","metadata":{"annotations":{"sidecar-injector-webhook.morven.me/inject":"yes"}}}' --command -- sleep infinity
    178. 但是这里会加入:注解来实现。这点很重要。
    • 3.MutatingAdmissionWebhook 本身 ```properties MutatingAdmissionWebhook 是一种插件形式的 admission controller ,且可以配置到 apiserver 中。MutatingAdmissionWebhook 插件可以从 MutatingWebhookConfiguration 中获取所有感兴趣的 admission webhooks。

    然后 MutatingAdmissionWebhook 监听 apiserver 的请求,拦截满足条件的请求,并并行执行。

    1. - [x] **4.Webhook Admission Server**
    2. ```properties
    3. Webhook Admission Server 只是一个附着到 k8s apiserver 的 http server。对于每一个 apiserver 的请求,MutatingAdmissionWebhook 都会发送一个 admissionReview 到相关的 webhook admission server。webhook admission server 再决定如何更改资源。
    • 5.webhook server log ```properties [root@dev1 ~]# kubectl -nwebhook logs -f sidecar-injector-webhook-deployment-86b5b4fb49-vkpj2 I1227 13:47:06.875470 1 webhook.go:88] New configuration: sha256sum 25d2a45d9d8301d08c7b99b6c6570e5626beeb39513488833daebc51453a836b I1227 13:48:47.590584 1 webhook.go:220] AdmissionReview for Kind=/v1, Kind=Pod, Namespace=webhook Name=alpine (alpine) UID=89f529e6-6abd-4319-b4c7-afd8b0a5941a patchOperation=CREATE UserInfo={kubernetes-admin [system:masters system:authenticated] map[]} I1227 13:48:47.590624 1 webhook.go:128] Mutation policy for /alpine: status: “” required:true I1227 13:48:47.590664 1 webhook.go:243] AdmissionResponse: patch=[{“op”:”add”,”path”:”/spec/containers/-“,”value”:{“name”:”sidecar-nginx”,”image”:”nginx:1.12.2”,”ports”:[{“containerPort”:80,”protocol”:”TCP”}],”resources”:{},”volumeMounts”:[{“name”:”nginx-conf”,”mountPath”:”/etc/nginx”}],”terminationMessagePath”:”/dev/termination-log”,”terminationMessagePolicy”:”File”,”imagePullPolicy”:”IfNotPresent”}},{“op”:”add”,”path”:”/spec/volumes/-“,”value”:{“name”:”nginx-conf”,”configMap”:{“name”:”nginx-configmap”,”defaultMode”:420}}},{“op”:”add”,”path”:”/metadata/annotations”,”value”:{“sidecar-injector-webhook.morven.me/status”:”injected”}}] I1227 13:48:47.590721 1 webhook.go:302] Ready to write reponse …

    ^C ```