Apiserver干的最重要的三个事就是:git
admission controller很是有用,也是常常会用到的k8s的一个扩展方式,今天在源码级别对其作一下介绍,以及如何本身去开发一个admission controller.github
咱们的应用场景是:咱们但愿把全部须要建立的pod都加上一个注解,由于咱们早期是经过podpreset给pod注入lxcfs的配置的,可是用户在写yaml文件时很容易忘记加上,因此须要在apiserver上来个自动处理golang
metadata: name: test-net annotations: initializer.kubernetes.io/lxcfs: "true" # 就是在pod的metadata里加上这个配置
已经有不少默认很是有用的admission插件,这里挑几个介绍一下:web
名称 | 做用 |
---|---|
AlwaysPullImages | 把全部镜像策略都调整成alwaysPull, 多租户安全时比较有用 |
DefaultStorageClass | 默认存储类型 |
DefaultTolerationSeconds | 节点notready:NoExecute时的容忍时间,好比有时咱们升级kubelet,但愿升级时pod不要漂移就会用到 |
DenyEscalatingExec | 拒绝远程链接容器 |
ExtendedResourceToleration | 好比我有扩展资源,那么我能够经过它来玷污节点,防止不须要该资源的pod到个人机器上来,如GPU |
LimitRanger | 在多租户配额时至关有用,若是pod没配额,那么我能够默认给个很低的配额 |
NamespaceAutoProvision | 这个也很是有用,资源的namespace不存在时就建立一个 |
PodPreset | 能够对pod进行一些预处理设置 |
ResourceQuota | 多租户配额时比较重要,看资源是否知足resource quota中的配置 |
多租户时常常会开启这个,强制全部的镜像必须去拉取,由于若是不这样,那么别的租户若是知道了你的镜像名就能够写一个yaml去启动你的镜像,强制拉时犹豫须要image pull secret因此没法拉取你的镜像。json
因此这个admission干的事就是把镜像拉取策略都改为alwaysPull:api
代码位置:安全
kubernetes/plugin/pkg/admission/alwayspullimages/admission.go func (a *AlwaysPullImages) Admit(attributes admission.Attributes, o admission.ObjectInterfaces) (err error) { // 你能够在attibutes里获取到对象的一切信息,用户信息等 if shouldIgnore(attributes) { // 检查一下是否是你关注的object, 好比建立的一个configmap 那么显然能够忽视 return nil } pod, ok := attributes.GetObject().(*api.Pod) // 这里把initContainer和Container的拉取策略都给改了 for i := range pod.Spec.InitContainers { pod.Spec.InitContainers[i].ImagePullPolicy = api.PullAlways } for i := range pod.Spec.Containers { pod.Spec.Containers[i].ImagePullPolicy = api.PullAlways } return nil } # 还提供一个校验接口,看是否是真的都已经被改了 func (a *AlwaysPullImages) Validate(attributes admission.Attributes, o admission.ObjectInterfaces) (err error) { pod, ok := attributes.GetObject().(*api.Pod) for i := range pod.Spec.InitContainers { if pod.Spec.InitContainers[i].ImagePullPolicy != api.PullAlways { return admission.NewForbidden(attributes, field.NotSupported(field.NewPath("spec", "initContainers").Index(i).Child("imagePullPolicy"), pod.Spec.InitContainers[i].ImagePullPolicy, []string{string(api.PullAlways)}, ), ) } } ... return nil }
而后实现一个注册函数:函数
func Register(plugins *admission.Plugins) { plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { return NewAlwaysPullImages(), nil }) } type AlwaysPullImages struct { *admission.Handler }
最后须要在plugin里面把其注册进去:this
kubernetes/pkg/kubeapiserver/options/plugins.go func RegisterAllAdmissionPlugins(plugins *admission.Plugins) { imagepolicy.Register(plugins) ... }
因此实现一个admission很是简单,主要就是实现两个接口便可。spa
不少状况下咱们并不但愿大动干戈去改apiserver代码,因此apiserver提供了一种动态扩展admission的方式,很是推荐。
有两种类型:
比较重要的是这个AdmissionReview结构体,包含一个请求一个响应
请求:有Object的详细信息,用户信息
响应: 最重要的是 1. 是否容许 2. 修改(patch)的类型 3. 修改(patch)的值, 这个符合json patch标准 (kubectl patch)
可在此 找到一个webhook server的例子
看一个具体例子,labelpatch,是给对象的元数据里加一些label的。
const ( // 特定的json patch格式 addFirstLabelPatch string = `[ { "op": "add", "path": "/metadata/labels", "value": {"added-label": "yes"}} ]` addAdditionalLabelPatch string = `[ { "op": "add", "path": "/metadata/labels/added-label", "value": "yes" } ]` ) // Add a label {"added-label": "yes"} to the object func addLabel(ar v1beta1.AdmissionReview) *v1beta1.AdmissionResponse { obj := struct { metav1.ObjectMeta Data map[string]string }{} raw := ar.Request.Object.Raw err := json.Unmarshal(raw, &obj) if err != nil { klog.Error(err) return toAdmissionResponse(err) } reviewResponse := v1beta1.AdmissionResponse{} reviewResponse.Allowed = true if len(obj.ObjectMeta.Labels) == 0 { reviewResponse.Patch = []byte(addFirstLabelPatch) // 这里最须要注意的就是修改时是经过patch的方式 } else { reviewResponse.Patch = []byte(addAdditionalLabelPatch) } pt := v1beta1.PatchTypeJSONPatch reviewResponse.PatchType = &pt return &reviewResponse }
把这个放到http handle里。
把这个HTTPS服务起一个service, 这样apiserver就能够自动发现它。
apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration metadata: name: <name of this configuration object> webhooks: - name: <webhook name, e.g., pod-policy.example.io> rules: # 最好明确一下该hook关心哪些api,防止带来没必要要的额外开销。 - apiGroups: - "" apiVersions: - v1 operations: - CREATE resources: - pods scope: "Namespaced" clientConfig: service: namespace: <namespace of the front-end service> # webhook server的namespace name: <name of the front-end service> # service name caBundle: <pem encoded ca cert that signs the server cert used by the webhook> # 由于须要经过https访问,因此要给apiserver配置ca admissionReviewVersions: - v1beta1 timeoutSeconds: 1
adminssion control 是很是重要的APIserver扩展的方式,掌握了其开发不少地方就能以比较优雅的方式解决一些实际问题。是基于k8s开发PaaS平台的利器
更多精彩: https://sealyun.com