您好,登錄后才能下訂單哦!
本篇內容主要講解“如何啟用Initializers”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何啟用Initializers”吧!
配置過kube-apiserver
的同學一定記得這個配置--admission-control
或者--admission-control-config-file
,你可以在這里順序的配置你想要的準入控制器,默認是AlwaysAdmit
。
在Kubernetes 1.9中,所有允許的控制器列表如已經支持多達32個:
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
注意,在我寫這博客的時候Dynamic Admission Controll官方文檔還沒來得及更新到1.9對應內容,官方文檔中還是寫的GenericAdmissionWebhook,實際上Webhook類已經分為MutatingAdmissionWebhook和ValidatingAdmissionWebhook了,而沒有GenericAdmissionWebhook這一項,其實它就是ValidatingAdmissionWebhook在Kubernetes 1.9后作的rename而已。
這么多的準入控制器,如果你并不想去了解那么多(雖然我不推薦你這么做,每一項的具體含義請參考admission-controllers官方文檔),沒關系,Kubernetes也有推薦項給你。
如果你使用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
再次強調一點,--admission-control配置的控制器列表是有順序的,越靠前的越先執行,一旦某個控制器返回的結果是reject的,那么整個準入控制階段立刻結束,所以這里的配置順序也是有講究的,配置順序不好,會導致性能會差些。
即便Kubernetes提供了這么多的準入控制器,也不可能滿足所有企業的需求,因此Kubernetes提供了三個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)
這三個Dynamic Admission Controller都是為了解決其他內置插件化準入控制器的兩個缺陷:
在kube-apiserver編譯時打包進去的,如果有定制化修改,需要重新編譯kube-apiserver。
如果需要修改--admission-controll
中的控制器列表(包括順序),都需要重啟kube-apiserver。
如果你沒做Kubernetes Master HA,會導致Kubernetes Master中斷服務;
如果你做了Kubernetes Master HA,就完全沒問題了嗎?當然也不完全是,服務不會中斷,但是存在一段時間會存在不同的kube-apiserver有不同的--admission-controll
配置,導致同樣的請求如果分發到不一樣配置的kube-apiserver,就不能做到冪等性了。當然,這好像影響也并不大。
我們什么時候需要用Initializers呢?當集群管理員需要強制對某些請求或者所有請求都進行校驗或者修改的時候,就可以考慮使用Initializers。
通過Initializers,你可以給每個即將創建的Pod都插入一個SideCar容器。
通過Initializers,給所有Pod都插入一個帶有測試數據的volume用于業務測試。
通過Initializers,檢查Secret的長度是否滿足要求,以此來保證密碼的復雜度,如果不滿足就拒絕create pod請求。
另外我之前思考的關于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即可。
前面提到,需要在每個kube-apiserver實例(考慮到Kubernetes Master HA)中--admission-controll
中添加Initializers
。
另外,還需要在每個kube-apiserver實例的--runtime-config
中添加admissionregistration.k8s.io/v1alpha1
。
首先部署你自己寫的Initializers controller。這個controller通過watch你想要的resource type,捕獲后對這些resource的POST請求做修改。我們以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時,千萬要注意設置metadata.initializers.pending為空,防止envoy-initializer的部署被自己stuck了。
然后你要創建你的initializerConfiguration
API Object, 比如你想通過Initializers給每個之后創建的Deployment注入一個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
創建后,你需要等待幾秒,然后再通過Deployment部署你的應用,這個時候對應的Initializers就會自動append到Deployment的metadata.initializers.pending數組中,以上面的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的時候,默認是無法通過api獲取到該deployment object的,因此Initializers controller list&wath 對象的時候需要在request url中添加參數
?includeUninitialized=true
。
然后這一創建Deployment對象的event被你自定義的Initializers controller捕獲到了,Initializers controller就按照你的邏輯對該Deployment進行修改,比如注入sidecar container和volume等,并且會從對象的metadata.initializers.pending中刪除掉自己對應的Initializers controller。
如果有多個Initializers映射到這個對象, 那么就會串行的按照上面的邏輯處理。因此如果是不需要對Object做修改操作的Admission Controller,建議通過webhook的方式處理(并行的),那樣性能會更高。initializers的串行方式注定性能會低,所以最好不要創建多的initializers。
當該Object的metadata.initializers.pending為null的時候,就認為已經完成初始化流程,接下來scheduler和controller-managers管理的controllers就能看到這些Object,繼續后面的調度和自動駕駛邏輯。
注意:當你通過kubectl或者rest api提交創建對象請求的時候,如果這個對象有相應的Initializers,那么這個對象會保持uninitialized狀態,需要要等待Initializers Controllers執行完對應的邏輯后才會返回,并且有個超時時間為30s。
基于上面對Initializers工作機制的理解,我們發現它也有缺陷或者注意事項:
如果你部署的Initializers Controllers不能正常工作了或者性能很低,在高并發場景下會導致大量的相關對象停留在uninitialized狀態,無法進行后續的調度。這可能會影響你的業務,比如你使用了HPA對相關Deployment對象進行彈性擴容,當負債上來的時候,你的Initializers Controllers不能正常工作了,會導致你的應用不能彈性伸縮,后果可想而知!所以寫一個高性能的穩定的Initializers Controllers是你必須的技能。
目前Initializers準入控制仍屬于Alpha,你懂得。
你部署的Initializers Controllers是如此重要,所以建議你給它部署在kube-system或者單獨的一個namespace中,給他分配足夠的ResourceQuota和LimitRanger,以保障它的穩定性。
如果你有多個Initializers Controllers關聯到某類resource,那么每次創建resource的時候,生成的metadata.initializers.pending數組元素順序可能是不一樣的,所以建議這些Initializers Controllers不應該有相互依賴。
再次強調一下,部署你的Initializers Controllers時,千萬要注意設置metadata.initializers.pending為空,防止Initializers Controllers的部署被自己stuck了。
請參考大神kelseyhightower的項目kubernetes-initializer-tutorial,代碼不到兩百行,很簡單。
... 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
來對uninitialized進行操作;
Initializers的啟用不需要手動配置feature gate,admission controll中配置后會自動添加到feature gate中;
Initializer名稱至少包含兩個.
,分隔成至少3段;
Fixes an initializer bug where update requests which had an empty pending initializers list were erroneously rejected.
到此,相信大家對“如何啟用Initializers”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。