如何启用Initializers

发布时间:2021-12-20 09:52:59 作者:iii
来源:亿速云 阅读:148

本篇内容主要讲解“如何启用Initializers”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何启用Initializers”吧!

Admission Controll的最佳配置

配置过kube-apiserver的同学一定记得这个配置--admission-control或者--admission-control-config-file,你可以在这里顺序的配置你想要的准入控制器,默认是AlwaysAdmit

注意,在我写这博客的时候Dynamic Admission Controll官方文档还没来得及更新到1.9对应内容,官方文档中还是写的GenericAdmissionWebhook,实际上Webhook类已经分为MutatingAdmissionWebhook和ValidatingAdmissionWebhook了,而没有GenericAdmissionWebhook这一项,其实它就是ValidatingAdmissionWebhook在Kubernetes 1.9后作的rename而已。

这么多的准入控制器,如果你并不想去了解那么多(虽然我不推荐你这么做,每一项的具体含义请参考admission-controllers官方文档),没关系,Kubernetes也有推荐项给你。

再次强调一点,--admission-control配置的控制器列表是有顺序的,越靠前的越先执行,一旦某个控制器返回的结果是reject的,那么整个准入控制阶段立刻结束,所以这里的配置顺序也是有讲究的,配置顺序不好,会导致性能会差些。

built-in准入控制的缺陷

即便Kubernetes提供了这么多的准入控制器,也不可能满足所有企业的需求,因此Kubernetes提供了三个Dynamic Admission Controller:

这三个Dynamic Admission Controller都是为了解决其他内置插件化准入控制器的两个缺陷:

Initializers工作机制

Initializers有什么用

我们什么时候需要用Initializers呢?当集群管理员需要强制对某些请求或者所有请求都进行校验或者修改的时候,就可以考虑使用Initializers。

另外我之前思考的关于Harbor镜像安全的问题:在多租户环境中,某个用户在某个Node上pull了一个带有敏感数据的镜像并且启动为Pod了。此时,另外一个用户只要知道这个image name,并且设置imagePullPolicy为IfNotPresent,那么这个用户的Pod就可能会被调度到这个节点(如果scheduler配置了ImageLocalityPriority priority policy,非默认配置,但在经常会配置,以提高pod启动速度),然后就把别人的敏感镜像跑起来了,这在公有云中是不可被接受的。

我们如何解决这个问题呢?在私有云中,会通过DevOps平台做好权限的控制,用户只能选择自己的app进行部署,并不能指定别人的镜像名称。在Kubernetes层面,有办法解决这个问题吗?嗯,利用Initializers就能很好解决(幸运的是,Kubernetes已经提供了AlwaysPullImages这个Admission Controller),所有用户创建的Pod请求,都经过你的Initializers进行检查和修改,强制修改Pod ImagePullPolicy为Always即可。

如何启用Initializers

Initializers的工作原理

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  initializers:
    pending: []
  labels:
    app: envoy-initializer
  name: envoy-initializer
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: envoy-initializer
      name: envoy-initializer
    spec:
      containers:
        - name: envoy-initializer
          image: gcr.io/hightowerlabs/envoy-initializer:0.0.1
          imagePullPolicy: Always
          args:
            - "-annotation=initializer.kubernetes.io/envoy"
            - "-require-annotation=true"

部署envoy-initializer时,千万要注意设置metadata.initializers.pending为空,防止envoy-initializer的部署被自己stuck了。

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: InitializerConfiguration
metadata:
  name: envoy
initializers:
  - name: envoy.initializer.kubernetes.io
    rules:
      - apiGroups:
          - "*"
        apiVersions:
          - "*"
        resources:
          - deployments
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  annotations:
    "initializer.kubernetes.io/envoy": "true"
  labels:
    app: helloworld
    envoy: "true"
  name: helloworld-with-annotation
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: helloworld
        envoy: "true"
      name: helloworld-with-annotation
    spec:
      containers:
        - name: helloworld
          image: gcr.io/hightowerlabs/helloworld:0.0.1
          imagePullPolicy: Always
          args:
            - "-http=127.0.0.1:8080"

注意:metadata.initializers.pending不为null的时候,默认是无法通过api获取到该deployment object的,因此Initializers controller list&wath 对象的时候需要在request url中添加参数?includeUninitialized=true

注意:当你通过kubectl或者rest api提交创建对象请求的时候,如果这个对象有相应的Initializers,那么这个对象会保持uninitialized状态,需要要等待Initializers Controllers执行完对应的逻辑后才会返回,并且有个超时时间为30s。

Initializers注意事项

基于上面对Initializers工作机制的理解,我们发现它也有缺陷或者注意事项:

如何开发一个自定义的Initializers

...
type config struct {
	Containers []corev1.Container
	Volumes    []corev1.Volume
}

func main() {
	...
	// Watch uninitialized Deployments in all namespaces.
	restClient := clientset.AppsV1beta1().RESTClient()
	watchlist := cache.NewListWatchFromClient(restClient, "deployments", corev1.NamespaceAll, fields.Everything())

	// Wrap the returned watchlist to workaround the inability to include
	// the `IncludeUninitialized` list option when setting up watch clients.
	includeUninitializedWatchlist := &cache.ListWatch{
		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
			options.IncludeUninitialized = true
			return watchlist.List(options)
		},
		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
			options.IncludeUninitialized = true
			return watchlist.Watch(options)
		},
	}

	resyncPeriod := 30 * time.Second

	_, controller := cache.NewInformer(includeUninitializedWatchlist, &v1beta1.Deployment{}, resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				err := initializeDeployment(obj.(*v1beta1.Deployment), c, clientset)
				if err != nil {
					log.Println(err)
				}
			},
		},
	)

	stop := make(chan struct{})
	go controller.Run(stop)

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	<-signalChan

	log.Println("Shutdown signal received, exiting...")
	close(stop)
}

func initializeDeployment(deployment *v1beta1.Deployment, c *config, clientset *kubernetes.Clientset) error {
	if deployment.ObjectMeta.GetInitializers() != nil {
		pendingInitializers := deployment.ObjectMeta.GetInitializers().Pending

		if initializerName == pendingInitializers[0].Name {
			log.Printf("Initializing deployment: %s", deployment.Name)

			o, err := runtime.NewScheme().DeepCopy(deployment)
			if err != nil {
				return err
			}
			initializedDeployment := o.(*v1beta1.Deployment)

			// Remove self from the list of pending Initializers while preserving ordering.
			if len(pendingInitializers) == 1 {
				initializedDeployment.ObjectMeta.Initializers = nil
			} else {
				initializedDeployment.ObjectMeta.Initializers.Pending = append(pendingInitializers[:0], pendingInitializers[1:]...)
			}

			if requireAnnotation {
				a := deployment.ObjectMeta.GetAnnotations()
				_, ok := a[annotation]
				if !ok {
					log.Printf("Required '%s' annotation missing; skipping envoy container injection", annotation)
					_, err = clientset.AppsV1beta1().Deployments(deployment.Namespace).Update(initializedDeployment)
					if err != nil {
						return err
					}
					return nil
				}
			}

			// Modify the Deployment's Pod template to include the Envoy container
			// and configuration volume. Then patch the original deployment.
			initializedDeployment.Spec.Template.Spec.Containers = append(deployment.Spec.Template.Spec.Containers, c.Containers...)
			initializedDeployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, c.Volumes...)

			oldData, err := json.Marshal(deployment)
			if err != nil {
				return err
			}

			newData, err := json.Marshal(initializedDeployment)
			if err != nil {
				return err
			}

			patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1beta1.Deployment{})
			if err != nil {
				return err
			}

			_, err = clientset.AppsV1beta1().Deployments(deployment.Namespace).Patch(deployment.Name, types.StrategicMergePatchType, patchBytes)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func configmapToConfig(configmap *corev1.ConfigMap) (*config, error) {
	var c config
	err := yaml.Unmarshal([]byte(configmap.Data["config"]), &c)
	if err != nil {
		return nil, err
	}
	return &c, nil
}

Kubernetes 1.9对Initializers的增强

到此,相信大家对“如何启用Initializers”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

推荐阅读:
  1. ESXi启用snmp服务
  2. Mongodb启用认证

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

上一篇:Kubernetes的架构怎么使用

下一篇:如何编写sqlmap tamper脚本

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》