您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“如何啟用Initializers”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“如何啟用Initializers”吧!
配置過(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ì)差些。
即便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)然,這好像影響也并不大。
我們什么時(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即可。
前面提到,需要在每個(gè)kube-apiserver實(shí)例(考慮到Kubernetes Master HA)中--admission-controll
中添加Initializers
。
另外,還需要在每個(gè)kube-apiserver實(shí)例的--runtime-config
中添加admissionregistration.k8s.io/v1alpha1
。
首先部署你自己寫(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)建你的initializerConfiguration
API 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。
基于上面對(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了。
請(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 }
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í)!
免責(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)容。