开发一个禁止删除 Namespace 的控制器

​大家好,我是乔克。

开发一个禁止删除 Namespace 的控制器插图亿华云

昨天收到一个朋友的信息,说不小心把集群的业务namespace干掉了,导致整个业务都停滞了,问我有没有禁止删除namespace的方案。

在我的记忆里,Kubernetes的准入里并没有这个控制器,所以我就给他说需要自己开发一个准入控制器来实现自己的目标。

作为人,何为正确!我不能只脱裤子,不放屁。所以这里也整理了一下如何自定义Kubernetes的准入控制器。

理论介绍

准入控制器(Admission Controller)位于 API Server 中,在对象被持久化之前,准入控制器拦截对 API Server 的请求,一般用来做身份验证和授权。其中包含两个特殊的控制器:MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook。

MutatingAdmissionWebhook :用于变更请求对象,比如istio为每个Pod注入sidecar,就是通过它实现。ValidatingAdmissionWebhook:用于验证请求对象。

整个准入控制器的流程如下:

开发一个禁止删除 Namespace 的控制器插图1亿华云

当 API 请求进入时,mutating 和 validating 控制器使用配置中的外部 webhooks 列表并发调用,规则如下:

如果所有的 webhooks 批准请求,准入控制链继续流转。如果有任意一个 webhooks 阻止请求,那么准入控制请求终止,并返回第一个 webhook 阻止的原因。其中,多个 webhooks 阻止也只会返回第一个 webhook 阻止的原因。如果在调用 webhook 过程中发生错误,那么请求会被终止或者忽略 webhook。

准入控制器是在 API Server 的启动参数中配置的。一个准入控制器可能属于以上两者中的一种,也可能两者都属于。

我们在部署 Kubernetes 集群的时候都会默认开启一系列准入控制器,如果没有设置这些准入控制器的话可以说你的 Kubernetes 集群就是在裸奔,应该叫管理员为集群添加准入控制器。

代码实现

实现逻辑

在开发之前先大致了解一下准入控制器的Webhook的大致实现逻辑:

Webhook是一个标准的HTTP服务,接收HTTP请求。接收到的请求是一个AdmissionReview对象。然后我们自定义的Hook会处理这个AdmissionReview对象。处理完过后再返回一个AdmissionReview对象,这里面会包含处理结果。

AdmissionReview的结构体如下:

// 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"`

}

从代码的命名中可以很清晰的看出,在请求发送到 WebHook 时我们只需要关注内部的 AdmissionRequest(实际入参),在我们编写的 WebHook 处理完成后只需要返回包含有 AdmissionResponse(实际返回体) 的 AdmissionReview 对象即可;总的来说 AdmissionReview 对象是个套壳,请求是里面的 AdmissionRequest,响应是里面的 AdmissionResponse。

开发一个禁止删除 Namespace 的控制器插图2亿华云

具体实现

(1)首先创建一个HTTP Server,监听端口,接收请求。

package main

import (

"context"

"flag"

"github.com/joker-bai/validate-namespace/http"

log "k8s.io/klog/v2"

"os"

"os/signal"

"syscall"

)

var (

tlscert, tlskey, port string

)

func main() {

flag.StringVar(

THE END
Copyright © 2024 亿华云