5.3. Go ベースの Operator
5.3.1. Go ベースの Operator の Operator SDK の使用を開始する
Operator SDK によって提供されるツールおよびライブラリーを使用して Go ベースの Operator をセットアップし、実行することに関連した基本内容を示すには、Operator 開発者は Go ベースの Memcached の Operator のサンプル、分散キー/値のストアをビルドして、クラスターへデプロイすることができます。
5.3.1.1. 前提条件
- Operator SDK CLI がインストールされている。
-
OpenShift CLI (
oc
) v4.11 以降がインストールされている - Go v1.18 以降
-
cluster-admin
パーミッションを持つアカウントを使用して、oc
で OpenShift Container Platform 4.11 クラスターにログインしている - クラスターがイメージをプルできるように、イメージをプッシュするリポジトリーを public として設定するか、イメージプルシークレットを設定している。
5.3.1.2. Go ベースの Operator の作成およびデプロイ
Operator SDK を使用して Memcached の単純な Go ベースの Operator をビルドし、デプロイできます。
手順
プロジェクトを作成します。
プロジェクトディレクトリーを作成します。
$ mkdir memcached-operator
プロジェクトディレクトリーに移動します。
$ cd memcached-operator
operator-sdk init
コマンドを実行してプロジェクトを初期化します。$ operator-sdk init \ --domain=example.com \ --repo=github.com/example-inc/memcached-operator
このコマンドは、デフォルトで Go プラグインを使用します。
API を作成します。
単純な Memcached API を作成します。
$ operator-sdk create api \ --resource=true \ --controller=true \ --group cache \ --version v1 \ --kind Memcached
Operator イメージをビルドし、プッシュします。
デフォルトの
Makefile
ターゲットを使用して Operator をビルドし、プッシュします。プッシュ先となるレジストリーを使用するイメージのプル仕様を使用してIMG
を設定します。$ make docker-build docker-push IMG=<registry>/<user>/<image_name>:<tag>
Operator を実行します。
CRD をインストールします。
$ make install
プロジェクトをクラスターにデプロイします。
IMG
をプッシュしたイメージに設定します。$ make deploy IMG=<registry>/<user>/<image_name>:<tag>
サンプルカスタムリソース (CR) を作成します。
サンプル CR を作成します。
$ oc apply -f config/samples/cache_v1_memcached.yaml \ -n memcached-operator-system
Operator を調整する CR を確認します。
$ oc logs deployment.apps/memcached-operator-controller-manager \ -c manager \ -n memcached-operator-system
CR を削除する
次のコマンドを実行して CR を削除します。
$ oc delete -f config/samples/cache_v1_memcached -n memcached-operator-system
クリーンアップします。
以下のコマンドを実行して、この手順の一部として作成されたリソースをクリーンアップします。
$ make undeploy
5.3.1.3. 次のステップ
- Go ベースの Operator のビルドに関する詳細な手順は、Go ベースの Operator の Operator SDK チュートリアル を参照してください。
5.3.2. Go ベースの Operator の Operator SDK チュートリアル
Operator 開発者は、Operator SDK での Go プログラミング言語のサポートを利用して、Go ベースの Memcached Operator のサンプルをビルドして、分散キー/値のストアを作成し、そのライフサイクルを管理することができます。
このプロセスは、Operator Framework の 2 つの重要な設定要素を使用して実行されます。
- Operator SDK
-
operator-sdk
CLI ツールおよびcontroller-runtime
ライブラリー API - Operator Lifecycle Manager (OLM)
- クラスター上の Operator のインストール、アップグレード、ロールベースのアクセス制御 (RBAC)
このチュートリアルでは、Go ベースの Operator の Operator SDK の使用を開始する よりも詳細に説明します。
5.3.2.1. 前提条件
- Operator SDK CLI がインストールされている。
-
OpenShift CLI (
oc
) v4.11 以降がインストールされている - Go v1.18 以降
-
cluster-admin
パーミッションを持つアカウントを使用して、oc
で OpenShift Container Platform 4.11 クラスターにログインしている - クラスターがイメージをプルできるように、イメージをプッシュするリポジトリーを public として設定するか、イメージプルシークレットを設定している。
5.3.2.2. プロジェクトの作成
Operator SDK CLI を使用して memcached-operator
というプロジェクトを作成します。
手順
プロジェクトのディレクトリーを作成します。
$ mkdir -p $HOME/projects/memcached-operator
ディレクトリーに切り替えます。
$ cd $HOME/projects/memcached-operator
Go モジュールのサポートをアクティブにします。
$ export GO111MODULE=on
operator-sdk init
コマンドを実行してプロジェクトを初期化します。$ operator-sdk init \ --domain=example.com \ --repo=github.com/example-inc/memcached-operator
注記operator-sdk init
コマンドは、デフォルトで Go プラグインを使用します。operator-sdk init
コマンドは、Go モジュール と使用するgo.mod
ファイルを生成します。生成されるファイルには有効なモジュールパスが必要であるため、$GOPATH/src/
外のプロジェクトを作成する場合は、--repo
フラグが必要です。
5.3.2.2.1. PROJECT ファイル
operator-sdk init
コマンドで生成されるファイルの 1 つに、Kubebuilder の PROJECT
ファイルがあります。プロジェクトルートから実行される後続の operator-sdk
コマンドおよび help
出力は、このファイルを読み取り、プロジェクトタイプが Go であることを認識しています。以下に例を示します。
domain: example.com layout: - go.kubebuilder.io/v3 projectName: memcached-operator repo: github.com/example-inc/memcached-operator version: "3" plugins: manifests.sdk.operatorframework.io/v2: {} scorecard.sdk.operatorframework.io/v2: {} sdk.x-openshift.io/v1: {}
5.3.2.2.2. Manager について
Operator の主なプログラムは、Manager を初期化して実行する main.go
ファイルです。Manager はすべてのカスタムリソース (CR) API 定義の Scheme を自動的に登録し、コントローラーおよび Webhook を設定して実行します。
Manager は、すべてのコントローラーがリソースの監視をする namespace を制限できます。
mgr, err := ctrl.NewManager(cfg, manager.Options{Namespace: namespace})
デフォルトで、Manager は Operator が実行される namespace を監視します。すべての namespace を確認するには、namespace
オプションを空のままにすることができます。
mgr, err := ctrl.NewManager(cfg, manager.Options{Namespace: ""})
MultiNamespacedCacheBuilder
関数を使用して、特定の namespace セットを監視することもできます。
var namespaces []string 1 mgr, err := ctrl.NewManager(cfg, manager.Options{ 2 NewCache: cache.MultiNamespacedCacheBuilder(namespaces), })
5.3.2.2.3. 複数グループ API について
API およびコントローラーを作成する前に、Operator に複数の API グループが必要かどうかを検討してください。このチュートリアルでは、単一グループ API のデフォルトケースについて説明しますが、複数グループ API をサポートするようにプロジェクトのレイアウトを変更するには、以下のコマンドを実行します。
$ operator-sdk edit --multigroup=true
このコマンドにより、PROJECT
ファイルが更新されます。このファイルは、以下の例のようになります。
domain: example.com layout: go.kubebuilder.io/v3 multigroup: true ...
複数グループプロジェクトの場合、API Go タイプのファイルが apis/<group>/<version>/
ディレクトリーに作成され、コントローラーは controllers/<group>/
ディレクトリーに作成されます。続いて、Dockerfile が適宜更新されます。
追加リソース
- 複数グループのプロジェクトへの移行に関する詳細は、Kubebuilder のドキュメント を参照してください。
5.3.2.3. API およびコントローラーの作成
Operator SDK CLI を使用してカスタムリソース定義 (CRD) API およびコントローラーを作成します。
手順
以下のコマンドを実行して、グループ
cache
、バージョン、v1
、および種類Memcached
を指定して API を作成します。$ operator-sdk create api \ --group=cache \ --version=v1 \ --kind=Memcached
プロンプトが表示されたら
y
を入力し、リソースとコントローラーの両方を作成します。Create Resource [y/n] y Create Controller [y/n] y
出力例
Writing scaffold for you to edit... api/v1/memcached_types.go controllers/memcached_controller.go ...
このプロセスでは、api/v1/memcached_types.go
で Memcached
リソース API が生成され、controllers/memcached_controller.go
でコントローラーが生成されます。
5.3.2.3.1. API の定義
Memcached
カスタムリソース (CR) の API を定義します。
手順
api/v1/memcached_types.go
で Go タイプの定義を変更し、以下のspec
およびstatus
を追加します。// MemcachedSpec defines the desired state of Memcached type MemcachedSpec struct { // +kubebuilder:validation:Minimum=0 // Size is the size of the memcached deployment Size int32 `json:"size"` } // MemcachedStatus defines the observed state of Memcached type MemcachedStatus struct { // Nodes are the names of the memcached pods Nodes []string `json:"nodes"` }
リソースタイプ用に生成されたコードを更新します。
$ make generate
ヒント*_types.go
ファイルの変更後は、make generate
コマンドを実行し、該当するリソースタイプ用に生成されたコードを更新する必要があります。上記の Makefile ターゲットは
controller-gen
ユーティリティーを呼び出して、api/v1/zz_generated.deepcopy.go
ファイルを更新します。これにより、API Go タイプの定義は、すべての Kind タイプが実装する必要のあるruntime.Object
インターフェイスを実装します。
5.3.2.3.2. CRD マニフェストの生成
API が spec
フィールドと status
フィールドおよびカスタムリソース定義 (CRD) 検証マーカーで定義された後に、CRD マニフェストを生成できます。
手順
以下のコマンドを実行し、CRD マニフェストを生成して更新します。
$ make manifests
この Makefile ターゲットは
controller-gen
ユーティリティーを呼び出し、config/crd/bases/cache.example.com_memcacheds.yaml
ファイルに CRD マニフェストを生成します。
5.3.2.3.2.1. OpenAPI 検証
OpenAPIv3 スキーマは、マニフェストの生成時に spec.validation
ブロックの CRD マニフェストに追加されます。この検証ブロックにより、Kubernetes が作成または更新時に Memcached CR のプロパティーを検証できます。
API の検証を設定するには、マーカーまたはアノテーションを使用できます。これらのマーカーには、+kubebuilder:validation
接頭辞が常にあります。
関連情報
API コードでのマーカーの使用に関する詳細は、以下の Kubebuilder ドキュメントを参照してください。
- CRD の OpenAPIv3 検証スキーマに関する詳細は、Kubernetes のドキュメント を参照してください。
5.3.2.4. コントローラーの実装
新規 API およびコントローラーの作成後に、コントローラーロジックを実装することができます。
手順
この例では、生成されたコントローラーファイル
controllers/memcached_controller.go
を以下の実装例に置き換えます。例5.1
memcached_controller.go
の例/* Copyright 2020. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package controllers import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "reflect" "context" "github.com/go-logr/logr" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" cachev1 "github.com/example-inc/memcached-operator/api/v1" ) // MemcachedReconciler reconciles a Memcached object type MemcachedReconciler struct { client.Client Log logr.Logger Scheme *runtime.Scheme } // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list; // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // TODO(user): Modify the Reconcile function to compare the state specified by // the Memcached object against the actual cluster state, and then // perform operations to make the cluster state reflect the state specified by // the user. // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.7.0/pkg/reconcile func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { //log := r.Log.WithValues("memcached", req.NamespacedName) log := ctrllog.FromContext(ctx) // Fetch the Memcached instance memcached := &cachev1.Memcached{} err := r.Get(ctx, req.NamespacedName, memcached) 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 log.Info("Memcached resource not found. Ignoring since object must be deleted") return ctrl.Result{}, nil } // Error reading the object - requeue the request. log.Error(err, "Failed to get Memcached") return ctrl.Result{}, err } // Check if the deployment already exists, if not create a new one found := &appsv1.Deployment{} err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found) if err != nil && errors.IsNotFound(err) { // Define a new deployment dep := r.deploymentForMemcached(memcached) log.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name) err = r.Create(ctx, dep) if err != nil { log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name) return ctrl.Result{}, err } // Deployment created successfully - return and requeue return ctrl.Result{Requeue: true}, nil } else if err != nil { log.Error(err, "Failed to get Deployment") return ctrl.Result{}, err } // Ensure the deployment size is the same as the spec size := memcached.Spec.Size if *found.Spec.Replicas != size { found.Spec.Replicas = &size err = r.Update(ctx, found) if err != nil { log.Error(err, "Failed to update Deployment", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name) return ctrl.Result{}, err } // Spec updated - return and requeue return ctrl.Result{Requeue: true}, nil } // Update the Memcached status with the pod names // List the pods for this memcached's deployment podList := &corev1.PodList{} listOpts := []client.ListOption{ client.InNamespace(memcached.Namespace), client.MatchingLabels(labelsForMemcached(memcached.Name)), } if err = r.List(ctx, podList, listOpts...); err != nil { log.Error(err, "Failed to list pods", "Memcached.Namespace", memcached.Namespace, "Memcached.Name", memcached.Name) return ctrl.Result{}, err } podNames := getPodNames(podList.Items) // Update status.Nodes if needed if !reflect.DeepEqual(podNames, memcached.Status.Nodes) { memcached.Status.Nodes = podNames err := r.Status().Update(ctx, memcached) if err != nil { log.Error(err, "Failed to update Memcached status") return ctrl.Result{}, err } } return ctrl.Result{}, nil } // deploymentForMemcached returns a memcached Deployment object func (r *MemcachedReconciler) deploymentForMemcached(m *cachev1.Memcached) *appsv1.Deployment { ls := labelsForMemcached(m.Name) replicas := m.Spec.Size dep := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: m.Name, Namespace: m.Namespace, }, Spec: appsv1.DeploymentSpec{ Replicas: &replicas, Selector: &metav1.LabelSelector{ MatchLabels: ls, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: ls, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{{ Image: "memcached:1.4.36-alpine", Name: "memcached", Command: []string{"memcached", "-m=64", "-o", "modern", "-v"}, Ports: []corev1.ContainerPort{{ ContainerPort: 11211, Name: "memcached", }}, }}, }, }, }, } // Set Memcached instance as the owner and controller ctrl.SetControllerReference(m, dep, r.Scheme) return dep } // labelsForMemcached returns the labels for selecting the resources // belonging to the given memcached CR name. func labelsForMemcached(name string) map[string]string { return map[string]string{"app": "memcached", "memcached_cr": name} } // getPodNames returns the pod names of the array of pods passed in func getPodNames(pods []corev1.Pod) []string { var podNames []string for _, pod := range pods { podNames = append(podNames, pod.Name) } return podNames } // SetupWithManager sets up the controller with the Manager. func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&cachev1.Memcached{}). Owns(&appsv1.Deployment{}). Complete(r) }
コントローラーのサンプルは、それぞれの
Memcached
カスタムリソース (CR) について以下の調整 (reconciliation) ロジックを実行します。- Memcached デプロイメントを作成します (ない場合)。
-
デプロイメントのサイズが、
Memcached
CR 仕様で指定されたものと同じであることを確認します。 -
Memcached
CR ステータスをmemcached
Pod の名前に置き換えます。
次のサブセクションでは、実装例のコントローラーがリソースを監視する方法と reconcile ループがトリガーされる方法を説明しています。これらのサブセクションを省略し、直接 Operator の実行 に進むことができます。
5.3.2.4.1. コントローラーによって監視されるリソース
controllers/memcached_controller.go
の SetupWithManager()
関数は、CR およびコントローラーによって所有され、管理される他のリソースを監視するようにコントローラーがビルドされる方法を指定します。
import ( ... appsv1 "k8s.io/api/apps/v1" ... ) func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&cachev1.Memcached{}). Owns(&appsv1.Deployment{}). Complete(r) }
NewControllerManagedBy()
は、さまざまなコントローラー設定を可能にするコントローラービルダーを提供します。
For(&cachev1.Memcached{})
は、監視するプライマリーリソースとして Memcached
タイプを指定します。Memcached
タイプのそれぞれの Add、Update、または Delete イベントの場合、reconcile ループに Memcached
オブジェクトの (namespace および name キーから成る) reconcile Request
引数が送られます。
Owns(&appsv1.Deployment{})
は、監視するセカンダリーリソースとして Deployment
タイプを指定します。Add、Update、または Delete イベントの各 Deployment
タイプの場合、イベントハンドラーは各イベントを、デプロイメントのオーナーの reconcile request にマップします。この場合、デプロイメントが作成された Memcached
オブジェクトがオーナーです。
5.3.2.4.2. コントローラーの設定
多くの他の便利な設定を使用すると、コントローラーを初期化できます。以下に例を示します。
MaxConcurrentReconciles
オプションを使用して、コントローラーの同時調整の最大数を設定します。デフォルトは1
です。func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&cachev1.Memcached{}). Owns(&appsv1.Deployment{}). WithOptions(controller.Options{ MaxConcurrentReconciles: 2, }). Complete(r) }
- 述語を使用した監視イベントをフィルタリングします。
-
EventHandler のタイプを選択し、監視イベントが reconcile ループの reconcile request に変換する方法を変更します。プライマリーリソースおよびセカンダリーリソースよりも複雑な Operator 関係の場合は、
EnqueueRequestsFromMapFunc
ハンドラーを使用して、監視イベントを任意の reconcile request のセットに変換することができます。
これらの設定およびその他の設定に関する詳細は、アップストリームの Builder および Controller の GoDocs を参照してください。
5.3.2.4.3. reconcile ループ
すべてのコントローラーには、reconcile ループを実装する Reconcile()
メソッドのある reconciler オブジェクトがあります。この reconcile ループには、キャッシュからプライマリーリソースオブジェクトの Memcached
を検索するために使用される namespace および name キーである Request
引数が渡されます。
import ( ctrl "sigs.k8s.io/controller-runtime" cachev1 "github.com/example-inc/memcached-operator/api/v1" ... ) func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // Lookup the Memcached instance for this reconcile request memcached := &cachev1.Memcached{} err := r.Get(ctx, req.NamespacedName, memcached) ... }
返り値、結果、およびエラーに基づいて、Request は再度キューに入れられ、reconcile ループが再びトリガーされる可能性があります。
// Reconcile successful - don't requeue return ctrl.Result{}, nil // Reconcile failed due to error - requeue return ctrl.Result{}, err // Requeue for any reason other than an error return ctrl.Result{Requeue: true}, nil
Result.RequeueAfter
を設定して、猶予期間後にも要求を再びキューに入れることができます。
import "time" // Reconcile for any reason other than an error after 5 seconds return ctrl.Result{RequeueAfter: time.Second*5}, nil
RequeueAfter
を定期的な CR の調整に設定している Result
を返すことができます。
reconciler、クライアント、およびリソースイベントとの対話に関する詳細は、Controller Runtime Client API のドキュメントを参照してください。
5.3.2.4.4. パーミッションおよび RBAC マニフェスト
コントローラーには、管理しているリソースと対話するために特定の RBAC パーミッションが必要です。これらは、以下のような RBAC マーカーを使用して指定されます。
// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list; func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { ... }
config/rbac/role.yaml
の ClusterRole
オブジェクトマニフェストは、make manifests
コマンドが実行されるたびに controller-gen
ユーティリティーを使用して、以前のマーカーから生成されます。
5.3.2.5. プロキシーサポートの有効化
Operator の作成者は、ネットワークプロキシーをサポートする Operator を開発できるようになりました。クラスター管理者は、Operator Lifecycle Manager (OLM) によって処理される環境変数のプロキシーサポートを設定します。Operator は以下の標準プロキシー変数の環境を検査し、値をオペランドに渡して、プロキシーされたクラスターをサポートする必要があります。
-
HTTP_PROXY
-
HTTPS_PROXY
-
NO_PROXY
このチュートリアルでは、HTTP_PROXY
を環境変数の例として使用します。
前提条件
- クラスター全体の egress プロキシーが有効にされているクラスター。
手順
controllers/memcached_controller.go
ファイルを編集し、以下のパラメーターを追加します。operator-lib
ライブラリーからproxy
パッケージをインポートします。import ( ... "github.com/operator-framework/operator-lib/proxy" )
proxy.ReadProxyVarsFromEnv
helper 関数を調整ループに、結果をオペランド環境に追加します。for i, container := range dep.Spec.Template.Spec.Containers { dep.Spec.Template.Spec.Containers[i].Env = append(container.Env, proxy.ReadProxyVarsFromEnv()...) } ...
以下を
config/manager/manager.yaml
ファイルに追加して、Operator デプロイメントに環境変数を設定します。containers: - args: - --leader-elect - --leader-election-id=ansible-proxy-demo image: controller:latest name: manager env: - name: "HTTP_PROXY" value: "http_proxy_test"
5.3.2.6. Operator の実行
Operator SDK CLI を使用して Operator をビルドし、実行する方法は 3 つあります。
- クラスター外で Go プログラムとしてローカルに実行します。
- クラスター上のデプロイメントとして実行します。
- Operator をバンドルし、Operator Lifecycle Manager (OLM) を使用してクラスター上にデプロイします。
Go ベースの Operator を OpenShift Container Platform でのデプロイメントとして、または OLM を使用するバンドルとして実行する前に、プロジェクトがサポートされているイメージを使用するように更新されていることを確認します。
5.3.2.6.1. クラスター外でローカルに実行する。
Operator プロジェクトをクラスター外の Go プログラムとして実行できます。これは、デプロイメントとテストを迅速化するという開発目的において便利です。
手順
以下のコマンドを実行して、
~/.kube/config
ファイルに設定されたクラスターにカスタムリソース定義 (CRD) をインストールし、Operator をローカルで実行します。$ make install run
出力例
... 2021-01-10T21:09:29.016-0700 INFO controller-runtime.metrics metrics server is starting to listen {"addr": ":8080"} 2021-01-10T21:09:29.017-0700 INFO setup starting manager 2021-01-10T21:09:29.017-0700 INFO controller-runtime.manager starting metrics server {"path": "/metrics"} 2021-01-10T21:09:29.018-0700 INFO controller-runtime.manager.controller.memcached Starting EventSource {"reconciler group": "cache.example.com", "reconciler kind": "Memcached", "source": "kind source: /, Kind="} 2021-01-10T21:09:29.218-0700 INFO controller-runtime.manager.controller.memcached Starting Controller {"reconciler group": "cache.example.com", "reconciler kind": "Memcached"} 2021-01-10T21:09:29.218-0700 INFO controller-runtime.manager.controller.memcached Starting workers {"reconciler group": "cache.example.com", "reconciler kind": "Memcached", "worker count": 1}
5.3.2.6.2. クラスター上でのデプロイメントとしての実行
Operator プロジェクトは、クラスター上でデプロイメントとして実行できます。
前提条件
- プロジェクトを更新してサポートされるイメージを使用することで、OpenShift Container Platform で実行する Go ベースの Operator が準備済みである。
手順
以下の
make
コマンドを実行して Operator イメージをビルドし、プッシュします。以下の手順のIMG
引数を変更して、アクセス可能なリポジトリーを参照します。Quay.io などのリポジトリーサイトにコンテナーを保存するためのアカウントを取得できます。イメージをビルドします。
$ make docker-build IMG=<registry>/<user>/<image_name>:<tag>
注記Operator の SDK によって生成される Dockerfile は、
go build
についてGOARCH=amd64
を明示的に参照します。これは、AMD64 アーキテクチャー以外の場合はGOARCH=$TARGETARCH
に修正できます。Docker は、-platform
で指定された値に環境変数を自動的に設定します。Buildah では、そのために-build-arg
を使用する必要があります。詳細は、Multiple Architectures を参照してください。イメージをリポジトリーにプッシュします。
$ make docker-push IMG=<registry>/<user>/<image_name>:<tag>
注記両方のコマンドのイメージの名前とタグ (例:
IMG=<registry>/<user>/<image_name>:<tag>
) を Makefile に設定することもできます。IMG ?= controller:latest
の値を変更して、デフォルトのイメージ名を設定します。
以下のコマンドを実行して Operator をデプロイします。
$ make deploy IMG=<registry>/<user>/<image_name>:<tag>
デフォルトで、このコマンドは
<project_name>-system
の形式で Operator プロジェクトの名前で namespace を作成し、デプロイメントに使用します。このコマンドは、config/rbac
から RBAC マニフェストもインストールします。以下のコマンドを実行して、Operator が実行されていることを確認します。
$ oc get deployment -n <project_name>-system
出力例
NAME READY UP-TO-DATE AVAILABLE AGE <project_name>-controller-manager 1/1 1 1 8m
5.3.2.6.3. Operator のバンドルおよび Operator Lifecycle Manager を使用したデプロイ
5.3.2.6.3.1. Operator のバンドル
Operator Bundle Format は、Operator SDK および Operator Lifecycle Manager (OLM) のデフォルトパッケージ方法です。Operator SDK を使用して OLM に対して Operator を準備し、バンドルイメージをとして Operator プロジェクトをビルドしてプッシュできます。
前提条件
- 開発ワークステーションに Operator SDK CLI がインストールされている。
-
OpenShift CLI (
oc
) v4.11 以降がインストールされている - Operator プロジェクトが Operator SDK を使用して初期化されている。
- Operator が Go ベースの場合、プロジェクトを更新して OpenShift Container Platform での実行をサポートするイメージを使用する必要がある。
手順
以下の
make
コマンドを Operator プロジェクトディレクトリーで実行し、Operator イメージをビルドし、プッシュします。以下の手順のIMG
引数を変更して、アクセス可能なリポジトリーを参照します。Quay.io などのリポジトリーサイトにコンテナーを保存するためのアカウントを取得できます。イメージをビルドします。
$ make docker-build IMG=<registry>/<user>/<operator_image_name>:<tag>
注記Operator の SDK によって生成される Dockerfile は、
go build
についてGOARCH=amd64
を明示的に参照します。これは、AMD64 アーキテクチャー以外の場合はGOARCH=$TARGETARCH
に修正できます。Docker は、-platform
で指定された値に環境変数を自動的に設定します。Buildah では、そのために-build-arg
を使用する必要があります。詳細は、Multiple Architectures を参照してください。イメージをリポジトリーにプッシュします。
$ make docker-push IMG=<registry>/<user>/<operator_image_name>:<tag>
Operator SDK
generate bundle
およびbundle validate
のサブコマンドを含む複数のコマンドを呼び出すmake bundle
コマンドを実行し、Operator バンドルマニフェストを作成します。$ make bundle IMG=<registry>/<user>/<operator_image_name>:<tag>
Operator のバンドルマニフェストは、アプリケーションを表示し、作成し、管理する方法を説明します。
make bundle
コマンドは、以下のファイルおよびディレクトリーを Operator プロジェクトに作成します。-
ClusterServiceVersion
オブジェクトを含むbundle/manifests
という名前のバンドルマニフェストディレクトリー -
bundle/metadata
という名前のバンドルメタデータディレクトリー -
config/crd
ディレクトリー内のすべてのカスタムリソース定義 (CRD) -
Dockerfile
bundle.Dockerfile
続いて、これらのファイルは
operator-sdk bundle validate
を使用して自動的に検証され、ディスク上のバンドル表現が正しいことを確認します。-
以下のコマンドを実行し、バンドルイメージをビルドしてプッシュします。OLM は、1 つ以上のバンドルイメージを参照するインデックスイメージを使用して Operator バンドルを使用します。
バンドルイメージをビルドします。イメージをプッシュしようとするレジストリー、ユーザー namespace、およびイメージタグの詳細で
BUNDLE_IMG
を設定します。$ make bundle-build BUNDLE_IMG=<registry>/<user>/<bundle_image_name>:<tag>
バンドルイメージをプッシュします。
$ docker push <registry>/<user>/<bundle_image_name>:<tag>
5.3.2.6.3.2. Operator Lifecycle Manager を使用した Operator のデプロイ
Operator Lifecycle Manager (OLM) は、Kubernetes クラスターで Operator (およびそれらの関連サービス) をインストールし、更新し、ライフサイクルを管理するのに役立ちます。OLM はデフォルトで OpenShift Container Platform にインストールされ、Kubernetes 拡張として実行されるため、追加のツールなしにすべての Operator のライフサイクル管理機能に Web コンソールおよび OpenShift CLI (oc
) を使用できます。
Operator Bundle Format は、Operator SDK および OLM のデフォルトパッケージ方法です。Operator SDK を使用して OLM でバンドルイメージを迅速に実行し、適切に実行されるようにできます。
前提条件
- 開発ワークステーションに Operator SDK CLI がインストールされている。
- Operator バンドルイメージがビルドされ、レジストリーにプッシュされている。
-
(OpenShift Container Platform 4.11 など、
apiextensions.k8s.io/v1
CRD を使用する場合は v1.16.0 以降の) Kubernetes ベースのクラスターに OLM がインストールされていること。 -
cluster-admin
パーミッションのあるアカウントを使用してoc
でクラスターへログインしていること。 - Operator が Go ベースの場合、プロジェクトを更新して OpenShift Container Platform での実行をサポートするイメージを使用する必要がある。
手順
以下のコマンドを入力してクラスターで Operator を実行します。
$ operator-sdk run bundle \1 -n <namespace> \2 <registry>/<user>/<bundle_image_name>:<tag> 3
- 1
run bundle
コマンドは、有効なファイルベースのカタログを作成し、OLM を使用して Operator バンドルをクラスターにインストールします。- 2
- オプション: デフォルトで、このコマンドは
~/.kube/config
ファイルの現在アクティブなプロジェクトに Operator をインストールします。-n
フラグを追加して、インストールに異なる namespace スコープを設定できます。 - 3
- イメージを指定しない場合、コマンドは
quay.io/operator-framework/opm:latest
をデフォルトのインデックスイメージとして使用します。イメージを指定した場合は、コマンドはバンドルイメージ自体をインデックスイメージとして使用します。
重要OpenShift Container Platform 4.11 の時点で、Operator カタログに関して、
run bundle
コマンドはデフォルトでファイルベースのカタログ形式をサポートします。Operator カタログに関して、非推奨の SQLite データベース形式は引き続きサポートされますが、今後のリリースで削除される予定です。Operator の作成者はワークフローをファイルベースのカタログ形式に移行することが推奨されます。このコマンドにより、以下のアクションが行われます。
- バンドルイメージをインジェクトしてインデックスイメージを作成します。インデックスイメージは不透明で一時的なものですが、バンドルを実稼働環境でカタログに追加する方法を正確に反映します。
- 新規インデックスイメージを参照するカタログソースを作成します。これにより、OperatorHub が Operator を検出できるようになります。
-
OperatorGroup
、Subscription
、InstallPlan
、および RBAC を含むその他の必要なリソースすべてを作成して、Operator をクラスターにデプロイします。
5.3.2.7. カスタムリソースの作成
Operator のインストール後に、Operator によってクラスターに提供されるカスタムリソース (CR) を作成して、これをテストできます。
前提条件
-
クラスターにインストールされている
Memcached
CR を提供する Memcached Operator の例
手順
Operator がインストールされている namespace へ変更します。たとえば、
make deploy
コマンドを使用して Operator をデプロイした場合は、以下のようになります。$ oc project memcached-operator-system
config/samples/cache_v1_memcached.yaml
でMemcached
CR マニフェストのサンプルを編集し、以下の仕様が含まれるようにします。apiVersion: cache.example.com/v1 kind: Memcached metadata: name: memcached-sample ... spec: ... size: 3
CR を作成します。
$ oc apply -f config/samples/cache_v1_memcached.yaml
Memcached
Operator が、正しいサイズで CR サンプルのデプロイメントを作成することを確認します。$ oc get deployments
出力例
NAME READY UP-TO-DATE AVAILABLE AGE memcached-operator-controller-manager 1/1 1 1 8m memcached-sample 3/3 3 3 1m
ステータスが Memcached Pod 名で更新されていることを確認するために、Pod および CR ステータスを確認します。
Pod を確認します。
$ oc get pods
出力例
NAME READY STATUS RESTARTS AGE memcached-sample-6fd7c98d8-7dqdr 1/1 Running 0 1m memcached-sample-6fd7c98d8-g5k7v 1/1 Running 0 1m memcached-sample-6fd7c98d8-m7vn7 1/1 Running 0 1m
CR ステータスを確認します。
$ oc get memcached/memcached-sample -o yaml
出力例
apiVersion: cache.example.com/v1 kind: Memcached metadata: ... name: memcached-sample ... spec: size: 3 status: nodes: - memcached-sample-6fd7c98d8-7dqdr - memcached-sample-6fd7c98d8-g5k7v - memcached-sample-6fd7c98d8-m7vn7
デプロイメントサイズを更新します。
config/samples/cache_v1_memcached.yaml
ファイルを更新し、Memcached
CR のspec.size
フィールドを3
から5
に変更します。$ oc patch memcached memcached-sample \ -p '{"spec":{"size": 5}}' \ --type=merge
Operator がデプロイメントサイズを変更することを確認します。
$ oc get deployments
出力例
NAME READY UP-TO-DATE AVAILABLE AGE memcached-operator-controller-manager 1/1 1 1 10m memcached-sample 5/5 5 5 3m
次のコマンドを実行して CR を削除します。
$ oc delete -f config/samples/cache_v1_memcached.yaml
このチュートリアルの一環として作成したリソースをクリーンアップします。
Operator のテストに
make deploy
コマンドを使用した場合は、以下のコマンドを実行します。$ make undeploy
Operator のテストに
operator-sdk run bundle
コマンドを使用した場合は、以下のコマンドを実行します。$ operator-sdk cleanup <project_name>
5.3.2.8. 関連情報
- Operator SDK によって作成されるディレクトリー構造の詳細は、Go ベースの Operator のプロジェクトレイアウト を参照してください。
- クラスター全体の egress プロキシーが設定されている場合 に、クラスター管理者は Operator Lifecycle Manager(OLM) で実行される特定の Operator の プロキシー設定を上書きしたり、 カスタム CA 証明書を挿入したり できます。
5.3.3. Go ベースの Operator のプロジェクトレイアウト
operator-sdk
CLI は、各 Operator プロジェクトに多数のパッケージおよびファイルを生成、または スキャフォールディング することができます。
5.3.3.1. Go ベースのプロジェクトレイアウト
operator-sdk init
コマンドを使用して生成される Go ベースの Operator プロジェクト (デフォルトタイプ) には、以下のディレクトリーおよびファイルが含まれます。
ファイルまたはディレクトリー | 目的 |
---|---|
|
Operator のメインプログラム。これは、 |
|
CRD の API を定義するディレクトリーツリー。 |
|
コントローラーの実装。 |
| クラスターにコントローラーをデプロイするために使用される Kubernetes マニフェスト (CRD、RBAC、および証明書を含む)。 |
| コントローラーのビルドおよびデプロイに使用するターゲット。 |
| コンテナーエンジンが Operator をビルドするために使用する手順。 |
| CRD の登録、RBAC のセットアップ、およびデプロイメントとして Operator のデプロイをする Kubernetes マニフェスト。 |
5.3.4. 新しい Operator SDK バージョンの Go ベースの Operator プロジェクトの更新
OpenShift Container Platform 4.11 は Operator SDK 1.22.2 をサポートします。ワークステーションに 1.16.0 CLI がすでにインストールされている場合は、最新バージョンをインストール して CLI を 1.22.2 に更新できます。
ただし、既存の Operator プロジェクトが Operator SDK 1.22.2 との互換性を維持するには、1.16.0 以降に導入された関連する重大な変更に対し、更新手順を実行する必要があります。アップグレードの手順は、以前は 1.16.0 で作成または維持されている Operator プロジェクトのいずれかで手動で実行する必要があります。
5.3.4.1. Operator SDK 1.22.2 の Go ベースの Operator プロジェクトの更新
次の手順では、1.22.2 との互換性を確保するため、既存の Go ベースの Operator プロジェクトを更新します。
前提条件
- Operator SDK 1.22.2 がインストールされている
- Operator プロジェクトが Operator SDK 1.16.0 で作成または保守されている。
手順
config/default/manager_auth_proxy_patch.yaml
ファイルに以下の変更を加えます。... spec: template: spec: containers: - name: kube-rbac-proxy image: registry.redhat.io/openshift4/ose-kube-rbac-proxy:v4.11 1 args: - "--secure-listen-address=0.0.0.0:8443" - "--upstream=http://127.0.0.1:8080/" - "--logtostderr=true" - "--v=0" 2 ... resources: limits: cpu: 500m memory: 128Mi requests: cpu: 5m memory: 64Mi 3
Makefile
に以下の変更を加えます。以下の環境変数を
Makefile
に追加して、イメージダイジェストのサポートを有効にします。古い
Makefile
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) ...
新しい
Makefile
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) # BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) # USE_IMAGE_DIGESTS defines if images are resolved via tags or digests # You can enable this value if you would like to use SHA Based Digests # To enable set flag to true USE_IMAGE_DIGESTS ?= false ifeq ($(USE_IMAGE_DIGESTS), true) BUNDLE_GEN_FLAGS += --use-image-digests endif
Makefile
を編集して、バンドルターゲットをBUNDLE_GEN_FLAGS
環境変数に置き換えます。古い
Makefile
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
新しい
Makefile
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
Makefile
を編集して、opm
をバージョン 1.23.0 に更新します。.PHONY: opm OPM = ./bin/opm opm: ## Download opm locally if necessary. ifeq (,$(wildcard $(OPM))) ifeq (,$(shell which opm 2>/dev/null)) @{ \ set -e ;\ mkdir -p $(dir $(OPM)) ;\ OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\ 1 chmod +x $(OPM) ;\ } else OPM = $(shell which opm) endif endif
- 1
v1.19.1
をv1.23.0
に置き換えます。
Makefile
を編集して、go get
ターゲットをgo install
ターゲットに置き換えます。古い
Makefile
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen .PHONY: controller-gen controller-gen: ## Download controller-gen locally if necessary. $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0) KUSTOMIZE = $(shell pwd)/bin/kustomize .PHONY: kustomize kustomize: ## Download kustomize locally if necessary. $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) ENVTEST = $(shell pwd)/bin/setup-envtest .PHONY: envtest envtest: ## Download envtest-setup locally if necessary. $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) # go-get-tool will 'go get' any package $2 and install it to $1. PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) define go-get-tool @[ -f $(1) ] || { \ set -e ;\ TMP_DIR=$$(mktemp -d) ;\ cd $$TMP_DIR ;\ go mod init tmp ;\ echo "Downloading $(2)" ;\ GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\ rm -rf $$TMP_DIR ;\ } endef
新しい
Makefile
##@ Build Dependencies ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin $(LOCALBIN): mkdir -p $(LOCALBIN) ## Tool Binaries KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions KUSTOMIZE_VERSION ?= v3.8.7 CONTROLLER_TOOLS_VERSION ?= v0.8.0 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. $(KUSTOMIZE): $(LOCALBIN) curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN) .PHONY: controller-gen controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. $(CONTROLLER_GEN): $(LOCALBIN) GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) .PHONY: envtest envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. $(ENVTEST): $(LOCALBIN) GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
Kubernetes 1.24 をサポートするように、
Makefile
のENVTEST_K8S_VERSION
フィールドおよびcontroller-gen
フィールドを更新します。... ENVTEST_K8S_VERSION = 1.24 1 ... sigs.k8s.io/controller-tools/cmd/controller-gen@v0.9.0 2
Makefile
に変更を適用し、以下のコマンドを入力して Operator を再ビルドします。$ make
go.mod
ファイルに以下の変更を加えて、Go とその依存関係を更新します。go 1.18 1 require ( github.com/onsi/ginkgo v1.16.5 2 github.com/onsi/gomega v1.18.1 3 k8s.io/api v0.24.0 4 k8s.io/apimachinery v0.24.0 5 k8s.io/client-go v0.24.0 6 sigs.k8s.io/controller-runtime v0.12.1 7 )
以下のコマンドを入力して、依存関係をダウンロードしてクリーンアップします。
$ go mod tidy
api/webhook_suitetest.go
およびcontrollers/suite_test.go
スイートテストファイルを使用する場合は、以下の変更を加えます。古いスイートテストファイル
cfg, err := testEnv.Start()
新しいスイートテストファイル
var err error // cfg is defined in this file globally. cfg, err = testEnv.Start()
Kubernetes の宣言プラグインを使用する場合は、以下の変更で Dockerfile を更新します。
COPY controllers/ controllers/
で始まる以下の行に、以下の変更を加えます。# https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern/blob/master/docs/addon/walkthrough/README.md#adding-a-manifest # Stage channels and make readable COPY channels/ /channels/ RUN chmod -R a+rx /channels/
COPY --from=builder /workspace/manager .
で始まる以下の行に、以下の変更を加えます。# copy channels COPY --from=builder /channels /channels