Kubernetes ImagePolicyWebhook与ValidatingAdmissionWebhook【2】Image_Policy.go源码解析(1)

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: Kubernetes ImagePolicyWebhook与ValidatingAdmissionWebhook【2】Image_Policy.go源码解析(1)

文章目录

1. 函数与包的依赖

2. handler的Image_Policy.go

`review.Status.Reason`是什么?

`v1alpha1.ImageReview`是什么?

`metav1`是表达的什么?

`metav1.ObjectMeta`包含了什么?

`ImageReviewSpec`作为`spect`的类型有什么?

`ImageReviewStatus` 作为`Status`的类型包含了什么?

3. rule目录的not_latest.go

normalize.go

anchoredIdentifierRegexp.MatchString

splitDockerDomain(s)

parse

referenceRegexp.FindStringSubmatch(s)

4. echo包

5. 总结

相关阅读:


Kubernetes ImagePolicyWebhook与ValidatingAdmissionWebhook【1】动手实践感受区别所在

KubernetesImagePolicyWebhook与ValidatingAdmissionWebhook【2】Image_Policy.go源码解析

Kubernetes ImagePolicyWebhook与ValidatingAdmissionWebhook【3】validating_admission.go源码解析

Kubernetes ImagePolicyWebhook与ValidatingAdmissionWebhook【4】main.go源码解析

kubernetes 快速学习手册

1. 函数与包的依赖

https://golang.google.cn/pkg/regexp/

https://pkg.go.dev/github.com/opencontainers/go-digest#Parse

https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#ObjectMeta

https://echo.labstack.com/guide/

https://github.com/containers/image/tree/main/docker/reference

https://github.com/containers/image/blob/main/docker/reference/normalize.go

https://github.com/containers/image/blob/14246c8ecba1132af73f7cd86c373c1e7a1726f6/docker/reference/regexp.go

https://github.com/containers/image/blob/ac2483ecd4b6cdf67d1eb0505a9a638c27ed1b8c/docker/reference/helpers.go

https://github.com/containers/image/blob/a7b943d24434256e7aa564f5b074e2d535e884fe/docker/reference/reference.go#L287


如果你想 深入了解源码的来龙去脉,是十分有必要打开上面的链接。


对于运维人员来说,如何读懂开发写源代码是很痛苦。往往不知如何去理解才好。没有思路,一个接一个的包的各种接口、方法、函数把你搞的晕头转向。那么我们该如何读懂源码呢?


在上一篇文章Kubernetes ImagePolicyWebhook与ValidatingAdmissionWebhook【1】动手实践感受区别所在,我们熟悉了 ImagePolicyWebhook与ValidatingAdmissionWebhook准入控制的运用,接下来我们去细细的拆分他们的逻辑。


源代码地址:https://github.com/kainlite/kube-image-bouncer


**那么,我们从输出的报错信息出发,逆向推理源代码的逻辑。**也许会是个不错的 原则。

2. handler的Image_Policy.go

我们首先看看ImagePolicyWebhook 检验效果的报错信息

$ kubectl describe rc nginx-latest  #警告不允许有latest标签的镜像
  Warning  FailedCreate  8s (x6 over 87s)  replication-controller  (combined from similar events): Error creating: pods "nginx-latest-f7667" is forbidden: image policy webhook backend denied one or more images: Images using latest tag are not allowed

搜索关键报错语句:Images using latest tag are not allowed

我们在image_policy.go的37行找到了

1035234-20181020215539574-213176954.png

review.Status.Reason是什么?

1035234-20181020215539574-213176954.png

从代码中:

  • review.Status.Reason可能是一个字符串类型,存储我们表达的信息。
  • review.Status可能是一个接口之类的东西
  • review是一个v1alpha1.ImageReview类型的变量

v1alpha1.ImageReview是什么?

从图中我们知道他来自专属k8s.io/api/imagepolicy/v1alpha`的包。那我们只能去它的源代码库查看了。

1035234-20181020215539574-213176954.png

1035234-20181020215539574-213176954.png

看到这里我们知道了,v1alpha1.ImageReview是个结构体,结构体顾名思义是形容一类物体的统称范畴,结构体肯定包含特定属性的组成部分,它即可以抽象的,也可以是具体的。例如:

type  人 struct {
  中国人  string
  日本人  string
  美国人 string
  .......
}

我们还可以这样分:

type  人 struct {
  黄种人  string
  黑种人 string
  白种人 string
...........
}
type  人  struct {
   名字 string
   年龄  int
   籍贯  string
   人体结构  json
   状态   json
}

总之,从某一个角度,根据我们是否需要想怎么分就怎么分。


回到本文中来,ImageReview,是描述的什么结构体呢,是一个pod的镜像,有什么内容呢?


metav1.TypeMeta 代表的镜像类型的元信息

metav1.ObjectMeta 代表的镜像对象的元信息

Spec ImageReviewSpec 代表的镜像的细节描述信息

Status ImageReviewStatus 代表的镜像的状态

metav1是表达的什么?

meta 是标签的意思,v1表达的是版本。

最后发现meta其实也是一个包,它的调用方式是k8s.io/apimachinery/pkg/apis/meta/v1

这个包包含了什么?

包v1包含所有版本都通用的API类型,但它们分为两类,一类是对外的,也就是能为我们开发定制的,另一部分是内部的。总之,我们纵观全览,这个包定义了k8s许多对象。我们继续往下看


metav1.TypeMeta中的TypeMeta包含了什么?

1035234-20181020215539574-213176954.png

b1ba385c79984eaead68fc20bfbe0ef1.png

原来,TypeMeta也是结构体,里边包含:


kind: 对象类型,这个我们非常常见,描述资源对象的类型。专业术语描述就是:Kind是一个字符串值,表示该对象所表示的REST资源,不能被更新。

APIversion: APIVersion定义了对象的这种表示的版本模式。

metav1.ObjectMeta包含了什么?

这个不用多说,一查就能知道;由于它的描述非常庞大,这里不展示原文注释,只展示这个结构体有什么,让我们心里有个大概。但陌生的内容我会加点注释。

type ObjectMeta struct {
  Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
  #GenerateName是一个可选的前缀,由服务器使用,以生成惟一的name ONLY如果没有提供name字段。
  GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`
  Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`
  #SelfLink是一个表示这个对象的URL,Kubernetes将在1.20版本中停止传播该字段,该字段已经计划好了在1.21版本中被删除
  SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"`
  UID types.UID `json:"uid,omitempty"protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`
  #ResourceVersion表示该对象的内部版本的不透明值,客户端可使用该值确定对象何时发生更改。可以用于乐观并发、更改检测和对资源或资源集的监视操作。客户端必须将这些值视为不透明的,并将未经修改的值传回服务器。它们可能只对特定的资源或资源集有效。
  ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"`
  #Generation表示所需状态的特定生成的序列号
  Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"`
  CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"`
  DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`
  #DeletionGracePeriodSeconds在将该对象从系统中删除之前,允许该对象正常终止的秒数。仅当还设置了deletionTimestamp时设置。可能只会缩短。
  DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`
  #Labels可用于组织和分类(范围和选择)对象的字符串键和值的映射。可以匹配复制控制器和服务的选择器。
  Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
  #注释是一种非结构化的键值映射,存储在资源中,外部工具可以设置该资源来存储和检索任意元数据。它们是不可查询的,在修改对象时应该保留它们。
  Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
  #由该对象依赖的对象列表,如果列表中的所有对象都有被删除,该对象将被垃圾回收。如果该对象由控制器管理,那么这个列表中的一个条目将指向这个控制器,当控制器字段设置为true时。管理控制器不能多于一个。
  OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"`
#在 k8s 删除对应资源时同时删除关联的外部资源,那么可以通过 Finalizers 来实现。Finalizers 是由字符串组成的列表,当 Finalizers 字段存在时,相关资源不允许被强制删除。存在 Finalizers 字段的的资源对象接收的第一个删除请求设置 metadata.deletionTimestamp 字段的值, 但不删除具体资源,在该字段设置后, finalizer 列表中的对象只能被删除,不能做其他操作。当 metadata.deletionTimestamp 字段非空时,controller watch 对象并执行对应 finalizers 的动作,当所有动作执行完后,需要清空 finalizers ,之后 k8s 会删除真正想要删除的资源。
  Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`
  ClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"`
  #ManagedFields包含了唯一的管理器。 管理器由管理实体自身的基本信息组成,比如操作类型、API 版本、以及它管理的字段。机制追踪对象字段的变化。 当一个字段值改变时,其所有权从当前管理器(manager)转移到施加变更的管理器。 
  ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" protobuf:"bytes,17,rep,name=managedFields"`
}

内容非常常见,当我们执行kubectl descibe pod 或者kubectl get pod ....-oyaml就能看到这类信息,作为运维k8s人员,大家应该知道他们的含义。

ImageReviewSpec作为spect的类型有什么?

如下:

type ImageReviewSpec struct {
     #Containers是正在创建的Pod的每个容器中的信息子集的列表
  Containers []ImageReviewContainerSpec `json:"containers,omitempty" protobuf:"bytes,1,rep,name=containers"`
  #annotation是从Pod的注释中提取的键值对列表。webhook有时会用到它。
  Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,2,rep,name=annotations"`
  Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`
}
ImageReviewStatus 作为Status的类型包含了什么?

如下:

type ImageReviewStatus struct {
    #Allowed表示允许运行所有映像
  Allowed bool `json:"allowed" protobuf:"varint,1,opt,name=allowed"`
  #显示报错信息
  Reason string `json:"reason,omitempty" protobuf:"bytes,2,opt,name=reason"`
  #对象的属性对象将添加AuditAnnotations,允许控制器使用'AddAnnotation',key应该没有前缀(即,允许控制器将添加一个合适的前缀)。
  AuditAnnotations map[string]string `json:"auditAnnotations,omitempty" protobuf:"bytes,3,rep,name=auditAnnotations"`
}

奥,看到这里终于明白了。回到我们的代码中,原来review.Status.Reason是留给我们设置输出报错信息的。


同时呢,我们也根据review.Status.Allowed的源代码库的解释,也明白了它在我们代码中的意义。即是否允许镜像运行的一个判断标志,其实根据代码含义我们也能推测出来。假如allow=true,表达运行镜像运行容器,allow=false,输出错误,并break停止运行程序。

1035234-20181020215539574-213176954.png

同时,我们也发现了for循环中的imageReview.Spec.Containers,如果你浏览了代码库,定也明白它的含义,那就是显示正在创建的Pod的每个容器中的信息子集的列表。

我们注意到for循环中有一个container.Image,它是怎么来的呢?

1035234-20181020215539574-213176954.png

发现 ImageReviewContainerSpec是有结构体的,里边包含image,同时,ImageReviewContainerSpec作为containers的切片对象,所以我们知道container.Image只是显示pod里的容器镜像,通过apped方法把所有镜像添加images切片中,当然,要提前做好切片初始化images := []string{}。

3. rule目录的not_latest.go

毫无疑问,代码中rule的方法,我们是引用的不是源代码库,而是本项目中的另一个代码文件的方法,它在rule目录中。

1035234-20181020215539574-213176954.png

rules目录下有:

1035234-20181020215539574-213176954.png

我们找到了,它不叫方法,它叫函数调用。我们看看这个函数里写了什么逻辑。

1035234-20181020215539574-213176954.png

传入镜像参数,通过调用reference.ParseNormalizedNamed函数返回名字,我们又要看看这个reference.ParseNormalizedNamed到底是什么逻辑,能明白它的准确意思,不能靠猜。所以,我们还有继续寻找代码的源头。


我们找到了这个函数所在的目录,具体是哪个文件呢?

1035234-20181020215539574-213176954.png

normalize.go

找关键词ParseNormalizedNamednormalize.go有点关联。

1035234-20181020215539574-213176954.png

果然找到了,注释翻译是:ParseNormalizedNamed将字符串解析为命名引用,将Docker UI中的熟悉名称转换为完全限定引用。如果值可能是一个标识符,则使用ParseAnyReference

没看明白,还是看具体代码逻辑吧

anchoredIdentifierRegexp.MatchString

anchoredIdentifierRegexp.MatchString又是什么?在这个normalize.go没有发现,通过关键词Regexp找,应该同目录下的regexp.go

1035234-20181020215539574-213176954.png

果然又找到了,注释翻译:anchoredIdentifierRegexp用于检查或匹配标识符值,锚定在字符串的开始和结束。问题又来了,anchored(IdentifierRegexp)又是啥,我点慌了,我像个鼹鼠挖地洞。


这个函数翻译描述是通过添加开始和结束分隔符锚定正则表达式。类似传达一个正则表达式的意思。就是分隔符匹配

1035234-20181020215539574-213176954.png

regexp.Regexp是包regexp的结构体,看看是啥,是个空结构体,空结构体不占据内存空间,因此被广泛作为各种场景下的占位符使用。一是节省资源,二是空结构体本身就具备很强的语义,即这里不需要任何值,仅作为占位符。

1035234-20181020215539574-213176954.png

1035234-20181020215539574-213176954.png

奥,看到这里明白一件事,MatchString是Regexp的方法,也就是,Regexp.MatchString是可以调用,而Regexp结构体本身是anchored函数的传参,anchored(ShortIdentifierRegexp)又被定义anchoredShortIdentifierRegexp,所以,根据go语言的传递规则,anchoredShortIdentifierRegexp可以直接调用Regexp的MatchString,即anchoredIdentifierRegexp.MatchString(s),它是什么意思呢?那就 得明白anchored的return的正则匹配规则,负责检查s(字符串)格式是否负责这个规则。也就是说看看这个规则match(^+ expression(res...).String() +$)是什么意思。

代码中

var match = regexp.MustCompile

也就是

regexp.MustCompile(`^` + expression(res...).String() + `$`)
  • ^ 代表匹配首
  • $ 代表匹配尾

1035234-20181020215539574-213176954.png

express函数对传递的res的正则符合字符串化,并与字符串s拼接。这里好像是闭包函数的使用。与我们的代码中使用的场景联想一下,正是镜像的格式例如:nginx:latest 、kainlite/kube-image-bouncer:latest,其实express作用的就是镜像中特殊字符为了避免被识别为正则表达式。


回头来看,nchoredIdentifierRegexp.MatchString(s)的作用就是判断s是不是一个镜像。

1035234-20181020215539574-213176954.png

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
8天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
28 2
|
8天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
21天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
39 3
|
15天前
|
安全 测试技术 Go
Go语言中的并发编程模型解析####
在当今的软件开发领域,高效的并发处理能力是提升系统性能的关键。本文深入探讨了Go语言独特的并发编程模型——goroutines和channels,通过实例解析其工作原理、优势及最佳实践,旨在为开发者提供实用的Go语言并发编程指南。 ####
|
20天前
|
Go
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
68 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0
|
1月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
62 0
|
1月前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
83 0
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
56 5

推荐镜像

更多
下一篇
无影云桌面