《[译]深刻剖析 Kubernetes MutatingAdmissionWebhook》最先发布在blog.hdls.me/15564491070…html
翻译自 《Diving into Kubernetes MutatingAdmissionWebhook》 原文连接:medium.com/ibm-cloud/d…node
对于在数据持久化以前,拦截到 Kubernetes API server 的请求,Admission controllers
是很是有用的工具。然而,因为其须要由集群管理员在 kube-apiserver 中编译成二进制文件,因此使用起来不是很灵活。从 Kubernetes 1.7 起,引入了 Initializers
和 External Admission Webhooks
,用以解决这个问题。在 Kubernetes 1.9 中,Initializers
依然停留在 alpha 版本,然而 External Admission Webhooks
被提高到了 beta 版,且被分红了 MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
。linux
MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
两者合起来就是一个特殊类型的 admission controllers
,一个处理资源更改,一个处理验证。验证是经过匹配到 MutatingWebhookConfiguration
中定义的规则完成的。nginx
在这篇文章中,我会深刻剖析 MutatingAdmissionWebhook
的细节,并一步步实现一个可用的 webhook admission server
。git
Kubernetes 集群管理员可使用 webhooks
来建立额外的资源更改及验证准入插件,这些准入插件能够经过 apiserver 的准入链来工做,而不须要从新编译 apiserver。这使得开发者能够对于不少动做均可以自定义准入逻辑,好比对任何资源的建立、更新、删除,给开发者提供了很大的自由和灵活度。可使用的应用数量巨大。一些常见的使用常见包括:github
StorageClass
。监听 PersistentVolumeClaim
资源,并按照事先定好的规则自动的为之增添对应的 StorageClass
。使用者无需关心 StorageClass
的建立。除了以上列出来的使用场景,基于 webhooks
还能够建立更多应用。web
基于社区的反馈,以及对 External Admission Webhooks
和 Initializers
的 alpha 版本的使用案例,Kubernetes 社区决定将 webhooks 升级到 beta 版,而且将其分红两种 webhooks(MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
)。这些更新使得 webhooks 与其余 admission controllers
保持一致,而且强制 mutate-before-validate
。Initializers
能够在 Kubernetes 资源建立前更改,从而实现动态准入控制。若是你对 Initializers
不熟悉,能够参考这篇文章。docker
因此,到底 Webhooks
和 Initializers
之间的区别是什么呢?shell
Webhooks
能够应用于更多操做,包括对于资源 "增删改" 的 "mutate" 和 "admit";然而 Initializers
不能够对 "删" 资源进行 "admit"。Webhooks
在建立资源前不容许查询;然而 Initializers
能够监听未初始化的资源,经过参数 ?includeUninitialized=true
来实现。Initializers
会把 "预建立" 状态也持久化到 etcd,所以会引入高延迟且给 etcd 带来负担,尤为在 apiserver 升级或失败时;然而 Webhooks 消耗的内存和计算资源更少。Webhooks
比 Initializers
对失败的保障更强大。Webhooks
的配置中能够配置失败策略,用以免资源在建立的时候被 hang 住。然而 Initializers
在尝试建立资源的时候可能会 block 住全部的资源。除了上面列举的不一样点,Initializer
在较长一段开发时间内还存在不少已知问题,包括配额补充错误等。Webhooks
升级为 beta 版也就预示着在将来 Webhooks
会是开发目标。若是你须要更稳定的操做,我推荐使用 Webhooks
。json
MutatingAdmissionWebhook
在资源被持久化到 etcd 前,根据规则将其请求拦截,拦截规则定义在 MutatingWebhookConfiguration
中。MutatingAdmissionWebhook
经过对 webhook server
发送准入请求来实现对资源的更改。而 webhook server
只是一个简单的 http 服务器。
下面这幅图详细描述了 MutatingAdmissionWebhook
如何工做:
MutatingAdmissionWebhook
须要三个对象才能运行:
MutatingAdmissionWebhook
须要根据 MutatingWebhookConfiguration
向 apiserver 注册。在注册过程当中,MutatingAdmissionWebhook
须要说明:
webhook admission server
;webhook admission server
;webhook admission server
的 URL path;webhook admission server
处理时遇到错误时如何处理。MutatingAdmissionWebhook
是一种插件形式的 admission controller
,且能够配置到 apiserver 中。MutatingAdmissionWebhook
插件能够从 MutatingWebhookConfiguration
中获取全部感兴趣的 admission webhooks
。
而后 MutatingAdmissionWebhook
监听 apiserver 的请求,拦截知足条件的请求,并并行执行。
Webhook Admission Server
只是一个附着到 k8s apiserver 的 http server。对于每个 apiserver 的请求,MutatingAdmissionWebhook
都会发送一个 admissionReview
到相关的 webhook admission server
。webhook admission server
再决定如何更改资源。
编写一个完整的 Webhook Admission Server
可能使人生畏。为了方便起见,咱们编写一个简单的 Webhook Admission Server
来实现注入 nginx sidecar 容器以及挂载 volume。完整代码在 kube-mutating-webhook-tutorial。这个项目参考了 Kubernetes webhook 示例和 Istio sidecar 注入实现。
在接下来的段落里,我会向你展现如何编写可工做的容器化 webhook admission server
,并将其部署到 Kubernetes 集群中。
MutatingAdmissionWebhook
要求 Kubernetes 版本为 1.9.0 及以上,其 admissionregistration.k8s.io/v1beta1
API 可用。确保下面的命令:
kubectl api-versions | grep admissionregistration.k8s.io/v1beta1
复制代码
其输出为:
admissionregistration.k8s.io/v1beta1
复制代码
另外,MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
准入控制器须要以正确的顺序加入到 kube-apiserver
的 admission-control
标签中。
Webhook Admission Server
是一个简单的 http 服务器,遵循 Kubernetes API。我粘贴部分伪代码来描述主逻辑:
sidecarConfig, err := loadConfig(parameters.sidecarCfgFile)
pair, err := tls.LoadX509KeyPair(parameters.certFile, parameters.keyFile)
whsvr := &WebhookServer {
sidecarConfig: sidecarConfig,
server: &http.Server {
Addr: fmt.Sprintf(":%v", 443),
TLSConfig: &tls.Config{Certificates: []tls.Certificate{pair}},
},
}
// define http server and server handler
mux := http.NewServeMux()
mux.HandleFunc("/mutate", whsvr.serve)
whsvr.server.Handler = mux
// start webhook server in new rountine
go func() {
if err := whsvr.server.ListenAndServeTLS("", ""); err != nil {
glog.Errorf("Filed to listen and serve webhook server: %v", err)
}
}()
复制代码
以上代码的详解:
sidecarCfgFile
包含了 sidecar 注入器模板,其在下面的 ConfigMap 中定义;certFile
和 keyFile
是秘钥对,会在 webhook server
和 apiserver 之间的 TLS 通讯中用到;'/mutate'
。接下来咱们关注处理函数 serve
的主要逻辑:
// Serve method for webhook server
func (whsvr *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
var body []byte
if r.Body != nil {
if data, err := ioutil.ReadAll(r.Body); err == nil {
body = data
}
}
var reviewResponse *v1beta1.AdmissionResponse
ar := v1beta1.AdmissionReview{}
deserializer := codecs.UniversalDeserializer()
if _, _, err := deserializer.Decode(body, nil, &ar); err != nil {
glog.Error(err)
reviewResponse = toAdmissionResponse(err)
} else {
reviewResponse = mutate(ar)
}
response := v1beta1.AdmissionReview{}
if reviewResponse != nil {
response.Response = reviewResponse
response.Response.UID = ar.Request.UID
}
// reset the Object and OldObject, they are not needed in a response.
ar.Request.Object = runtime.RawExtension{}
ar.Request.OldObject = runtime.RawExtension{}
resp, err := json.Marshal(response)
if err != nil {
glog.Error(err)
}
if _, err := w.Write(resp); err != nil {
glog.Error(err)
}
}
复制代码
函数 serve
是一个简单的 http 处理器,参数为 http request
和 response writer
。
AdmissionReview
,其中包括 object
、oldobject
及 userInfo
...mutate
来建立 patch
,以实现注入 sidecar 容器及挂载 volume。admission decision
和额外 patch
组装成响应,并发送回给 apiserver。对于函数 mutate
的实现,你能够随意发挥。我就以个人实现方式作个例子:
// main mutation process
func (whsvr *WebhookServer) mutate(ar *v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
req := ar.Request
var pod corev1.Pod
if err := json.Unmarshal(req.Object.Raw, &pod); err != nil {
glog.Errorf("Could not unmarshal raw object: %v", err)
return &v1beta1.AdmissionResponse {
Result: &metav1.Status {
Message: err.Error(),
},
}
}
// determine whether to perform mutation
if !mutationRequired(ignoredNamespaces, &pod.ObjectMeta) {
glog.Infof("Skipping mutation for %s/%s due to policy check", pod.Namespace, pod.Name)
return &v1beta1.AdmissionResponse {
Allowed: true,
}
}
annotations := map[string]string{admissionWebhookAnnotationStatusKey: "injected"}
patchBytes, err := createPatch(&pod, whsvr.sidecarConfig, annotations)
return &v1beta1.AdmissionResponse {
Allowed: true,
Patch: patchBytes,
PatchType: func() *v1beta1.PatchType {
pt := v1beta1.PatchTypeJSONPatch
return &pt
}(),
}
}
复制代码
从上述代码中能够看出,函数 mutate
请求了 mutationRequired
来决定这个改动是否被容许。对于被容许的请求,函数 mutate
从另外一个函数 createPatch
中获取到修改体 'patch'
。注意这里函数 mutationRequired
的诡计,咱们跳过了带有注解 sidecar-injector-webhook.morven.me/inject: true
。这里稍后会在部署 deployment
的时候提到。完整代码请参考这里。
建立构建脚本:
dep ensure
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o kube-mutating-webhook-tutorial .
docker build --no-cache -t morvencao/sidecar-injector:v1 .
rm -rf kube-mutating-webhook-tutorial
docker push morvencao/sidecar-injector:v1
复制代码
如下面为依赖编写 Dockerfile 文件:
FROM alpine:latest
ADD kube-mutating-webhook-tutorial /kube-mutating-webhook-tutorial ENTRYPOINT ["./kube-mutating-webhook-tutorial"] 复制代码
在手动构建容器前,你须要 Docker ID 帐号并将 image name 和 tag (Dockerfile 和 deployment.yaml 文件中)修改为你本身的,而后执行如下命令:
[root@mstnode kube-mutating-webhook-tutorial]# ./build
Sending build context to Docker daemon 44.89MB
Step 1/3 : FROM alpine:latest
---> 3fd9065eaf02
Step 2/3 : ADD kube-mutating-webhook-tutorial /kube-mutating-webhook-tutorial
---> 432de60c2b3f
Step 3/3 : ENTRYPOINT ["./kube-mutating-webhook-tutorial"]
---> Running in da6e956d1755
Removing intermediate container da6e956d1755
---> 619faa936145
Successfully built 619faa936145
Successfully tagged morvencao/sidecar-injector:v1
The push refers to repository [docker.io/morvencao/sidecar-injector]
efd05fe119bb: Pushed
cd7100a72410: Layer already exists
v1: digest: sha256:7a4889928ec5a8bcfb91b610dab812e5228d8dfbd2b540cd7a341c11f24729bf size: 739
复制代码
如今咱们来建立一个 Kubernetes ConfigMap
,包含须要注入到目标 pod 中的容器和 volume
信息:
apiVersion: v1
kind: ConfigMap
metadata:
name: sidecar-injector-webhook-configmap
data:
sidecarconfig.yaml: | containers:
- name: sidecar-nginx
image: nginx:1.12.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx
volumes:
- name: nginx-conf
configMap:
name: nginx-configmap
复制代码
从上面的清单中看,这里须要另外一个包含 nginx conf
的 ConfigMap
。完整 yaml 参考 nginxconfigmap.yaml。
而后将这两个 ConfigMap 部署到集群中:
[root@mstnode kube-mutating-webhook-tutorial]# kubectl create -f ./deployment/nginxconfigmap.yaml
configmap "nginx-configmap" created
[root@mstnode kube-mutating-webhook-tutorial]# kubectl create -f ./deployment/configmap.yaml
configmap "sidecar-injector-webhook-configmap" created
复制代码
因为准入控制是一个高安全性操做,因此对外在的 webhook server
提供 TLS 是必须的。做为流程的一部分,咱们须要建立由 Kubernetes CA 签名的 TLS 证书,以确保 webhook server
和 apiserver 之间通讯的安全性。对于 CSR 建立和批准的完整步骤,请参考 这里 。
简单起见,咱们参考了 Istio 的脚本并建立了一个相似的名为 webhook-create-signed-cert.sh
的脚本,来自动生成证书及秘钥对并将其加入到 secret
中。
#!/bin/bash
while [[ $# -gt 0 ]]; do
case ${1} in
--service)
service="$2"
shift
;;
--secret)
secret="$2"
shift
;;
--namespace)
namespace="$2"
shift
;;
esac
shift
done
[ -z ${service} ] && service=sidecar-injector-webhook-svc
[ -z ${secret} ] && secret=sidecar-injector-webhook-certs
[ -z ${namespace} ] && namespace=default
csrName=${service}.${namespace}
tmpdir=$(mktemp -d)
echo "creating certs in tmpdir ${tmpdir} "
cat <<EOF >> ${tmpdir}/csr.conf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${service}
DNS.2 = ${service}.${namespace}
DNS.3 = ${service}.${namespace}.svc
EOF
openssl genrsa -out ${tmpdir}/server-key.pem 2048
openssl req -new -key ${tmpdir}/server-key.pem -subj "/CN=${service}.${namespace}.svc" -out ${tmpdir}/server.csr -config ${tmpdir}/csr.conf
# clean-up any previously created CSR for our service. Ignore errors if not present.
kubectl delete csr ${csrName} 2>/dev/null || true
# create server cert/key CSR and send to k8s API
cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: ${csrName}
spec:
groups:
- system:authenticated
request: $(cat ${tmpdir}/server.csr | base64 | tr -d '\n')
usages:
- digital signature
- key encipherment
- server auth
EOF
# verify CSR has been created
while true; do
kubectl get csr ${csrName}
if [ "$?" -eq 0 ]; then
break
fi
done
# approve and fetch the signed certificate
kubectl certificate approve ${csrName}
# verify certificate has been signed
for x in $(seq 10); do
serverCert=$(kubectl get csr ${csrName} -o jsonpath='{.status.certificate}')
if [[ ${serverCert} != '' ]]; then
break
fi
sleep 1
done
if [[ ${serverCert} == '' ]]; then
echo "ERROR: After approving csr ${csrName}, the signed certificate did not appear on the resource. Giving up after 10 attempts." >&2
exit 1
fi
echo ${serverCert} | openssl base64 -d -A -out ${tmpdir}/server-cert.pem
# create the secret with CA cert and server cert/key
kubectl create secret generic ${secret} \
--from-file=key.pem=${tmpdir}/server-key.pem \
--from-file=cert.pem=${tmpdir}/server-cert.pem \
--dry-run -o yaml |
kubectl -n ${namespace} apply -f -
复制代码
运行脚本后,包含证书和秘钥对的 secret
就被建立出来了:
[root@mstnode kube-mutating-webhook-tutorial]# ./deployment/webhook-create-signed-cert.sh
creating certs in tmpdir /tmp/tmp.wXZywp0wAF
Generating RSA private key, 2048 bit long modulus
...........................................+++
..........+++
e is 65537 (0x10001)
certificatesigningrequest "sidecar-injector-webhook-svc.default" created
NAME AGE REQUESTOR CONDITION
sidecar-injector-webhook-svc.default 0s https://mycluster.icp:9443/oidc/endpoint/OP#admin Pending
certificatesigningrequest "sidecar-injector-webhook-svc.default" approved
secret "sidecar-injector-webhook-certs" created
复制代码
deployment 带有一个 pod,其中运行的就是 sidecar-injector
容器。该容器以特殊参数运行:
sidecarCfgFile
指的是 sidecar 注入器的配置文件,挂载自上面建立的 ConfigMap sidecar-injector-webhook-configmap
。tlsCertFile
和 tlsKeyFile
是秘钥对,挂载自 Secret injector-webhook-certs
。alsologtostderr
、v=4
和 2>&1
是日志参数。apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: sidecar-injector-webhook-deployment
labels:
app: sidecar-injector
spec:
replicas: 1
template:
metadata:
labels:
app: sidecar-injector
spec:
containers:
- name: sidecar-injector
image: morvencao/sidecar-injector:v1
imagePullPolicy: IfNotPresent
args:
- -sidecarCfgFile=/etc/webhook/config/sidecarconfig.yaml
- -tlsCertFile=/etc/webhook/certs/cert.pem
- -tlsKeyFile=/etc/webhook/certs/key.pem
- -alsologtostderr
- -v=4
- 2>&1
volumeMounts:
- name: webhook-certs
mountPath: /etc/webhook/certs
readOnly: true
- name: webhook-config
mountPath: /etc/webhook/config
volumes:
- name: webhook-certs
secret:
secretName: sidecar-injector-webhook-certs
- name: webhook-config
configMap:
name: sidecar-injector-webhook-configmap
复制代码
Service 暴露带有 app=sidecar-injector
label 的 pod,使之在集群中可访问。这个 Service 会被 MutatingWebhookConfiguration
中定义的 clientConfig
部分访问,默认的端口 spec.ports.port
须要设置为 443。
apiVersion: v1
kind: Service
metadata:
name: sidecar-injector-webhook-svc
labels:
app: sidecar-injector
spec:
ports:
- port: 443
targetPort: 443
selector:
app: sidecar-injector
复制代码
而后将上述 Deployment 和 Service 部署到集群中,而且验证 sidecar 注入器的 webhook server
是否 running:
[root@mstnode kube-mutating-webhook-tutorial]# kubectl create -f ./deployment/deployment.yaml
deployment "sidecar-injector-webhook-deployment" created
[root@mstnode kube-mutating-webhook-tutorial]# kubectl create -f ./deployment/service.yaml
service "sidecar-injector-webhook-svc" created
[root@mstnode kube-mutating-webhook-tutorial]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
sidecar-injector-webhook-deployment 1 1 1 1 2m
[root@mstnode kube-mutating-webhook-tutorial]# kubectl get pod
NAME READY STATUS RESTARTS AGE
sidecar-injector-webhook-deployment-bbb689d69-fdbgj 1/1 Running 0 3m
复制代码
MutatingWebhookConfiguration
中具体说明了哪一个 webhook admission server
是被使用的而且哪些资源受准入服务器的控制。建议你在建立 MutatingWebhookConfiguration
以前先部署 webhook admission server
,并确保其正常工做。不然,请求会被无条件接收或根据失败规则被拒。
如今,咱们根据下面的内容建立 MutatingWebhookConfiguration
:
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
name: sidecar-injector-webhook-cfg
labels:
app: sidecar-injector
webhooks:
- name: sidecar-injector.morven.me
clientConfig:
service:
name: sidecar-injector-webhook-svc
namespace: default
path: "/mutate"
caBundle: ${CA_BUNDLE}
rules:
- operations: [ "CREATE" ]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
namespaceSelector:
matchLabels:
sidecar-injector: enabled
复制代码
第 8 行:name
- webhook 的名字,必须指定。多个 webhook 会以提供的顺序排序; 第 9 行:clientConfig
- 描述了如何链接到 webhook admission server
以及 TLS 证书; 第 15 行:rules
- 描述了 webhook server
处理的资源和操做。在咱们的例子中,只拦截建立 pods 的请求; 第 20 行:namespaceSelector
- namespaceSelector
根据资源对象是否匹配 selector
决定了是否针对该资源向 webhook server
发送准入请求。
在部署 MutatingWebhookConfiguration
前,咱们须要将 ${CA_BUNDLE}
替换成 apiserver 的默认 caBundle
。咱们写个脚原本自动匹配:
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
ROOT=$(cd $(dirname $0)/../../; pwd)
export CA_BUNDLE=$(kubectl get configmap -n kube-system extension-apiserver-authentication -o=jsonpath='{.data.client-ca-file}' | base64 | tr -d '\n')
if command -v envsubst >/dev/null 2>&1; then
envsubst
else
sed -e "s|\${CA_BUNDLE}|${CA_BUNDLE}|g"
fi
复制代码
而后执行:
[root@mstnode kube-mutating-webhook-tutorial]# cat ./deployment/mutatingwebhook.yaml |\
> ./deployment/webhook-patch-ca-bundle.sh >\
> ./deployment/mutatingwebhook-ca-bundle.yaml
复制代码
咱们看不到任何日志描述 webhook server
接收到准入请求,彷佛该请求并无发送到 webhook server
同样。因此有一种可能性是这是被 MutatingWebhookConfiguration
中的配置触发的。再确认一下 MutatingWebhookConfiguration
咱们会发现下面的内容:
namespaceSelector:
matchLabels:
sidecar-injector: enabled
复制代码
咱们在 MutatingWebhookConfiguration
中配置了 namespaceSelector
,也就意味着只有在知足条件的 namespace 下的资源可以被发送到 webhook server
。因而咱们将 default 这个 namespace 打上标签 sidecar-injector=enabled
:
[root@mstnode kube-mutating-webhook-tutorial]# kubectl label namespace default sidecar-injector=enabled
namespace "default" labeled
[root@mstnode kube-mutating-webhook-tutorial]# kubectl get namespace -L sidecar-injector
NAME STATUS AGE sidecar-injector
default Active 1d enabled
kube-public Active 1d
kube-system Active 1d
复制代码
如今咱们配置的 MutatingWebhookConfiguration
会在 pod 建立的时候就注入 sidecar 容器。将运行中的 pod 删除并确认是否建立了新的带 sidecar 容器的 pod:
[root@mstnode kube-mutating-webhook-tutorial]# kubectl delete pod sleep-6d79d8dc54-r66vz
pod "sleep-6d79d8dc54-r66vz" deleted
[root@mstnode kube-mutating-webhook-tutorial]# kubectl get pods
NAME READY STATUS RESTARTS AGE
sidecar-injector-webhook-deployment-bbb689d69-fdbgj 1/1 Running 0 29m
sleep-6d79d8dc54-b8ztx 0/2 ContainerCreating 0 3s
sleep-6d79d8dc54-r66vz 1/1 Terminating 0 11m
[root@mstnode kube-mutating-webhook-tutorial]# kubectl get pod sleep-6d79d8dc54-b8ztx -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubernetes.io/psp: default
sidecar-injector-webhook.morven.me/inject: "true"
sidecar-injector-webhook.morven.me/status: injected
labels:
app: sleep
pod-template-hash: "2835848710"
name: sleep-6d79d8dc54-b8ztx
namespace: default
spec:
containers:
- command:
- /bin/sleep
- infinity
image: tutum/curl
imagePullPolicy: IfNotPresent
name: sleep
resources: {}
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-d7t2r
readOnly: true
- image: nginx:1.12.2
imagePullPolicy: IfNotPresent
name: sidecar-nginx
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/nginx
name: nginx-conf
volumes:
- name: default-token-d7t2r
secret:
defaultMode: 420
secretName: default-token-d7t2r
- configMap:
defaultMode: 420
name: nginx-configmap
name: nginx-conf
...
复制代码
能够看到,sidecar 容器和 volume 被成功注入到应用中。至此,咱们成功建立了可运行的带 MutatingAdmissionWebhook
的 sidecar 注入器。经过 namespaceSelector
,咱们能够轻易的控制在特定的 namespace 中的 pods 是否须要被注入 sidecar 容器。
但这里有个问题,根据以上的配置,在 default 这个 namespace 下的全部 pods 都会被注入 sidecar 容器,无一例外。
多亏了 MutatingAdmissionWebhook
的灵活性,咱们能够轻易的自定义变动逻辑来筛选带有特定注解的资源。还记得上面提到的注解 sidecar-injector-webhook.morven.me/inject: "true"
吗?在 sidecar 注入器中这能够当成另外一种控制方式。在 webhook server
中我写了一段逻辑来跳过那行不带这个注解的 pod。
咱们来尝试一下。在这种状况下,咱们建立另外一个 sleep 应用,其 podTemplateSpec
中不带注解 sidecar-injector-webhook.morven.me/inject: "true"
:
[root@mstnode kube-mutating-webhook-tutorial]# kubectl delete deployment sleep
deployment "sleep" deleted
[root@mstnode kube-mutating-webhook-tutorial]# cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
> kind: Deployment
> metadata:
> name: sleep
> spec:
> replicas: 1
> template:
> metadata:
> labels:
> app: sleep
> spec:
> containers:
> - name: sleep
> image: tutum/curl
> command: ["/bin/sleep","infinity"]
> imagePullPolicy: IfNotPresent
> EOF
deployment "sleep" created
复制代码
而后确认 sidecar 注入器是否跳过了这个 pod:
[root@mstnode kube-mutating-webhook-tutorial]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
sidecar-injector-webhook-deployment 1 1 1 1 45m
sleep 1 1 1 1 17s
[root@mstnode kube-mutating-webhook-tutorial]# kubectl get pod
NAME READY STATUS RESTARTS AGE
sidecar-injector-webhook-deployment-bbb689d69-fdbgj 1/1 Running 0 45m
sleep-776b7bcdcd-4bz58 1/1 Running 0 21s
复制代码
结果显示,这个 sleep 应用只包含一个容器,没有额外的容器和 volume 注入。而后咱们将这个 deployment 增长注解,并确认其重建后是否被注入 sidecar:
[root@mstnode kube-mutating-webhook-tutorial]# kubectl patch deployment sleep -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar-injector-webhook.morven.me/inject": "true"}}}}}'
deployment "sleep" patched
[root@mstnode kube-mutating-webhook-tutorial]# kubectl delete pod sleep-776b7bcdcd-4bz58
pod "sleep-776b7bcdcd-4bz58" deleted
[root@mstnode kube-mutating-webhook-tutorial]# kubectl get pods
NAME READY STATUS RESTARTS AGE
sidecar-injector-webhook-deployment-bbb689d69-fdbgj 1/1 Running 0 49m
sleep-3e42ff9e6c-6f87b 0/2 ContainerCreating 0 18s
sleep-776b7bcdcd-4bz58 1/1 Terminating 0 3m
复制代码
与预期一致,pod 被注入了额外的 sidecar 容器。至此,咱们就得到了一个可工做的 sidecar 注入器,可由 namespaceSelector
或更细粒度的由注解控制。
MutatingAdmissionWebhook
是 Kubernetes 扩展功能中最简单的方法之一,工做方式是经过全新规则控制、资源更改。
此功能使得更多的工做模式变成了可能,而且支持了更多生态系统,包括服务网格平台 Istio。从 Istio 0.5.0 开始,Istio 的自动注入部分的代码被重构,实现方式从 initializers
变动为 MutatingAdmissionWebhook
。