Kubebuilder can build AdmissionWebhooks in addition to CRD APIs and their Controllers, and this article will analyze how Kubebuilder builds AdmissionWebhooks in detail.
AdmissionWebhooks for K8s
First of all, it is important to know what AdmissionWebhooks is in K8s and what its purpose is.
Let’s start with the scenario. If we need to make configuration changes or checks on a pod before it is created, this part of the work would require the administrator to compile it into a binary file in ApiServer, which would be very annoying if the configuration changes were made in a custom form. Admission controllers are tools for this scenario, and are attached to ApiServer in the form of plug-ins, of which AdmissionWebhooks is one.
K8s’ AdmissionWebhooks are of two kinds:
ValidatingAdmissionWebhook, which together are a special type of
admission controllers, one dealing with resource changes and the other with validation.
MutatingAdmissionWebhook does three main things.
- MutatingWebhookConfiguration: the configuration for MutatingAdmissionWebhook to register itself with the ApiServer.
- MutatingAdmissionWebhook itself: an admission controller in the form of a plugin that needs to register itself with the ApiServer.
- Webhook Admission Server: an http server attached to the k8s ApiServer that receives requests from the ApiServer.
If we use Kubebuilder to build AdmissionWebhooks, Kubebuilder will automatically generate the Webhook Server for us and leave a few functions for us to add our own logic.
Creating Custom AdmissionWebhooks
Here’s a demo using a simple scenario where we customize a resource called App, and when a user creates an App instance, we create a Deployment based on the user’s description.
Then we add a
MutatingAdmissionWebhook that automatically adds a sidecar container to the Pod when the user creates a Deployment via the App (using nginx as the sidecar here).
Initialize API and Controller
The first step is to create the CRD and its Controller, which can be done with a few lines of command.
What I’ve done here is relatively simple. The
AppSpec only defines a deploy property (which is
appsv1.DeploymentSpec), and the Controller generates the corresponding Deployment based on the deploy property.
After refining the
Reconcile functions of
AppSpec and Controller, make Kubebuilder regenerate the code and apply the CRD yaml under
config/crd to the current cluster.
Creating a Webhook Server
The next step is to use Kubebuilder to generate Webhooks.
A file named
app_webhook.go is generated under the path
api/v1. You can see that Kubebuilder has defined two variables for you.
These two variables represent MutatingWebhookServer and ValidatingWebhookServer respectively, which will run up when the program starts.
For MutatingWebhookServer, Kubebuilder reserves the
Default() function for users to fill in their own logic.
For what kind of changes we want the Webhook to trigger when the resource changes, you can modify it with this comment.
The corresponding parameters are.
- failurePolicy: indicates the failure policy when the ApiServer cannot communicate with the webhook server, takes the value of “ignore” or “fail”.
- groups: indicates the Api Group under which this webhook will receive requests.
- mutating: this parameter is a bool type, indicating whether it is a mutating type.
- name: the name of the webhook, which should correspond to the configuration.
- path: the path of the webhook.
- resources: indicates which resource the webhook will receive a request for when it changes.
- verbs: indicates the webhook will receive requests for which resource changes, and takes the values “create”, “update”, “delete”, “connect”, or “*” (i.e., all).
- versions: indicates at which version of the resource this webhook will receive a request when it changes.
For ValidatingWebhookServer, Kubebuilder handles it in the same way as MutatingWebhookServer, so I won’t go over it here.
For convenience, I only defined the
Default function of MutatingWebhookServer to inject an nginx sidecar container for each pod of App-type resources.
Running Webhook Server
This article only shares the debugging solution for local development testing, please refer to the official documentation for the online deployment solution.
First, you need to modify MutatingWebhookConfiguration slightly to make ApiServer communicate with Webhook Server. The specific method is as follows.
Configuring Server Path
The first step is to configure Server Path; remove the service and replace it with
url: https://<server_ip>:9443/mutate-app-o0w0o-cn-v1-app, where
server_ip is the ip of the Webhook Server, or if running locally, the local ip. Note that the path in the url should be the same as the one defined in
Configure the certificate
The second step is to configure caBundle; since all components interacting with ApiServer in Kube require bi-directional TLS authentication with ApiServer, we need to manually issue a self-signed CA certificate here first.
After certificate generation, copy
server.crt to the private key and certificate path of the webhook server set by Kubebuilder:
- Path to the private key of the webhook server:
- Path to webhook server’s certificate:
Note: If $(TMPDIR) is empty, the default path is “/tmp/k8s-webhook-server/…” but the default path for android is “/data/local/tmp/k8s-webhook-server/…”.
And the caBundle in MutatingWebhookConfiguration is the base64 encoded result of ca.crt. The final yaml result is as follows.
ValidatingWebhookConfiguration is similar to MutatingWebhookConfiguration, just note that the server path is the same as in
app_webhook.go. After both configuration files are modified, apply them in the cluster.
Finally, run the CRD Controller and Webhook Server directly locally.
Simply run an app and try it.
To see if the sidecar container has been injected.