kubernetes 之 admission controller原理
本文主要讲解kubernetes的admission controller调用过程,以及原理设计。
APIServer其实最重要的三件事情就是:
- 认证: 用户是否合法
- 授权: 用户拥有哪些权限
- 准入控制: 一个调用链,对请求进行修改或拒绝
这个准入控制器是十分有用的,也是kubernetes一种比较常见的扩展方式。现在,我们将详细的介绍一下这个admission controller。
admission controller支持参数说明
1 |
|
- AdmissionOptions 是数据结构并同时支持低版本的参数传入
- NewAdmissionOptions 进行插件注册、排序、将默认关闭的插件禁用。
- Validate 对参数–enable-admission-plugins 、 –disable-admission-plugins 和 admission-control 验证有效性。
- ApplyTo把对应的参数配置信息写入到etcd中去
admission controller插件相关逻辑
1 |
|
这段代码主要是说明默认关闭的admission controller
-
默认开启的admission controller:
名称 作用 NamespaceLifecycle 确保处于termination状态的namespace不再接收任何新对象的请求,并拒绝请求不存在的namespace。 LimitRanger 在多租户配额时相当有用,如果pod没配额,那么我可以默认给个很低的配额 ServiceAccount 在pod没有设置serviceAccount属性时,将这个pod的sa设置为"default”;在安全环境下使用,因为default这个sa的权限是admin权限。 DefaultStorageClass 默认存储类型 PersistentVolumeClaimResize 检查传入的 PersistentVolumeClaim 调整大小请求,对其执行额外的验证操作。(注意:对调整卷大小的支持是一种 Alpha 特性。管理员必须将特性门控 ExpandPersistentVolumes 设置为 true 才能启用调整大小。) DefaultTolerationSeconds 设置POD的默认forgiveness toleration为5分钟。 MutatingAdmissionWebhook 变更准入控制webhook ValidatingAdmissionWebhook 验证准入控制webhook ResourceQuota 多租户配额时比较重要,看资源是否满足resource quota中的配置 这里有两个比较特殊的控制器:MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook。
-
根据版本开启的feature
而这里需要注意admission controller 需要进行版本判断,目前 PodPriority和TaintNodesByCondition需要根据不同的版本来判断是否开启这个功能,至于这俩插件的具体功能就不具体赘述了。我们这里主要讲述admission的原理。
plugins.gokube_features.go 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// owner: @bsalamat // alpha: v1.8 // beta: v1.11 // GA: v1.14 // // Add priority to pods. Priority affects scheduling and preemption of pods. PodPriority utilfeature.Feature = "PodPriority" // owner: @k82cn // beta: v1.12 // // Taint nodes based on their condition status for 'NetworkUnavailable', // 'MemoryPressure', 'PIDPressure' and 'DiskPressure'. TaintNodesByCondition utilfeature.Feature = "TaintNodesByCondition"
-
最后最终处理一下默认关闭的admission sets.NewString(AllOrderedPlugins…).Difference(defaultOnPlugins) 将默认开启的admission排除后为默认关闭的admission。
1 |
|
这段代码主要是把所有支持的admission controller进行注册和排序
- AllOrderedPlugins 是对所有的插件进行了一次排序
- RegisterAllAdmissionPlugins 所有支持的插件在Kubernetes的代码中进行了注册
看到这里我们知道了其实注册admission controller的话跟我们在apiserver中配置的顺序并无太大的关系,内部已经定好了加载顺序了。而且如果我们要在kubernetes改动源码来编写对应的admission controller的话 需要我们在这两个函数也注册一下我们的plugin否则kubernetes是不认识的。
编写一个自定义的插件
所有的插件定义都在kubernetes/plugin/pkg/admission。其实是比较简单的。
- 实现admission controller对应的接口
- 定义并注册这个插件
- 在plugins.go中的RegisterAllAdmissionPlugins函数中调用之前写好的注册函数
- 在plugins.go中的AllOrderedPlugins函数中把写好的插件进行排序
实现admission controller对应的接口
1 |
|
-
Handles函数 主要是告诉kubernetes这个admission是否能处理对应的操作,具体的操作有: CREATE, UPDATE, DELETE, or CONNECT。
- 如果无需处理则直接返回true即可
- admission.NewHandler(admission.Create, admission.Update) 可调用公共方法来直接生成方法实现。
-
Admit函数 对kubernetes的资源进行处理或者修改等,比如加个label什么的。
-
Validate 对请求数据的具体属性进行一次验证,比如验证k8s的资源定义是否符合规则等。
由于admission controller会在apiserver接收到数据后优先进行Admit函数修改资源后再调用Validate进行验证资源数据属性的合法性。所以这里可以看到 Validate其实本质上就是Admit的一个验证而已。
定义并注册这个插件
- 定义插件名
go 1
const PluginName = "AlwaysPullImages"
- 声明插件对应的结构体
go 1
2
3type AlwaysPullImages struct { *admission.Handler }
go 1
2
3
4
5// Handles returns true if this admission controller can handle the given operation // where operation can be one of CREATE, UPDATE, DELETE, or CONNECT func (alwaysAdmit) Handles(operation admission.Operation) bool { return true }
go 1
2
3
4
5
6
7
8// NewAlwaysPullImages creates a new always pull images admission control handler func NewAlwaysPullImages() *AlwaysPullImages { return &AlwaysPullImages{ Handler: admission.NewHandler(admission.Create, admission.Update), } } var _ admission.MutationInterface = &AlwaysPullImages{} var _ admission.ValidationInterface = &AlwaysPullImages{}
go 1
2var _ admission.MutationInterface = &AlwaysPullImages{} var _ admission.ValidationInterface = &AlwaysPullImages{}
- 将这个插件注册到admission中去
其实就是把写好的初始化函数注册到admission中
go 1
2
3
4
5
6// Register registers a plugin func Register(plugins *admission.Plugins) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { return NewAlwaysPullImages(), nil }) }