4.3 注入sidecar的mutatePod注入函数编写

news/2025/2/14 1:52:52/

本节重点总结 :

  • serveMutate编写
    • 准入控制请求参数校验
    • 根据annotation标签判断是否需要注入sidecar
    • mutatePod 注入函数编写
    • 生成注入容器和volume的patch函数

serveMutate编写

普通校验请求

  • serveMutate方法
  • body是否为空
  • req header的Content-Type 是否为application/json
	var body []byteif r.Body != nil {if data, err := ioutil.ReadAll(r.Body); err == nil {body = data}}if len(body) == 0 {glog.Error("empty body")http.Error(w, "empty body", http.StatusBadRequest)return}// verify the content type is accuratecontentType := r.Header.Get("Content-Type")if contentType != "application/json" {glog.Errorf("Content-Type=%s, expect application/json", contentType)http.Error(w, "invalid Content-Type, expect `application/json`", http.StatusUnsupportedMediaType)return}

准入控制请求参数校验

  • 构造准入控制的审查对象 包括请求和响应
  • 然后使用UniversalDeserializer解析传入的申请
  • 如果出错就设置响应为报错的信息
  • 没出错就调用mutatePod生成响应
	// 构造准入控制器的响应var admissionResponse *v1beta1.AdmissionResponse// 构造准入控制的审查对象 包括请求和响应// 然后使用UniversalDeserializer解析传入的申请// 如果出错就设置响应为报错的信息// 没出错就调用mutatePod生成响应ar := v1beta1.AdmissionReview{}if _, _, err := deserializer.Decode(body, nil, &ar); err != nil {glog.Errorf("Can't decode body: %v", err)admissionResponse = &v1beta1.AdmissionResponse{Result: &metav1.Status{Message: err.Error(),},}} else {admissionResponse = ws.mutatePod(&ar)}
  • 解析器使用UniversalDeserializer D:\go_path\pkg\mod\k8s.io\apimachinery@v0.22.1\pkg\runtime\serializer\codec_factory.go
package mainimport ("encoding/json""fmt""github.com/golang/glog""gopkg.in/yaml.v2""io/ioutil""k8s.io/api/admission/v1beta1"corev1 "k8s.io/api/core/v1"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/runtime/serializer""net/http"
)var (runtimeScheme = runtime.NewScheme()codecs        = serializer.NewCodecFactory(runtimeScheme)deserializer  = codecs.UniversalDeserializer()// (https://github.com/kubernetes/kubernetes/issues/57982)defaulter = runtime.ObjectDefaulter(runtimeScheme)
)

写入响应

  • 构造最终响应对象 admissionReview
  • 给response赋值
  • json解析后用 w.write写入
	// 构造最终响应对象 admissionReview// 给response赋值// json解析后用 w.write写入admissionReview := v1beta1.AdmissionReview{}if admissionResponse != nil {admissionReview.Response = admissionResponseif ar.Request != nil {admissionReview.Response.UID = ar.Request.UID}}resp, err := json.Marshal(admissionReview)if err != nil {glog.Errorf("Can't encode response: %v", err)http.Error(w, fmt.Sprintf("could not encode response: %v", err), http.StatusInternalServerError)}glog.Infof("Ready to write reponse ...")if _, err := w.Write(resp); err != nil {glog.Errorf("Can't write response: %v", err)http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError)}

mutatePod 注入函数编写

  • 将请求中的对象解析为pod,如果出错就返回
	// 将请求中的对象解析为pod,如果出错就返回req := ar.Requestvar pod corev1.Podif 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(),},}}

是否需要注入判断

	// 是否需要注入判断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,}}
  • mutationRequired判断函数,判断这个pod资源要不要注入
    1. 如果pod在高权限的ns中,不注入
    1. 如果pod annotations中 标记为已注入就不再注入了
    1. 如果pod annotations中 配置不愿意注入就不注入
// 判断这个pod资源要不要注入
// 1. 如果pod在高权限的ns中,不注入
// 2. 如果pod annotations中 标记为已注入就不再注入了
// 3. 如果pod annotations中 配置不愿意注入就不注入
func mutationRequired(ignoredList []string, metadata *metav1.ObjectMeta) bool {// skip special kubernete system namespacesfor _, namespace := range ignoredList {if metadata.Namespace == namespace {glog.Infof("Skip mutation for %v for it's in special namespace:%v", metadata.Name, metadata.Namespace)return false}}annotations := metadata.GetAnnotations()if annotations == nil {annotations = map[string]string{}}// 如果 annotation中 标记为已注入就不再注入了status := annotations[admissionWebhookAnnotationStatusKey]if strings.ToLower(status) == "injected" {return false}// 如果pod中配置不愿意注入就不注入switch strings.ToLower(annotations[admissionWebhookAnnotationInjectKey]) {default:return falsecase "true":return false}
}
  • 相关的常量定义
const (// 代表这个pod是否要注入  = ture代表要注入admissionWebhookAnnotationInjectKey = "sidecar-injector-webhook.xiaoyi/need_inject"// 代表判断pod已经注入过的标志 = injected代表已经注入了,就不再注入admissionWebhookAnnotationStatusKey = "sidecar-injector-webhook.xiaoyi/status"
)// 为了安全,不给这两个ns中的pod注入 sidecar
var ignoredNamespaces = []string{metav1.NamespaceSystem,metav1.NamespacePublic,
}

添加默认的配置

defaulter = runtime.ObjectDefaulter(runtimeScheme)
func applyDefaultsWorkaround(containers []corev1.Container, volumes []corev1.Volume) {defaulter.Default(&corev1.Pod{Spec: corev1.PodSpec{Containers: containers,Volumes:    volumes,},})
}

定义pathoption

type patchOperation struct {Op    string      `json:"op"`              // 动作Path  string      `json:"path"`            // 操作的pathValue interface{} `json:"value,omitempty"` // 值
}

生成容器端的patch函数

// 添加容器的patch
// 如果是第一个patch 需要在path末尾添加 /-
func addContainer(target, added []corev1.Container, basePath string) (patch []patchOperation) {first := len(target) == 0var value interface{}for _, add := range added {value = addpath := basePathif first {first = falsevalue = []corev1.Container{add}} else {path = path + "/-"}patch = append(patch, patchOperation{Op:    "add",Path:  path,Value: value,})}return patch
}

生成添加volume的patch函数

func addVolume(target, added []corev1.Volume, basePath string) (patch []patchOperation) {first := len(target) == 0var value interface{}for _, add := range added {value = addpath := basePathif first {first = falsevalue = []corev1.Volume{add}} else {path = path + "/-"}patch = append(patch, patchOperation{Op:    "add",Path:  path,Value: value,})}return patch
}

更新annotation 的patch

func updateAnnotation(target map[string]string, added map[string]string) (patch []patchOperation) {for key, value := range added {if target == nil || target[key] == "" {target = map[string]string{}patch = append(patch, patchOperation{Op:   "add",Path: "/metadata/annotations",Value: map[string]string{key: value,},})} else {patch = append(patch, patchOperation{Op:    "replace",Path:  "/metadata/annotations/" + key,Value: value,})}}return patch
}
  • 最终的patch调用
func createPatch(pod *corev1.Pod, sidecarConfig *Config, annotations map[string]string) ([]byte, error) {var patch []patchOperationpatch = append(patch, addContainer(pod.Spec.Containers, sidecarConfig.Containers, "/spec/containers")...)patch = append(patch, addVolume(pod.Spec.Volumes, sidecarConfig.Volumes, "/spec/volumes")...)patch = append(patch, updateAnnotation(pod.Annotations, annotations)...)return json.Marshal(patch)
}

调用patch 生成patch option

  • mutatePod方法中
	annotations := map[string]string{admissionWebhookAnnotationStatusKey: "injected"}patchBytes, err := createPatch(&pod, ws.sidecarConfig, annotations)if err != nil {return &v1beta1.AdmissionResponse{Result: &metav1.Status{Message: err.Error(),},}}glog.Infof("AdmissionResponse: patch=%v\n", string(patchBytes))return &v1beta1.AdmissionResponse{Allowed: true,Patch:   patchBytes,PatchType: func() *v1beta1.PatchType {pt := v1beta1.PatchTypeJSONPatchreturn &pt}(),}return nil

本节重点总结 :

  • serveMutate编写
    • 准入控制请求参数校验
    • 根据annotation标签判断是否需要注入sidecar
    • mutatePod 注入函数编写
    • 生成注入容器和volume的patch函数

http://www.ppmy.cn/news/1571848.html

相关文章

React 性能优化的核心方案

一、减少不必要的渲染 组件渲染控制 React.memo:对函数组件进行浅比较,避免相同 props 下的重复渲染。可以通过自定义比较逻辑进一步优化。PureComponent:类组件自动对新旧 props 和 state 进行浅比较,决定是否重新渲染。shouldCo…

Java常见排序算法及代码实现

1、选择排序算法 选择排序(Selection Sort)是一种简单直观的排序算法,它的工作原理是每次从未排序部分选择最小(或最大)的元素,将其放到已排序部分的末尾。 2、冒泡排序算法 冒泡排序(Bubble…

数据可视化技术综述(4)衡量数据的性能指标的十大维度

数据可视化系统的性能直接影响用户体验与业务决策效率。本文从技术实现与工程化视角,系统阐述衡量数据可视化性能的十大核心维度,包括渲染效率、数据吞吐量、内存占用、响应延迟、可扩展性、准确性、跨平台兼容性、交互流畅度、资源消耗及容错性。通过分…

技术革新让生活更便捷

量子通信是一种利用量子力学原理进行信息传递的技术。它的基本原理是量子纠缠和量子密钥分发。量子纠缠指两个粒子即使相隔很远,一个粒子的状态改变会立刻引起另一个粒子状态的相应变化。量子密钥分发则是通过量子态传输实现加密密钥的安全交换。 在信息安全领域&a…

探索 Amazon Aurora DSQL:基本操作全解析(系列①)

1. 引言 在业务中,我使用 Aurora Global Database (PostgreSQL),通常配置为 东京(Act) 和 大阪(Sby),以便在灾害或大规模故障发生时能够进行系统切换。 在 Aurora DSQL 中,两个区域…

【C++学习篇】C++11第三期

目录 ​编辑 1. 新的类功能 1.1 默认的移动构造和移动赋值 1.2 defult和delete 2. 包装器 2.1 function 2.1.1 为什么调用类域里面的静态函数不需要定义对象? 2.1.2 我们来回忆一下,函数指针调用的方式!!&#xff01…

【Java基础-44.2】Java中的LinkedList:特征与方法详解

在Java集合框架中,LinkedList是一个非常重要的数据结构,它实现了List和Deque接口,提供了双向链表的实现。与ArrayList不同,LinkedList在插入和删除操作上具有更高的效率,但在随机访问元素时性能较差。本文将深入探讨Li…

港中文腾讯提出可穿戴3D资产生成方法BAG,可自动生成服装和配饰等3D资产如,并适应特定的人体模型。

今天给大家介绍一种名为BAG(Body-Aligned 3D Wearable Asset Generation)的新方法,可以自动生成可穿戴的3D资产,如服装和配饰,以适应特定的人体模型。BAG方法通过构建一个多视图图像扩散模型,生成与人体对齐…