溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何啟用Initializers

發(fā)布時(shí)間:2021-12-20 09:52:59 來(lái)源:億速云 閱讀:148 作者:iii 欄目:云計(jì)算

本篇內(nèi)容主要講解“如何啟用Initializers”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“如何啟用Initializers”吧!

Admission Controll的最佳配置

配置過(guò)kube-apiserver的同學(xué)一定記得這個(gè)配置--admission-control或者--admission-control-config-file,你可以在這里順序的配置你想要的準(zhǔn)入控制器,默認(rèn)是AlwaysAdmit 。

  • 在Kubernetes 1.9中,所有允許的控制器列表如已經(jīng)支持多達(dá)32個(gè):

    • AlwaysAdmit,

    • AlwaysDeny,

    • AlwaysPullImages,

    • DefaultStorageClass,

    • DefaultTolerationSeconds,

    • DenyEscalatingExec,

    • DenyExecOnPrivileged,

    • EventRateLimit,

    • ExtendedResourceToleration,

    • ImagePolicyWebhook,

    • InitialResources,

    • Initializers,

    • LimitPodHardAntiAffinityTopology,

    • LimitRanger,

    • MutatingAdmissionWebhook,

    • NamespaceAutoProvision,

    • NamespaceExists,

    • NamespaceLifecycle,

    • NodeRestriction,

    • OwnerReferencesPermissionEnforcement,

    • PVCProtection,

    • PersistentVolumeClaimResize,

    • PersistentVolumeLabel,

    • PodNodeSelector,

    • PodPreset,

    • PodSecurityPolicy,

    • PodTolerationRestriction,

    • Priority,

    • ResourceQuota,

    • SecurityContextDeny,

    • ServiceAccount,

    • ValidatingAdmissionWebhook

注意,在我寫(xiě)這博客的時(shí)候Dynamic Admission Controll官方文檔還沒(méi)來(lái)得及更新到1.9對(duì)應(yīng)內(nèi)容,官方文檔中還是寫(xiě)的GenericAdmissionWebhook,實(shí)際上Webhook類(lèi)已經(jīng)分為MutatingAdmissionWebhook和ValidatingAdmissionWebhook了,而沒(méi)有GenericAdmissionWebhook這一項(xiàng),其實(shí)它就是ValidatingAdmissionWebhook在Kubernetes 1.9后作的rename而已。

這么多的準(zhǔn)入控制器,如果你并不想去了解那么多(雖然我不推薦你這么做,每一項(xiàng)的具體含義請(qǐng)參考admission-controllers官方文檔),沒(méi)關(guān)系,Kubernetes也有推薦項(xiàng)給你。

  • 如果你使用Kubernetes 1.6 ~ 1.8,官方推薦配置如下:

    	--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds


  • 如果你使用Kubernetes 1.9,官方推薦配置如下:

    	--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ValidatingAdmissionWebhook,ResourceQuota,DefaultTolerationSeconds,MutatingAdmissionWebhook


再次強(qiáng)調(diào)一點(diǎn),--admission-control配置的控制器列表是有順序的,越靠前的越先執(zhí)行,一旦某個(gè)控制器返回的結(jié)果是reject的,那么整個(gè)準(zhǔn)入控制階段立刻結(jié)束,所以這里的配置順序也是有講究的,配置順序不好,會(huì)導(dǎo)致性能會(huì)差些。

built-in準(zhǔn)入控制的缺陷

即便Kubernetes提供了這么多的準(zhǔn)入控制器,也不可能滿足所有企業(yè)的需求,因此Kubernetes提供了三個(gè)Dynamic Admission Controller:

  • Initializers(Alpha, Default disable in 1.9)

  • MutatingAdmissionWebhook(Belta, Default enable in 1.9)

  • ValidatingAdmissionWebhook(Alpha in 1.8, Belta in 1.9, Default enable in 1.9)

這三個(gè)Dynamic Admission Controller都是為了解決其他內(nèi)置插件化準(zhǔn)入控制器的兩個(gè)缺陷:

  • 在kube-apiserver編譯時(shí)打包進(jìn)去的,如果有定制化修改,需要重新編譯kube-apiserver。

  • 如果需要修改--admission-controll中的控制器列表(包括順序),都需要重啟kube-apiserver。

    • 如果你沒(méi)做Kubernetes Master HA,會(huì)導(dǎo)致Kubernetes Master中斷服務(wù);

    • 如果你做了Kubernetes Master HA,就完全沒(méi)問(wèn)題了嗎?當(dāng)然也不完全是,服務(wù)不會(huì)中斷,但是存在一段時(shí)間會(huì)存在不同的kube-apiserver有不同的--admission-controll配置,導(dǎo)致同樣的請(qǐng)求如果分發(fā)到不一樣配置的kube-apiserver,就不能做到冪等性了。當(dāng)然,這好像影響也并不大。

Initializers工作機(jī)制

Initializers有什么用

我們什么時(shí)候需要用Initializers呢?當(dāng)集群管理員需要強(qiáng)制對(duì)某些請(qǐng)求或者所有請(qǐng)求都進(jìn)行校驗(yàn)或者修改的時(shí)候,就可以考慮使用Initializers。

  • 通過(guò)Initializers,你可以給每個(gè)即將創(chuàng)建的Pod都插入一個(gè)SideCar容器。

  • 通過(guò)Initializers,給所有Pod都插入一個(gè)帶有測(cè)試數(shù)據(jù)的volume用于業(yè)務(wù)測(cè)試。

  • 通過(guò)Initializers,檢查Secret的長(zhǎng)度是否滿足要求,以此來(lái)保證密碼的復(fù)雜度,如果不滿足就拒絕create pod請(qǐng)求。

另外我之前思考的關(guān)于Harbor鏡像安全的問(wèn)題:在多租戶環(huán)境中,某個(gè)用戶在某個(gè)Node上pull了一個(gè)帶有敏感數(shù)據(jù)的鏡像并且啟動(dòng)為Pod了。此時(shí),另外一個(gè)用戶只要知道這個(gè)image name,并且設(shè)置imagePullPolicy為IfNotPresent,那么這個(gè)用戶的Pod就可能會(huì)被調(diào)度到這個(gè)節(jié)點(diǎn)(如果scheduler配置了ImageLocalityPriority priority policy,非默認(rèn)配置,但在經(jīng)常會(huì)配置,以提高pod啟動(dòng)速度),然后就把別人的敏感鏡像跑起來(lái)了,這在公有云中是不可被接受的。

我們?nèi)绾谓鉀Q這個(gè)問(wèn)題呢?在私有云中,會(huì)通過(guò)DevOps平臺(tái)做好權(quán)限的控制,用戶只能選擇自己的app進(jìn)行部署,并不能指定別人的鏡像名稱(chēng)。在Kubernetes層面,有辦法解決這個(gè)問(wèn)題嗎?嗯,利用Initializers就能很好解決(幸運(yùn)的是,Kubernetes已經(jīng)提供了AlwaysPullImages這個(gè)Admission Controller),所有用戶創(chuàng)建的Pod請(qǐng)求,都經(jīng)過(guò)你的Initializers進(jìn)行檢查和修改,強(qiáng)制修改Pod ImagePullPolicy為Always即可。

如何啟用Initializers

  • 前面提到,需要在每個(gè)kube-apiserver實(shí)例(考慮到Kubernetes Master HA)中--admission-controll中添加Initializers

  • 另外,還需要在每個(gè)kube-apiserver實(shí)例的--runtime-config中添加admissionregistration.k8s.io/v1alpha1。

Initializers的工作原理

  • 首先部署你自己寫(xiě)的Initializers controller。這個(gè)controller通過(guò)watch你想要的resource type,捕獲后對(duì)這些resource的POST請(qǐng)求做修改。我們以envoy-initializer為例:

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時(shí),千萬(wàn)要注意設(shè)置metadata.initializers.pending為空,防止envoy-initializer的部署被自己stuck了。

  • 然后你要?jiǎng)?chuàng)建你的initializerConfigurationAPI Object, 比如你想通過(guò)Initializers給每個(gè)之后創(chuàng)建的Deployment注入一個(gè)envoy proxy sidecar容器:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: InitializerConfiguration
metadata:
  name: envoy
initializers:
  - name: envoy.initializer.kubernetes.io
    rules:
      - apiGroups:
          - "*"
        apiVersions:
          - "*"
        resources:
          - deployments
  • initializerConfiguration創(chuàng)建后,你需要等待幾秒,然后再通過(guò)Deployment部署你的應(yīng)用,這個(gè)時(shí)候?qū)?yīng)的Initializers就會(huì)自動(dòng)append到Deployment的metadata.initializers.pending數(shù)組中,以上面的example為例,就是附加metadata.initializers.pending[0]=envoy.initializer.kubernetes.io

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的時(shí)候,默認(rèn)是無(wú)法通過(guò)api獲取到該deployment object的,因此Initializers controller list&wath 對(duì)象的時(shí)候需要在request url中添加參數(shù)?includeUninitialized=true。

  • 然后這一創(chuàng)建Deployment對(duì)象的event被你自定義的Initializers controller捕獲到了,Initializers controller就按照你的邏輯對(duì)該Deployment進(jìn)行修改,比如注入sidecar container和volume等,并且會(huì)從對(duì)象的metadata.initializers.pending中刪除掉自己對(duì)應(yīng)的Initializers controller。

  • 如果有多個(gè)Initializers映射到這個(gè)對(duì)象, 那么就會(huì)串行的按照上面的邏輯處理。因此如果是不需要對(duì)Object做修改操作的Admission Controller,建議通過(guò)webhook的方式處理(并行的),那樣性能會(huì)更高。initializers的串行方式注定性能會(huì)低,所以最好不要?jiǎng)?chuàng)建多的initializers。

  • 當(dāng)該Object的metadata.initializers.pending為null的時(shí)候,就認(rèn)為已經(jīng)完成初始化流程,接下來(lái)scheduler和controller-managers管理的controllers就能看到這些Object,繼續(xù)后面的調(diào)度和自動(dòng)駕駛邏輯。

注意:當(dāng)你通過(guò)kubectl或者rest api提交創(chuàng)建對(duì)象請(qǐng)求的時(shí)候,如果這個(gè)對(duì)象有相應(yīng)的Initializers,那么這個(gè)對(duì)象會(huì)保持uninitialized狀態(tài),需要要等待Initializers Controllers執(zhí)行完對(duì)應(yīng)的邏輯后才會(huì)返回,并且有個(gè)超時(shí)時(shí)間為30s。

Initializers注意事項(xiàng)

基于上面對(duì)Initializers工作機(jī)制的理解,我們發(fā)現(xiàn)它也有缺陷或者注意事項(xiàng):

  • 如果你部署的Initializers Controllers不能正常工作了或者性能很低,在高并發(fā)場(chǎng)景下會(huì)導(dǎo)致大量的相關(guān)對(duì)象停留在uninitialized狀態(tài),無(wú)法進(jìn)行后續(xù)的調(diào)度。這可能會(huì)影響你的業(yè)務(wù),比如你使用了HPA對(duì)相關(guān)Deployment對(duì)象進(jìn)行彈性擴(kuò)容,當(dāng)負(fù)債上來(lái)的時(shí)候,你的Initializers Controllers不能正常工作了,會(huì)導(dǎo)致你的應(yīng)用不能彈性伸縮,后果可想而知!所以寫(xiě)一個(gè)高性能的穩(wěn)定的Initializers Controllers是你必須的技能。

  • 目前Initializers準(zhǔn)入控制仍屬于Alpha,你懂得。

  • 你部署的Initializers Controllers是如此重要,所以建議你給它部署在kube-system或者單獨(dú)的一個(gè)namespace中,給他分配足夠的ResourceQuota和LimitRanger,以保障它的穩(wěn)定性。

  • 如果你有多個(gè)Initializers Controllers關(guān)聯(lián)到某類(lèi)resource,那么每次創(chuàng)建resource的時(shí)候,生成的metadata.initializers.pending數(shù)組元素順序可能是不一樣的,所以建議這些Initializers Controllers不應(yīng)該有相互依賴。

  • 再次強(qiáng)調(diào)一下,部署你的Initializers Controllers時(shí),千萬(wàn)要注意設(shè)置metadata.initializers.pending為空,防止Initializers Controllers的部署被自己stuck了。

如何開(kāi)發(fā)一個(gè)自定義的Initializers

  • 請(qǐng)參考大神kelseyhightower的項(xiàng)目kubernetes-initializer-tutorial,代碼不到兩百行,很簡(jiǎn)單。

...
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對(duì)Initializers的增強(qiáng)

  • kubectl annotate, apply, edit-last-applied, delete, describe, edit, get, label, set命令可以增加--include-uninitialized來(lái)對(duì)uninitialized進(jìn)行操作;

  • Initializers的啟用不需要手動(dòng)配置feature gate,admission controll中配置后會(huì)自動(dòng)添加到feature gate中;

  • Initializer名稱(chēng)至少包含兩個(gè).,分隔成至少3段;

  • Fixes an initializer bug where update requests which had an empty pending initializers list were erroneously rejected.

到此,相信大家對(duì)“如何啟用Initializers”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI