Kubernetes / Linux Note / 运维笔记

Kubernetes Admission Webhook Part 2

Einic Yeo · 2月18日 · 2022年 · · · · · ·

在上一篇文章中,我们主要讨论了使用operator-sdk工具编写Kubernetes Admission Webhook,并针对我们的自定义资源类型创建了我们的第一个Mutating Admission Webhook。但是,这次我们将为DeploymentPod等核心类型创建另一种类型的Admission Webhook,称为“验证”,而不是自定义资源类型,因为我们可版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!能并不总是有某种自定义资源类型。

一、Kubebuilder

此外,我们将使用“kubebuilder”来搭建项目模板。为什么?因为改变总是好的。我们在Trendyol的平台团队开发webhook时正版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!在使用这种方法

Kubebuilder是一个使用自定义资源定义 (CRD)构建Kubernetes API的框架。

Ruby on RailsSpringBoot等 Web 开发框架类似,Kubebuilder 提高了速度并降低了开发人员管理的复杂性,以便在 Go 中快速构建和发布 Kubernetes API。它建立在用于构建核心 Kubernetes API 的规范技术之上,以提供简单的抽象来减少样板文件和繁琐的工作。

正如我之前提到的,我们熟悉 Kubernetes Operator 模式中的这些工具,但这些工具也有助于创建 Kubernetes Admission Webhooks

确实有很好的文档可以用来了解kubebuilder,但是在这篇文章中,我们将重点关注“Webhook for Core Types”一书的特殊部分。前面我们也提到过,kubebuilderoperator-sdk不支持核心类型的webhook脚手架,我们必须使用controler-runtime中的库来处理它。在控制器运行时有一个例子

现在,让我们进入Demo部分,这一次使用kubebuilder工具来弄脏我们的手。

二、版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!Demo 实践

在这个Demo中,我们将根据我们标记为必需的标签来验证Pod。如果Pod上存在必要的注解,那么我们允许创建Pod,如果没有,我们不允许。

先决条件

  • Minikube v1.18.1
  • Kubebuilder v2.3.2
  • kubectl v1.20.5
  • go v1.16.3

我将在macOS环境中进行此Demo,因此,您可以使用macOS的包管理器“brhttps://brew.sh/ew”来安装上述所有工具。

三、验证 Pod 的 AdmissionWebhook

由于我们使用的是 operator-sdk,因此我们可能熟悉 kubebuilder 命令。让我们为项目创建目录

$ mkdir -p pod-validatingwebhook 
$ cd pod-validatingwebhook

在使用 kubebuilder 初始化项目模板之前,我们应该首先创建 go模块:

$ go mod init pod-validatingwebhook
go: creating new go.mod: module pod-validationwebhook

要初始化项目模板,我们应该运行以下命令:

$ kubebuilder init --domain developer.guy --license none --owner "developer-guy"
Writing scaffold for you to edit...
Get controller runtime:
$ go get sigs.k8s.io/[email protected]
Update go.mod:
$ go mod tidy
Running make:
$ make
go: creating new go.mod: module tmp
go get: added sigs.k8s.io/controller-tools v0.2.5
/Users/batuhan.apaydin/go/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go build -o bin/manager main.go
Next: define a resource with:
$ kubebuilder create api
# Look at the directory structure, you should see similar to the following
$ tree -L 2 .
.
├── Dockerfile
├── Makefile
├── PROJECT
├── bin
│   └── manager
├── config
│   ├── certmanager
│   ├── default
│   ├── manager
│   ├── prometheus
│   ├── rbac
│   └── webhook
├── go.mod
├── go.sum
├── hack
│   └── boilerplate.go.txt
└── main.go

现在,我们已经准备好为&nbs版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!p;Pod 创建 ValidatingAdmissionWebhook,但让我们先创建一个 API

$ kubebuilder create api — group core — version v1 — kind Pod — resource=false — controller=false ← be careful here we specified resource and controller creation as false here because we don’t need them.
Writing scaffold for you to edit…
Running make:
$ make
go: creating new go.mod: module tmp
go get: added sigs.k8s.io/controller-tools v0.2.5
/Users/batuhan.apaydin/go/bin/controller-gen object:headerFile=”hack/boilerplate.go.txt” paths=”./…”
Error: go [list -e -json -compiled=true -test=false -export=false -deps=true -find=false -tags ignore_autogenerated — ./…]: exit status 1: go: github.com/developer-guy/pod-validationwebhook: package k8s.io/api/core/v1 imported from implicitly required module; to add missing requirements, run:
 go get k8s.io/api/core/[email protected]
# if you get the same error above , just get the dependency and run make again like the following:
$ go get k8s.io/api/core/[email protected]
$ make
go: creating new go.mod: module tmp
go get: added sigs.k8s.io/controller-tools v0.2.5
/Users/batuhan.apaydin/go/bin/controller-gen object:headerFile=”hack/boilerplate.go.txt” paths=”./…”
go fmt ./…
go vet ./…
go build -o bin/manager main.go

之后,创建 ValidatingAdmissionWebhook

# Remember here, programmatic-validation is for ValidatingAdmissionWebhook, and the --default for the Mutating one.
$ kubebuilder create webhook --group core --version v1 --kind Pod --programmatic-validation
Writing scaffold for you to edit...
api/v1/pod_webhook.go

打开项目后,您应该注意到main.go和“api/v1/pod_webhook.go”文件中存在某种编译时错误。图片

main.go
api/v1/pod_webhook.go

因此,为了解决这个问题,我们将遵循我上面提到的kubebuilder书中的“Webhook for Core Types”部分。

让我们用下面的代码来改变pod_webhook.go的内容,然后,我们将稍微解释一下代码。

package v1

import (
 "context"
 "fmt"
 "net/http"

 corev1 "k8s.io/api/core/v1"
 "sigs.k8s.io/controller-runtime/pkg/client"
 "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// +kubebuilder:webhook:verbs=create;update,path=/validate-core-v1-pod,mutating=false,failurePolicy=fail,groups=core,resources=pods,versions=v1,name=vpod.kb.io

// podValidator validates Pods
type podValidator struct {
 Client  client.Client
 decoder *admission.Decoder
}

func NewPodValidator(c client.Client) admission.Handler {
 return &podValidator{Client: c}
}

// podValidator admits a pod if a specific annotation exists.
func (v *podValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
 pod := &corev1.Pod{}

 err := v.decoder.Decode(req, pod)
 if err != nil {
  return admission.Errored(http.StatusBadRequest, err)
 }

 key := "example-mutating-admission-webhook"
 anno, found := pod.Annotations[key]
 if !found {
  return admission.Denied(fmt.Sprintf("missing annotation %s", key))
 }
 if anno != "foo" {
  return admission.Denied(fmt.Sprintf("annotation %s did not have value %q", key, "foo"))
 }

 return admission.Allowed("")
}

// podValidator implements admission.DecoderInjector.
// A decoder will be automatically injected.

// InjectDecoder injects the decoder.
func (v *podValidator) InjectDecoder(d *admission.Decoder) error {
 v.decoder = d
 return nil
}

我们还需要做一件事。打开 main.go 文件并将 55 和 59 之间的行更改为以下内容以注册我们的 webhook 服务器。

pv := v1.NewPodValidator(mgr.GetClient())
mgr.GetWebhookServer().Register("/validate-core-v1-pod", &webhook.Admission{Handler: pv})

现在,我们从代码的角度准备好了。让我们继续部署webhook。在构建和推送镜像之前,请从Dockerfile中删除#L15处的“COPY controllers/controllers/”,因为我们在这里没有使用控制器。

$ make docker-build docker-push IMG=devopps/pod-validationwebhook:v1
go: creating new go.mod: module tmp
go get: added sigs.k8s.io/controller-tools v0.2.5
/Users/batuhan.apaydin/go/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
/Users/batuhan.apaydin/go/bin/controller-gen "crd:trivialVersions=true" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
go test ./... -coverprofile cover.out
?       github.com/developer-guy/pod-validationwebhook  [no test files]
?       github.com/developer-guy/pod-validationwebhook/api/v1   [no test files]
docker build . -t devopps/pod-validationwebhook:v1
[+] Building 24.2s (16/16) FINISHED
...
docker push devopps/pod-validationwebhook:v1
The push refers to repository [docker.io/devopps/pod-validationwebhook]
45ed4c050068: Pushed
417cb9b79ade: Pushed
v1: digest: sha256:63f3aaf383f34af74027802316f2c58ee750076c2b03046775ad39be818a80c3 size: 739

一旦镜像推送成功,我们需要在进入部署部分之前做几件事,请删除config/default文件夹下的39 webhookcainjection_patch.yaml之间的行,因为我们没有在这里创建MutatingAdmissionWebhook,并删除来自同一目录下的kustomization.yaml16。另外,从config/rbac目录下的kustomization.yaml中删除2,一切就绪。

我们应该在这里做的最后一件事是启用和部署证书管理器,为了做到这一点,我们应该通过取消注释标记的部分和注释来编辑“config/default/kustomization.yaml” 文件。[WEBHOOK][CERTMANAGER]我们知道kubebuilder也使用cert-manager来管理我们的webhookTLS管理,所以,我们应该先在我们的集群中安装一个cert-manager,让我们这样做。

$ minikube start
 minikube v1.18.1 on Darwin 10.15.7
✨ Using the virtualbox driver based on user configuration
 Starting control plane node minikube in cluster minikube
 Creating virtualbox VM (CPUs=3, Memory=8192MB, Disk=20000MB) …
 Preparing Kubernetes v1.20.2 on Docker 20.10.3 …
 ▪ Generating certificates and keys …
 ▪ Booting up control plane …
 ▪ Configuring RBAC rules …
 Verifying Kubernetes components…
 ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v4
 Enabled addons: storage-provisioner, default-storageclass
 Done! kubectl is now configured to use “minikube” cluster and “default” namespace by default
$ helm repo add jetstack https://charts.jetstack.io
"jetstack" has been added to your repositories
$ helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --version v1.2.0 \
  --create-namespace \
  --set installCRDs=true
NAME: cert-manager
LAST DEPLOYED: Mon Apr  5 22:53:17 2021
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager has been deployed successfully!
In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
More information on the different types of issuers and how to configure them
can be found in our documentation:
https://cert-manager.io/docs/configuration/
For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:
https://cert-manager.io/docs/usage/ingress/

$ kubectl get pods --namespace cert-manager --watch
NAME                                       READY   STATUS               RESTARTS   AGE
cert-manager-85f9bbcd97-pqmnv              1/1     Running   0          3m46s
cert-manager-cainjector-74459fcc56-nnpjg   1/1     Running   0          3m46s
cert-manager-webhook-57d97ccc67-8vjff      1/1     Running   0          3m46s

现在,我们已准备好部署 webhook

$ make deploy IMG=devopps/pod-validationwebhook:v1
go: creating new go.mod: module tmp
go get: added sigs.k8s.io/controller-tools v0.2.5
/Users/batuhan.apaydin/go/bin/controller-gen “crd:trivialVersions=true” rbac:roleName=manager-role webhook paths=”./…” output:crd:artifacts:config=config/crd/bases
cd config/manager && kustomize edit set image controller=devopps/pod-validationwebhook:v1
kustomize build config/default | kubectl apply -f -
namespace/pod-validatingwebhook-system created
role.rbac.authorization.k8s.io/pod-validatingwebhook-leader-election-role created
clusterrole.rbac.authorization.k8s.io/pod-validatingwebhook-proxy-role created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRole is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRole
clusterrole.rbac.authorization.k8s.io/pod-validatingwebhook-metrics-reader created
rolebinding.rbac.authorization.k8s.io/pod-validatingwebhook-leader-election-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/pod-validatingwebhook-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/pod-validatingwebhook-proxy-rolebinding created
service/pod-validatingwebhook-controller-manager-metrics-service created
service/pod-validatingwebhook-webhook-service created
deployment.apps/pod-validatingwebhook-controller-manager created
certificate.cert-manager.io/pod-validatingwebhook-serving-cert created
issuer.cert-manager.io/pod-validatingwebhook-selfsigned-issuer created
Warning: admissionregistration.k8s.io/v1beta1 ValidatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 ValidatingWebhookConfiguration
validatingwebhookconfiguration.admissionregistration.k8s.io/pod-validatingwebhook-validating-webhook-configuration created

耶!!!似乎一切正常,但让我们通过创建没有必需注释的 Pod 来测试 webhook

# test with invalid Pod
$ kubectl run --generator=run-pod/v1 nginx --image=nginx
Found existing alias for “kubectl”. You should use: “k”
Alias tip: k run — generator=run-pod/v1 nginx — image=nginx
Flag — generator has been deprecated, has no effect and will be removed in the future.
Error from server (missing annotation example-mutating-admission-webhook): admission webhook “vpod.kb.io” denied the request: missing annotation example-mutating-admission-webhook
# test with valid Pod
$ kubectl run --generator=run-pod/v1 nginx --image=nginx  --overrides='{ "apiVersion": "v1", "metadata": {"annotations": { "required-label":"foo" } } }'
Flag --generator has been deprecated, has no effect and will be removed in the future.
pod/nginx created

现在,我们证明一切都如我们预期的那样工作

参考文献

https://medium.com/trendyol-tech/getting-版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!started-to-write-your-first-kubernetes-admission-webhook-part-2-48d0b0b1780e

0 条回应