Kubernetes开发(5)-validateadmission练手

18 篇文章 3 订阅

补充好基础知识后,上手实践才是最快的学习方法,所以先写一个validateadmission练练手。模拟一个生产需求,假设生产环境所有的deployment不允许单点,即副本数量不能少于2,如果少于2则不允许创建,且多个Kubernetes集群都有类似需求,那么这个场景用webhook实现就非常简单。

先上一下自己写的demo地址webhook demo,可以对照查看

思路

之前有提过webhook的调用逻辑,结合当前场景,大致上是以下几个步骤:

  • 创建一个tls 的webserver, 做好路由,路由绑定handler
  • handler 用于接收api-server发来的admission review
  • 创建一个ValidatingWebhookConfiguration,用于注册web server的url 以及 绑定证书。
  • web server 解析admission review ,根据信息做判断
  • 对结果进行处理,然后回调api-server
  • 创建完成

大致的流程就是以上几个步骤,其实非常简单,主要搞清楚几个结构体就可以了,下面开始操作:

// 启动HTTP服务
	mux := http.New()
	handle.InitHandle(mux)
	srv := &go_http.Server{
		Handler:      mux.GetRouter(),
		Addr:         fmt.Sprintf(":%d", port),
		TLSConfig: &tls.Config{
			Certificates: []tls.Certificate{cert},
		},
		WriteTimeout: 15 * time.Hour,
		ReadTimeout:  15 * time.Hour,
	}
	go srv.ListenAndServeTLS("", "")

入口就是一个简单的webserver, 指定好证书路径,以及加载mux路由即可。

func valiDateAdmission(res http.ResponseWriter, req *http.Request) {
	// 解析收到的报文
	reqContent, err := ioutil.ReadAll(req.Body)
	defer req.Body.Close()
	if err != nil {
		klog.Errorf("[webhook] 请求报文解析失败")
		common.ReqBodyInvalid(res)
		return
	}

	// 定义resp的报文
	var admissionResponse *admissionv1.AdmissionResponse
	requestedAdmissionReview := admissionv1.AdmissionReview{}
	// 对api-server传过来的报文做解析
	_, _, err = deserializer.Decode(reqContent, nil, &requestedAdmissionReview)
	if err != nil {
		klog.Errorf("[webhook] 请求报文解析失败: %v", err)
		admissionResponse = &admissionv1.AdmissionResponse{
			Result: &metav1.Status{
				Code:    http.StatusInternalServerError,
				Message: err.Error(),
			},
		}
	// 解析成功,进行具体的拦截
	} else {
		admissionResponse = controller.Validate(&requestedAdmissionReview)
	}

	// 构造返回的 AdmissionReview 这个结构体
	responseAdmissionReview := admissionv1.AdmissionReview{}
	// admission/v1
	responseAdmissionReview.APIVersion = requestedAdmissionReview.APIVersion
	responseAdmissionReview.Kind = requestedAdmissionReview.Kind
	if admissionResponse != nil {
		responseAdmissionReview.Response = admissionResponse
		if requestedAdmissionReview.Request != nil { // 返回相同的 UID
			responseAdmissionReview.Response.UID = requestedAdmissionReview.Request.UID
		}
	}

	klog.Infof("[webhook] 回调api-server, 发送报文: %v",responseAdmissionReview.Response)
	// send response
	respBytes, err := json.Marshal(responseAdmissionReview)
	if err != nil {
		klog.Errorf("[webhook] 无法解析回调报文: %v", err)
		http.Error(res, fmt.Sprintf("Can't write reponse: %v", err), http.StatusBadRequest)
		return
	}
	klog.Info("[webhook] 准备发送回包")

	if _, err := res.Write(respBytes); err != nil {
		klog.Errorf("[webhook] 无法发送回调报文: %v", err)
		http.Error(res, fmt.Sprintf("Can't write reponse: %v", err), http.StatusBadRequest)
	}
}

其中**admissionResponse=controller.Validate(&requestedAdmissionReview)**才是具体的拦截逻辑,前面的处理可以独立出来作为一个单独的装饰函数,因为都是用来解析admission review的

func Validate(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
	req := ar.Request
	var (
		allowed = true
		code    = http.StatusOK
		message = ""
	)

admission的具体结构可以看之前的文章,之前也有提到,拦截是否允许主要就是看allowed这个字段,之前的需求是判断副本数量,所以需要对admission进行解析

	var dep appsv1.Deployment
	if err := json.Unmarshal(req.Object.Raw, &dep); err != nil {
		klog.Errorf("[webhook] 无法解析AdmissionReview object raw: %v", err)
		allowed = false
		code = http.StatusBadRequest
		return &admissionv1.AdmissionResponse{
			Allowed: allowed,
			Result: &metav1.Status{
				Code:    int32(code),
				Message: err.Error(),
			},
		}
	}

因为处理的是deployment, 所以需要对以appsv1.Deployment对结构体进行解析,然后进行判断

	// 处理真正的业务逻辑
	replicas := *dep.Spec.Replicas
	if replicas < 3 {
		klog.Infof("[webhook] deployment不满足最低副本数量,无法创建")
		allowed = false
		code = http.StatusForbidden
		message = "need 3 replicas at least, create deployment failed"
	}

然后打包成docker镜像,以deployment 部署在kubernetes 集群内,然后进行部署一个service, 用于api-server的调用,之后部署ValidatingWebhookConfiguration

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: admission-registry
webhooks:
- name: zyx.test.admission-registry
  rules:
  - apiGroups:   ["apps"]
    apiVersions: ["v1"]
    operations:  ["CREATE"]
    #resources:   ["pods","deployments"]
    resources:   ["deployments"]
  clientConfig:
    service:
      namespace: default
      name: admission-registry
      path: "/v1/api/validate"
    caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURuakNDQW9hZ0F3SUJBZ0lVT2RVeWVCenVVcFdIenFvMGczOE5XeE1SNm9jd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1p6RUxNQWtHQTFVRUJoTUNRMDR4RVRBUEJnTlZCQWdUQ0ZOb1lXNW5hR0ZwTVJFd0R3WURWUVFIRXdoVAphR0Z1WjJoaGFURU1NQW9HQTFVRUNoTURhemh6TVE4d0RRWURWUVFMRXdaVGVYTjBaVzB4RXpBUkJnTlZCQU1UCkNtdDFZbVZ5Ym1WMFpYTXdIaGNOTWpFd05USTBNRGMwT1RBd1doY05Nall3TlRJek1EYzBPVEF3V2pCbk1Rc3cKQ1FZRFZRUUdFd0pEVGpFUk1BOEdBMVVFQ0JNSVUyaGhibWRvWVdreEVUQVBCZ05WQkFjVENGTm9ZVzVuYUdGcApNUXd3Q2dZRFZRUUtFd05yT0hNeER6QU5CZ05WQkFzVEJsTjVjM1JsYlRFVE1CRUdBMVVFQXhNS2EzVmlaWEp1ClpYUmxjekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFLTWJLT0Q5WGMzMDhlWlQKK0J2MGYzR3QxTGtzWk1jUXNUUG5rQXpWeVRQSDdFSkpORkVoVzI0UE8yNzgvaTNWcEdPNU12MVZCcW1rcGwrSgorZ1dwZW5TSDNIbTdsOTZmckt4TmRGWG94Y3VLaWNPOHhvVUVxWCt0cVIvR0toUGYrcjBCWXFUR1o1aVVCZGdVCjU0STJIQjREc1Exc0lCY1JBUms5WVJ5ZjRLYXRZVVEzdGRuNFRyNmFhUXg5OE9Gd1hUNXpiNktINVQ2MTNKdk8KUExpV0JNWUxUZG5WZ256ZzVXeDUreDkvV0FtaTJSK0JjTllLUjY0RWgzeWJZam5NeGtGL1JEMTZPSTlFOTJNRApEaVB6U09oemplQ3RIY1NRcStpTllIQzhzSUJlRFlxL256bTFDR0hTL1NpMDdZVFBjZTBXeE5RelRpMlM2dlZXCkdrWGVPaWNDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHcKSFFZRFZSME9CQllFRlB0S2U2TXFoTjlLaEJ5S3Ntdy9sR3pZd25iL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQgpBUUFqNnVyVTVhS0I5MWZuQTF2dE1DVjZRVHFmenMrd1dFcHZVbElXOHdZYXVydDZPWUc1YkxFSHoyRUJJRXplCjMvcGM4ZUkvamJSU0JNZDJGbk9Sa0pVTmh5MDBBZE05MTRFUFo5Y0FIOFpGUVI1UTZCUmw1Qnl6Mk5jOHN3dGMKMmtraW0vWUJ4TkFFYnFLQjBhTmw0TW5LbTc3TzgzSXpwREN2SzBWdlRCVXJVYnlzd1l6d0lCaEZxSTl5ZUo2Zgo1U1JpemdrUHVYRG1qSlZiN092dzNTNkxRT1o4VmdJRS9wZGJvRTBsTHpmZ2pROWVDVFM0ZmRXSE9oNGp1TXJMCklqeG1pMHhOVVVkTXdPeFNvQlJKa3J3eU1Fa1FoN2swYmZROFdsRkFNenVST29RVmVraU1BNkhLeDdQOFd1UmUKYzhKRkVOVmk5VEVMTXkwWHpJNWhoOVlwCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
  admissionReviewVersions: ["v1"]
  sideEffects: None

其中rules 就是对资源的限制条件,path是之前webserver的url, caBundle 是客户端的ca证书,cat **.pem | bare64即可。

个人公众号, 分享一些日常开发,运维工作中的日常以及一些学习感悟,欢迎大家互相学习,交流

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值