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

本节重点总结 :

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

serveMutate编写

普通校验请求

  • serveMutate方法
  • body是否为空
  • req header的Content-Type 是否为application/json
	var body []byte
	if 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 accurate
	contentType := 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 main

import (
	"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 = admissionResponse
		if 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.Request
	var pod corev1.Pod
	if 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 namespaces
	for _, 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 false
	case "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,
}

添加默认的配置

  • https://github.com/kubernetes/kubernetes/pull/58025
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"`            // 操作的path
	Value interface{} `json:"value,omitempty"` // 值
}

生成容器端的patch函数

// 添加容器的patch
// 如果是第一个patch 需要在path末尾添加 /-
func addContainer(target, added []corev1.Container, basePath string) (patch []patchOperation) {
	first := len(target) == 0
	var value interface{}
	for _, add := range added {
		value = add
		path := basePath
		if first {
			first = false
			value = []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) == 0
	var value interface{}
	for _, add := range added {
		value = add
		path := basePath
		if first {
			first = false
			value = []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 []patchOperation

	patch = 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.PatchTypeJSONPatch
			return &pt
		}(),
	}
	return nil

本节重点总结 :

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/967225.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32 Unix时间戳

Unix时间戳 Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒 时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量 世界上所有时区的秒计数器相同,不同时区通过…

Spring Boot启动内嵌tocmat原理

要研究Spring Boot启动内嵌tomcat的原理,就需要先了解一下Spring Boot自动配置的过程,首先简要的梳理一下springboot自动配置的步骤。 一、SpringBoot自动配置 当SpringBoot应用启动时,EnableAutoConfiguration注解被激活,该注解…

腾讯云AI代码助手评测:如何智能高效完成Go语言Web项目开发

腾讯云AI代码助手评测:如何智能高效完成Go语言Web项目开发 ?? 文章目录 腾讯云AI代码助手评测:如何智能高效完成Go语言Web项目开发 ?? 背景引言开发环境介绍腾讯云AI代码助手使用实例 1. 代码补全2. 技术对话3. 代码优化4. 规范代码5. Bug处理 获得…

feign 远程调用详解

在平常的开发工作中,我们经常需要跟其他系统交互,比如调用用户系统的用户信息接口、调用支付系统的支付接口等。那么,我们应该通过什么方式进行系统之间的交互呢?今天,简单来总结下 feign 的用法。 1:引入依…

【JVM详解三】垃圾回收机制

一、对象是否存活 强引用:Object obj new Object(); 只要强引用还在,垃圾收集器永远不会回收掉被引用的对象。在不用对象的时将引用赋值为 null,能够帮助垃圾回收器回收对象。比如 ArrayList 的 clear() 方法实现。软引用(SoftRe…

基于lstm+gru+transformer的电池寿命预测健康状态预测-完整数据代码

项目视频讲解: 毕业设计:基于lstm+gru+transformer的电池寿命预测 健康状态预测_哔哩哔哩_bilibili 数据: 实验结果:

opentelemetry-collector 配置elasticsearch

一、修改otelcol-config.yaml receivers:otlp:protocols:grpc:endpoint: 0.0.0.0:4317http:endpoint: 0.0.0.0:4318 exporters:debug:verbosity: detailedotlp/jaeger: # Jaeger supports OTLP directlyendpoint: 192.168.31.161:4317tls:insecure: trueotlphttp/prometheus: …

uniapp访问django目录中的图片和视频,2025[最新]中间件访问方式

新建中间件, middleware.py 匹配,以/cover_image/ 开头的图片 匹配以/episode_video/ 开头的视频 imageSrc: http://192.168.110.148:8000/cover_image/12345/1738760890657_mmexport1738154397386.jpg, videoSrc: http://192.168.110.148:8000/episode_video/12345/compres…

ChunkKV:优化 KV 缓存压缩,让 LLM 长文本推理更高效

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

Windows下ollama详细安装指南

文章目录 1、Windows下ollama详细安装指南1.1、ollama介绍1.2、系统要求1.3、下载安装程序1.4、安装步骤1.5、验证安装1.6、环境变量配置1.7、模型选择与安装【deepseek 示例】1.7.1、拉取并运行模型1.7.2、进阶使用技巧 1、Windows下ollama详细安装指南 1.1、ollama介绍 olla…

【算法】动态规划专题⑥ —— 完全背包问题 python

目录 前置知识进入正题模板 前置知识 【算法】动态规划专题⑤ —— 0-1背包问题 滚动数组优化 完全背包问题是动态规划中的一种经典问题,它与0-1背包问题相似,但有一个关键的区别:在完全背包问题中,每种物品都有无限的数量可用。…

基于自然语言处理的客服情感分析系统分析报告

1.大纲分析 基于自然语言处理的客服情感分析系统分析报告 引言 随着互联网的发展,企业的客服体系面临着巨大的挑战和机遇。传统的客服模式依赖人工接听电话和处理邮件,这种方式效率低下且难以满足日益增长的服务需求。为了提高服务质量和服务效率&…

Untiy3d 配置vs code开发环境

安装插件 2. 修改unity3d 的开发工具 问题处理,如果出现 visualstudiotoolsforunity.vstuc requested to download the .NET Runtime 并且一直在下载Downloading .NET version(s) 8.0.12~x64,可以手动去微软的官方下载安装.net sdk对应的版本 然后在项目…

第一财经对话东土科技 | 探索工业科技新边界

当前以ChatGPT、Sora等为代表的生成式人工智能快速发展,越来越多面向垂直场景的行业大模型涌现出来,并成为推动制造业智能化改造与数字化转型、加快推进新型工业化,进而培育发展新质生产力的新引擎。 在垂类场景的应用落地,是AI发…

win32汇编环境,结构体的使用示例二

;运行效果 ;win32汇编环境,结构体的使用示例二 ;举例说明结构体的定义,如何访问其中的成员,使用assume指令指向某个结构体,计算结构数组所需的偏移量得到某个成员值等 ;直接抄进RadAsm可编译运行。重要部分加备注。 ;下面为asm文件 ;>>…

C++ 设计模式 - 访问者模式

一:概述 访问者模式将作用于对象层次结构的操作封装为一个对象,并使其能够在不修改对象层次结构的情况下定义新的操作。 《设计模式:可复用面向对象软件的基础》一书中的访问者模式因两个原因而具有传奇色彩:一是因为它的复杂性&a…

deepseek本地部署-linux

1、官网推荐安装方法(使用脚本,我绕不过github,未采用) 登录ollama下载网站https://ollama.com/download/linux,linux下有下载脚本。 正常来说,在OS系统下直接执行脚本即可。 2、手动安装方法 2.1获取ol…

Spring Boot Actuator(官网文档解读)

定义 Spring Boot Actuator 是 Spring Boot 提供的一个用于监控和管理应用程序的模块。它能够提供各种生产级别的功能,如健康检查、度量指标收集、配置属性查看等,帮助开发者了解应用的内部状态并进行故障排查。 Actuator 引入 要启用 Actuator…

保姆级教程Docker部署Zookeeper模式的Kafka镜像

目录 一、安装Docker及可视化工具 二、Docker部署Zookeeper 三、单节点部署 1、创建挂载目录 2、命令运行容器 3、Compose运行容器 4、查看运行状态 5、验证功能 四、部署可视化工具 1、创建挂载目录 2、Compose运行容器 3、查看运行状态 一、安装Docker及可视化工…

【力扣】279.完全平方数

AC截图 题目 思路 总结动态规划方程得出的思路 找到最小子问题,涉及到当前数和上一个数的跨度,以及上一个数的结果如何变成当前数的结果这两个点。 1,当前数n和上一个数的跨度: 假设n12, 上一个数可以是11&#xff0c…