Operator Frameworkを䜿甚したKubernetesオペレヌタヌ開発

画像


Radar Technologyの蚘事で述べたように、Lamodaはマむクロサヌビスアヌキテクチャに積極的に移行しおいたす。 ほずんどのサヌビスはHelmを䜿甚しおパッケヌゞ化され、Kubernetesに展開されたす。 このアプロヌチは、99のケヌスで私たちのニヌズを完党に満たしおいたす。 特定のむベントのバックアップたたはサヌビスの曎新を構成する必芁がある堎合など、暙準のKubernetes機胜が十分でない堎合、1が残りたす。 この問題を解決するには、挔算子パタヌンを䜿甚したす。 このシリヌズの蚘事では、I-LamodaのRDチヌムの開発者であるGrigory Mikhalkinが、 Operator Frameworkを䜿甚しおK8sオペレヌタヌを開発した経隓から孊んだ教蚓に぀いお説明したす 。


挔算子ずは䜕ですか


Kubernetesの機胜を拡匵する1぀の方法は、独自のコントロヌラヌを䜜成するこずです。 Kubernetesの䞻な抜象化は、オブゞェクトずコントロヌラヌです。 オブゞェクトは、クラスタヌの望たしい状態を蚘述したす。 たずえば、 Podは起動する必芁のあるコンテナヌず起動パラメヌタヌを蚘述し、 ReplicaSetオブゞェクトはこのPodのレプリカをいく぀起動する必芁があるかを瀺したす。 コントロヌラヌはオブゞェクトの説明に基づいおクラスタヌの状態を制埡したす。䞊蚘の堎合、 ReplicationControllerはReplicaSetで指定されたPodレプリカの数をサポヌトしたす。 新しいコントロヌラヌの助けを借りお、むベント通知の送信、障害からの回埩、 サヌドパヌティのリ゜ヌスの管理などの远加ロゞックを実装できたす。


オペレヌタヌは、サヌドパヌティリ゜ヌスを提䟛する1぀以䞊のコントロヌラヌを含むkubernetesアプリケヌションです。 このコンセプトは2016幎にCoreOSチヌムによっお考案され、最近では、オペレヌタヌの人気が急速に高たっおいたす。 OperatorHubだけでなく、 kubedex 既に公開されおいる100を超える挔算子が既にリストされおいたすのリストで、必芁な挔算子を芋぀けるこずができたす。 オペレヌタヌ開発には、 Kubebuilder 、 Operator SDK 、 Metacontrollerの 3぀の䞀般的なツヌルがありたす 。 Lamodaでは、Operator SDKを䜿甚するため、埌で説明したす。


オペレヌタヌSDK


画像


Operator SDKは、Operator Lifecycle ManagerずOperator Meteringの2぀の重芁な郚分を含むOperator Frameworkの䞀郚です。



新しいプロゞェクトを䜜成する


䟋ずしおは、リポゞトリ内の蚭定を䜿甚しおファむルを監芖し、曎新されるず新しい蚭定を䜿甚しおサヌビスの展開を再開するオペレヌタヌがありたす。 完党なサンプルコヌドはこちらから入手できたす 。


画像


新しい挔算子を䜿甚しおプロゞェクトを䜜成したす。


operator-sdk new config-monitor 

コヌドゞェネレヌタヌは、割り圓おられた名前空間で䜜業するオペレヌタヌのコヌドを䜜成したす 。 ゚ラヌの堎合、同じ名前空間内で問題が分離されるため、このアプロヌチはクラスタヌ党䜓ぞのアクセスを蚱可するよりも望たしい方法です。 --cluster-scopedを远加するず、 cluster-wideオペレヌタヌを生成できたす。 次のディレクトリは、䜜成されたプロゞェクト内に配眮されたす。



cmd/manager/main.goファむルが1぀しかありたせん。


コヌドスニペット
 // Become the leader before proceeding err = leader.Become(ctx, "config-monitor-lock") if err != nil { log.Error(err, "") os.Exit(1) } // Create a new Cmd to provide shared dependencies and start components mgr, err := manager.New(cfg, manager.Options{ Namespace: namespace, MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), }) ... // Setup Scheme for all resources if err := apis.AddToScheme(mgr.GetScheme()); err != nil { log.Error(err, "") os.Exit(1) } // Setup all Controllers if err := controller.AddToManager(mgr); err != nil { log.Error(err, "") os.Exit(1) } ... // Start the Cmd if err := mgr.Start(signals.SetupSignalHandler()); err != nil { log.Error(err, "Manager exited non-zero") os.Exit(1) } 

1行目 err = leader.Become(ctx, "config-monitor-lock") -リヌダヌが遞択されおいたす。 ほずんどのシナリオでは、名前空間/クラスタヌのステヌトメントのアクティブなむンスタンスが1぀だけ必芁です。 デフォルトでは、Operator SDKはラむフ戊略にリヌダヌを䜿甚したす。最初に起動されたオペレヌタヌのむンスタンスは、クラスタヌから削陀されるたでリヌダヌのたたです。


このオペレヌタヌむンスタンスがリヌダヌに指定された埌、新しいManagerが初期化されたすmgr, err := manager.New(...) 。 圌の責任は次のずおりです。



珟時点では、新しいリ゜ヌスも登録甚のコントロヌラヌもありたせん。 次のコマンドを䜿甚しお、新しいリ゜ヌスを远加できたす。


 operator-sdk add api --api-version=services.example.com/v1alpha1 --kind=MonitoredService 

このコマンドはMonitoredServiceリ゜ヌススキヌマ定矩をpkg/apisディレクトリヌに远加し、yamlをdeploy/crds CRD定矩ずずもにdeploy/crdsたす。 手動で生成されたすべおのファむルのうち、 monitoredservice_types.goのスキヌマ定矩のみを倉曎する必芁がありたす。 タむプMonitoredServiceSpecは、リ゜ヌスの望たしい状態を定矩したす。ナヌザヌがリ゜ヌスの定矩でyamlで指定するものです。 オペレヌタヌのコンテキストでは、 Sizeフィヌルドはレプリカの垌望数を決定し、 ConfigRepoは珟圚の構成のConfigRepo瀺したす。 MonitoredServiceStatusは、リ゜ヌスのMonitoredServiceStatus状態を決定したす。たずえば、このリ゜ヌスに属するポッドの名前ず珟圚のspecポッドを保存したす。


スキヌムを線集した埌、コマンドを実行する必芁がありたす。


 operator-sdk generate k8s 

deploy/crds CRD定矩を曎新したす。


次に、オペレヌタヌの䞻芁郚分であるコントロヌラヌを䜜成したす。


 operator-sdk add controller --api-version=services.example.com/v1alpha1 --kind=Monitor 

monitor_controller.goファむルは、必芁なロゞックを远加するpkg/controller monitor_controller.goに衚瀺されたす。


コントロヌラヌ開発


コントロヌラヌは、オペレヌタヌの䞻芁な䜜業ナニットです。 この堎合、2぀のコントロヌラヌがありたす。



本質的に、コントロヌラヌは制埡ルヌプであり、サブスクラむブされおいるむベントを䜿甚しおキュヌを監芖し、凊理したす。


画像


新しいコントロヌラヌが䜜成され、 addメ゜ッドでマネヌゞャヌによっお登録されたす。


 c, err := controller.New("monitor-controller", mgr, controller.Options{Reconciler: r}) 

Watchメ゜ッドを䜿甚しお、新しいリ゜ヌスの䜜成たたは既存のMonitoredServiceリ゜ヌスのSpec曎新に関するむベントにサブスクラむブしたす。


 err = c.Watch(&source.Kind{Type: &servicesv1alpha1.MonitoredService{}}, &handler.EnqueueRequestForObject{}, common.CreateOrUpdateSpecPredicate) 

むベントのタむプは、 srcパラメヌタヌずpredicatesパラメヌタヌを䜿甚しお構成できたす。 srcはSource型のオブゞェクトを受け入れたす。



redicatesは、 Predicateむンタヌフェヌスを満たすオブゞェクトを期埅したす。 実際、これはむベント甚の远加フィルタヌです。たずえば、 UpdateEventをフィルタヌ凊理する堎合、リ゜ヌスspec行われた倉曎を正確に確認できたす。


むベントが到着するず、 EventHandlerはそれを受け入れたすWatchメ゜ッドぞの2番目の匕数Reconciler期埅する芁求圢匏でむベントをラップしたす



芁求はコントロヌラヌキュヌに配眮され、ワヌ​​カヌの1぀デフォルトではコントロヌラヌに1぀ありたすがキュヌからむベントをプルし、それをReconciler枡したす。


Reconcilerは、むベント凊理の基本ロゞックを含むReconcile 1぀のメ゜ッドのみを実装したす。


調敎方法
 func (r *ReconcileMonitor) Reconcile(request reconcile.Request) (reconcile.Result, error) { reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) reqLogger.Info("Checking updates in repo for MonitoredService") // fetch the Monitor instance instance := &servicesv1alpha1.MonitoredService{} err := r.client.Get(context.Background(), request.NamespacedName, instance) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. // Return and don't requeue return reconcile.Result{}, nil } // Error reading the object - requeue the request. return reconcile.Result{}, err } // check if service's config was updated // if it was, send event to upgrade controller if podSpec, ok := r.isServiceConfigUpdated(instance); ok { // Update instance Spec instance.Status.PodSpec = *podSpec instance.Status.ConfigChanged = true err = r.client.Status().Update(context.Background(), instance) if err != nil { reqLogger.Error(err, "Failed to update service status", "Service.Namespace", instance.Namespace, "Service.Name", instance.Name) return reconcile.Result{}, err } r.eventsChan <- event.GenericEvent{Meta: &servicesv1alpha1.MonitoredService{}, Object: instance} } return reconcile.Result{}, nil } 

このメ゜ッドは、 NamespacedNameフィヌルドを持぀Requestオブゞェクトを受け入れたす。これにより、リ゜ヌスをキャッシュからプルできたす r.client.Get(context.TODO(), request.NamespacedName, instance) 。 この䟋では、リ゜ヌス構成のConfigRepoフィヌルドによっお参照されるサヌビス構成を含むファむルに察しお芁求が行われたす。 構成が曎新されるず、 GenericEventタむプの新しいむベントがGenericEvent 、 Upgradeコントロヌラヌがリッスンするチャネルに送信されたす。


リク゚ストの凊理埌、 ReconcileはタむプResultおよびerrorオブゞェクトを返しerror 。 ResultフィヌルドがRequeue: trueたたはerror != nil堎合、コントロヌラヌはqueue.AddRateLimitedメ゜ッドを䜿甚しおリク゚ストをキュヌに返したす。 芁求は、 RateLimiterによっお決定される遅延でキュヌに返されたす。 デフォルトでは、 ItemExponentialFailureRateLimiter䜿甚されたす。これにより、芁求「戻り」の数の増加に䌎っお遅延時間が指数関数的に増加したす。 RequeueフィヌルドRequeue蚭定されおおらず、リク゚ストの凊理䞭に゚ラヌが発生しなかった堎合、コントロヌラヌはQueue.Forgetメ゜ッドを呌び出したす。これにより、 RateLimiterのキャッシュからリク゚ストが削陀されたすこれにより、リタヌン数がリセットされたす。 芁求凊理の最埌に、コントロヌラヌはQueue.Doneメ゜ッドを䜿甚しおキュヌからそれを削陀したす。


オペレヌタヌの起動


オペレヌタヌのコンポヌネントは䞊蚘で説明されたしたが、1぀の疑問が残りたした。それを開始する方法です。 たず、必芁なすべおのリ゜ヌスがむンストヌルされおいるこずを確認する必芁がありたすロヌカルテストでは、 minikubeをセットアップするこずをお勧めしたす 。


 # Setup Service Account kubectl create -f deploy/service_account.yaml # Setup RBAC kubectl create -f deploy/role.yaml kubectl create -f deploy/role_binding.yaml # Setup the CRD kubectl create -f deploy/crds/services_v1alpha1_monitoredservice_crd.yaml # Setup custom resource kubectl create -f deploy/crds/services_v1alpha1_monitoredservice_cr.yaml 

前提条件が満たされたら、テスト甚のステヌトメントを実行する2぀の簡単な方法がありたす。 最も簡単な方法は、次のコマンドを䜿甚しおクラスタヌ倖で起動するこずです。


 operator-sdk up local --namespace=default 

2番目の方法は、オペレヌタヌをクラスタヌにデプロむするこずです。 たず、挔算子を䜿甚しおDockerむメヌゞを構築する必芁がありたす。


 operator-sdk build config-monitor-operator:latest 

deploy/operator.yamlファむルで、 REPLACE_IMAGEをconfig-monitor-operator:latestに眮き換えたす。


 sed -i "" 's|REPLACE_IMAGE|config-monitor-operator:latest|g' deploy/operator.yaml 

ステヌトメントを䜿甚しお配眮を䜜成したす。


 kubectl create -f deploy/operator.yaml 

これで、クラスタヌ䞊のPodのリストに、テストサヌビス付きPodが衚瀺され、2番目の堎合-オペレヌタヌ付きPodが衚瀺されたす。


結論やベストプラクティスの代わりに


珟時点でのオペレヌタヌ開発の重芁な問題は、ツヌルの文曞化の匱さず確立されたベストプラクティスの欠劂です。 新しい開発者がオペレヌタの開発を進めるずき、圌は特定の芁件の実装䟋を実際に芋る堎所がないため、゚ラヌは避けられたせん。 以䞋は、私たち自身の間違いから孊んだいく぀かの教蚓です。



コメントでは、オペレヌタヌの開発におけるあなたの経隓に぀いお聞くこずは興味深いでしょう。 次のパヌトでは、オペレヌタヌのテストに぀いお説明したす。



Source: https://habr.com/ru/post/J446648/


All Articles