Go言語で Kubernetes Operator を書いてみた

GitHub の [operator-framework/getting-started](https://github.com/operator-framework/getting-started) を、ステップ・バイ・ステップで試してみるが、Create a Memcached CRで、書いてある通りに動かない。 なぜ、オペレーターが GitHubのREADME.md通りに動作しないか理由が解らない。(2019年8月15日現在)

具体的には、カスタム・リソースを適用した後に、kubectl get deployment を実行して、デプロイメント・コントローラーの管理下で、複数のポッドが起動していることを確認している。しかし実際に動かして試すと、ポッドが一個起動するだけで、カスタム・リソースのspec.Size の値を変更しても、ポッドの数が変われない。そこで原因を追求して、動かない理由が解ったので、メモとして残しておく。

これは Qiita [あまり良く知られていない Kubernetes Operator とは？](https://qiita.com/MahoTakara/items/af4ad8ab69c24102bd72)の続きであり、以前の記事は「オペレーターとな何に？」という質問に答えるように書いた。一方、この記事では、さらに一歩進めて、オペレーターのプログラム開発にせまる内容である。


## Operator SDKのインストール

オペレーターの開発のために、Operator SDK[1] があり、その中のコマンド operator-sdk を利用する[2],[3]。

## GO言語の開発環境

オペレーターの開発にはGo言語が必要なので、別途インストールして、環境要件[2]に適合するようにしておく。
Homebrew でインストールした場合、v1.12.9がインストールされるが、v1.12.8にレベルダウンする方法[4]を書き残しておいたので必要に応じて参照いただきたい。

~~~
$ go version
go version go1.12.8 darwin/amd64

$ go env GOPATH
/Users/maho/go

$ export GOPATH=$(go env GOPATH)
$ cd $GOPATH
~~~

## Operator-SDKの導入

macOSの場合は、brew install を利用すると時間の節約ができ便利である[2]。 バージョンは v0.10.0である。バージョン番号から Operator SDK の開発は、まだまだ、道半ばと言った印象を受ける。

~~~
$ operator-sdk version
operator-sdk version: v0.10.0, commit: ff80b17737a6a0aade663e4827e8af3ab5a21170
~~~

## オペレータの足場(scaffold)作成

operator-sdk new <オペレータ名> コマンドを利用することで、コンテナのGo言語ソースコードとビルド用ファイル、K8sのマニフェストと、必要なディレクトリが作成される。

次の実行例では、takara-operatorの足場となるコードを生成したところである。このコマンドによって、オペレータの本体 cmd/manager/main.go が作成された。コードの内容も良く確認しておくと良い。

~~~
$ operator-sdk new takara-operator
INFO[0000] Creating new Go operator 'takara-operator'.  
INFO[0000] Created go.mod                               
INFO[0000] Created tools.go                             
INFO[0000] Created cmd/manager/main.go                  
INFO[0000] Created build/Dockerfile                     
INFO[0000] Created build/bin/entrypoint                 
INFO[0000] Created build/bin/user_setup                 
INFO[0000] Created deploy/service_account.yaml          
INFO[0000] Created deploy/role.yaml                     
INFO[0000] Created deploy/role_binding.yaml             
INFO[0000] Created deploy/operator.yaml                 
INFO[0000] Created pkg/apis/apis.go                     
INFO[0000] Created pkg/controller/controller.go         
INFO[0000] Created version/version.go                   
INFO[0000] Created .gitignore                           
INFO[0000] Validating project                           
go: finding github.com/operator-framework/operator-sdk master
INFO[0005] Project validation successful.               
INFO[0005] Project creation complete.   
~~~

オペレーターのコード cmd/manager/main.go が何をしているのか、箇条書きのメモを残しておく。

func main()の概要を以下の列挙する。コードの頭から順番に初期化処理が流れ、最後にマネージャーを起動して、イベント待ちに入る。この後は、コントローラーが中心に動作することになる。

1. フラグの読み取り
1. デフォルトロガーの設定
1. バージョンのプリント
1. K8s環境の名前空間名の取得
1. apiserverと説臆するためkubeconfig取得
1. 空のコンテキスト生成
1. オペレータのポッドが一個とするために、ポッドのリーダーにする
1. コントローラーを作成するために、新しいマネジャーを作る
1. 全てのリソースをスキーマに追加する
1. 全てのコントローラーをマネージャーに追加する
1. カスタムリソースのメトリックスを提供できるようにセットアップする
1. マネージャーを開始する

検証では、オペレーターに対して、コントローラーやリソースも一つしか作っていないが、この作りからすると、オペレーターは、複数のコントローラーを同時並行で動かせることになる。例えば、Cluster AutoScale Controller と Horizontal Pod Scaller を組み合わせて、一つのカスタムコントローラーにすることもできると考えられる。そうすれば、それぞれを設定するのではなく。一つのカスタム・オートスケーラーで、ポッド数の増減とノード数の増減を制御できることになる。


## APIsの追加

ここではカスタム・リソースを作成して、K8s APIとしてapiserverをアクセスできるようにするための準備を行う。YAMLのマニフェストのヘッダーにセットする項目が、ここで決まる。

~~~
apiVersion: maho.takara.org/v1alpha1  ==  --api-version
kind: Takara                          ==  --kind
<以下省略>
~~~

コマンドは、オペーレーターのコードが収められるディレクトリのルートで実行する。 このコマンドで、pkg/apisフォルダの下にファイルが生成され、カスタム・リソース・デフィニッションとカスタム・リソースが deploy/crds/ に作成されたことがわかる。

~~~
$ cd takara-operator/
$ operator-sdk add api --api-version=maho.takara.org/v1alpha1 --kind=Takara
INFO[0000] Generating api version maho.takara.org/v1alpha1 for kind Takara. 
INFO[0000] Created pkg/apis/maho/group.go               
INFO[0001] Created pkg/apis/maho/v1alpha1/takara_types.go 
INFO[0001] Created pkg/apis/addtoscheme_maho_v1alpha1.go 
INFO[0001] Created pkg/apis/maho/v1alpha1/register.go   
INFO[0001] Created pkg/apis/maho/v1alpha1/doc.go        
INFO[0001] Created deploy/crds/maho_v1alpha1_takara_cr.yaml 
INFO[0008] Created deploy/crds/maho_v1alpha1_takara_crd.yaml 
INFO[0008] Running deepcopy code-generation for Custom Resource group versions: [maho:[v1alpha1], ] 
INFO[0014] Code-generation complete.                    
INFO[0014] Running OpenAPI code-generation for Custom Resource group versions: [maho:[v1alpha1], ] 
INFO[0027] Created deploy/crds/maho_v1alpha1_takara_crd.yaml 
INFO[0027] Code-generation complete.                    
INFO[0027] API generation complete.  
~~~

ここで生成されたコードのうちで、pkg/apis/maho/v1alpha1/takara_types.go に specとstatusのAPIのパラメータ項目をセットする部分があり、コントローラーの実装と並行して、API項目の追加が必要になる。 ここでAPIsに変更を加えたら、次のコマンドを実行するようにとの注意書きがあったので、その通りに実行しておく。これによって、Util関数や補助コードが生成されるらしい。　将来、APIパラメータ項目を修正したら、毎回、次のコマンドを実行して、変更を反映させること。

~~~
$ operator-sdk generate k8s
INFO[0000] Running deepcopy code-generation for Custom Resource group versions: [maho:[v1alpha1], ] 
INFO[0006] Code-generation complete.  
~~~

## コンローラーの追加

APIsのコードが生成された後に、本体となるコントローラーを作成する。実行時に指定するオプションの--api-vesrion と --kind が一致していなければならない。 pkg/controller の下にコントローラーの本体となるコードが生成される。 これから takara_controller.go のカスタマイズが重要になる。

~~~
$ operator-sdk add controller --api-version=maho.takara.org/v1alpha1 --kind=Takara
INFO[0000] Generating controller version maho.takara.org/v1alpha1 for kind Takara. 
INFO[0000] Created pkg/controller/takara/takara_controller.go 
INFO[0000] Created pkg/controller/add_takara.go         
INFO[0000] Controller generation complete.
~~~

## コントローラーのビルド

Macのローカル環境で、「operator-sdk build」により、オペレータのコンテナをビルドして、プッシュできるようにタグを付与してく。 コンテナのレジストリは、どこでも良いので、アカウントを持っている DockerHubとした。ここでビルドするオペレーターは、operator-sdk だけで生成されたコードで、カスタマイズ部分を含んでいないので、何もできないオペレータである。

~~~
$ operator-sdk build docker.io/maho/takara-operator:v0.0.1
INFO[0028] Building OCI image docker.io/maho/takara-operator:v0.0.1 
Sending build context to Docker daemon  45.29MB
Step 1/7 : FROM registry.access.redhat.com/ubi7/ubi-minimal:latest
 ---> f5abf81c362e
Step 2/7 : ENV OPERATOR=/usr/local/bin/takara-operator     USER_UID=1001     USER_NAME=takara-operator
 ---> Using cache
 ---> 9230ee0cb5dc
Step 3/7 : COPY build/_output/bin/takara-operator ${OPERATOR}
 ---> 764ee389108f
Step 4/7 : COPY build/bin /usr/local/bin
 ---> bad204a241c0
Step 5/7 : RUN  /usr/local/bin/user_setup
 ---> Running in 32f5a6d4612d
+ mkdir -p /root
+ chown 1001:0 /root
+ chmod ug+rwx /root
+ chmod g+rw /etc/passwd
+ rm /usr/local/bin/user_setup
Removing intermediate container 32f5a6d4612d
 ---> 05f5a398c120
Step 6/7 : ENTRYPOINT ["/usr/local/bin/entrypoint"]
 ---> Running in ab37d8257689
Removing intermediate container ab37d8257689
 ---> 8951daa19d4c
Step 7/7 : USER ${USER_UID}
 ---> Running in e9cb749be9e1
Removing intermediate container e9cb749be9e1
 ---> 122cf9949a2f
Successfully built 122cf9949a2f
Successfully tagged maho/takara-operator:v0.0.1
INFO[0037] Operator build complete.
~~~

## Dockerレジストリへプッシュ

ビルドして出来たイメージを、レジストリへ登録しておく。

~~~
$ docker push docker.io/maho/takara-operator:v0.0.1
The push refers to repository [docker.io/maho/takara-operator]
d9086086704a: Pushed
d46b339737f0: Pushed
f48f156fe734: Pushed
2705f79af6db: Layer already exists
ce459cc53899: Layer already exists
v0.0.1: digest: sha256:7a8d3feac8d51910dc4407c878d53960280b00655b3fc8607fb8510f7f7d557b size: 1363
~~~

## Custom Resource Definition をデプロイすることで、APIをつうじてオペレーターへアクセスできるようになる。

Custom Resource Definition (CRD)は、operator-sdk によって deploy/crds の下に生成されているので、そのまま利用する。「kubectl get crd」で結果を確認しておく。

~~~
$ kubectl apply -f deploy/crds/maho_v1alpha1_takara_crd.yaml
customresourcedefinition.apiextensions.k8s.io/takaras.maho.takara.org created

$ kubectl get crd
NAME                                          CREATED AT
<中略>
takaras.maho.takara.org                       2019-08-17T14:08:53Z
~~~


## オペレータをローカルで動作させる

オペレーターは、K8sクラウド上で動かすものであるが、開発時はローカル環境で起動して、apiserverに接続して、デバッグをローカル環境上で実施できる。次の実行画面は、ローカルで起動して、起動初期のメッセージが一通り表示されたところである。

~~~
$ operator-sdk up local --namespace=default
INFO[0000] Running the operator locally.                
INFO[0000] Using namespace default.                     
{"level":"info","ts":1566050832.627087,"logger":"cmd","msg":"Go Version: go1.12.8"}
{"level":"info","ts":1566050832.627469,"logger":"cmd","msg":"Go OS/Arch: darwin/amd64"}
{"level":"info","ts":1566050832.6274781,"logger":"cmd","msg":"Version of operator-sdk: v0.10.0"}
{"level":"info","ts":1566050832.62928,"logger":"leader","msg":"Trying to become the leader."}
{"level":"info","ts":1566050832.629302,"logger":"leader","msg":"Skipping leader election; not running in a cluster."}
{"level":"info","ts":1566050833.838594,"logger":"cmd","msg":"Registering Components."}
{"level":"info","ts":1566050833.838762,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"takara-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1566050833.838927,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"takara-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1566050833.839081,"logger":"cmd","msg":"Could not generate and serve custom resource metrics","error":"operator run mode forced to local"}
{"level":"info","ts":1566050834.335118,"logger":"metrics","msg":"Skipping metrics Service creation; not running in a cluster."}
{"level":"info","ts":1566050834.383544,"logger":"cmd","msg":"Could not create ServiceMonitor object","error":"no ServiceMonitor registered with the API"}
{"level":"info","ts":1566050834.383578,"logger":"cmd","msg":"Install prometheus-operator in your cluster to create ServiceMonitor objects","error":"no ServiceMonitor registered with the API"}
{"level":"info","ts":1566050834.383592,"logger":"cmd","msg":"Starting the Cmd."}
{"level":"info","ts":1566050834.488001,"logger":"kubebuilder.controller","msg":"Starting Controller","controller":"takara-controller"}
{"level":"info","ts":1566050834.592572,"logger":"kubebuilder.controller","msg":"Starting workers","controller":"takara-controller","worker count":1}
~~~



## カスタムリソースをデプロイする

オペレーターのスタンバイが出来たので、もう一つターミナルを開いて、カスタム・リソースを適用して、オペレーターへ指示を送ってみる。

~~~file:crds/maho_v1alpha1_takara_cr.yaml
apiVersion: maho.takara.org/v1alpha1
kind: Takara
metadata:
  name: example-takara
spec:
  # Add fields here
  size: 3
~~~

カスタムに作ったリソース、APIとコントローラーで、ポッドが起動した。
しかし、上記では size: 3 と設定されているにも関わらず、下記の実行結果では、ポッドは一つしか起動していない。

~~~
$ kubectl apply -f crds/maho_v1alpha1_takara_cr.yaml
takara.maho.takara.org/example-takara created

$ kubectl get po
NAME                               READY   STATUS    RESTARTS   AGE
example-takara-pod                 1/1     Running   0          4s
takara-operator-7b48f487c8-ghzzs   1/1     Running   0          5m10s
~~~

## ポッドが1個しか挙がらない原因調査

次のコードは、operator-sdk によって生成されたコントローラーのコードである。この中で、Podが起動させているか否かを確認して、ポッドを起動しているが、Sizeつまり、ポッドの数を見ていないことがわかる。つまり、カスタム・リソースのマニフェストのAPIを受け取っていないのである。

~~~file:pkg/controller/takara/takara_controller.go抜粋
    85	func (r *ReconcileTakara) Reconcile(request reconcile.Request) (reconcile.Result, error) {
    86		reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
    87		reqLogger.Info("Reconciling Takara")
    88	
    89		// Fetch the Takara instance
    90		instance := &mahov1alpha1.Takara{}
    91		err := r.client.Get(context.TODO(), request.NamespacedName, instance)
    92		if err != nil {
    93			if errors.IsNotFound(err) {
    94				// Request object not found, could have been deleted after reconcile request.
    95				// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
    96				// Return and don't requeue
    97				return reconcile.Result{}, nil
    98			}
    99			// Error reading the object - requeue the request.
   100			return reconcile.Result{}, err
   101		}
   102	
   103		// Define a new Pod object
   104		pod := newPodForCR(instance)
   105	
   106		// Set Takara instance as the owner and controller
   107		if err := controllerutil.SetControllerReference(instance, pod, r.scheme); err != nil {
   108			return reconcile.Result{}, err
   109		}
   110	
   111		// Check if this Pod already exists
   112		found := &corev1.Pod{}
   113		err = r.client.Get(context.TODO(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, found)
   114		if err != nil && errors.IsNotFound(err) {
   115			reqLogger.Info("Creating a new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name)
   116			err = r.client.Create(context.TODO(), pod)
   117			if err != nil {
   118				return reconcile.Result{}, err
   119			}
   120	
   121			// Pod created successfully - don't requeue
   122			return reconcile.Result{}, nil
   123		} else if err != nil {
   124			return reconcile.Result{}, err
   125		}
   126	
   127		// Pod already exists - don't requeue
   128		reqLogger.Info("Skip reconcile: Pod already exists", "Pod.Namespace", found.Namespace, "Pod.Name", found.Name)
   129		return reconcile.Result{}, nil
   130	}
~~~


## オペレータのカスタマイズ

operator-sdkで自動生成されるコードは、ポッドを1つ立ち上げるだけのシンプルなコードなので、目的の動作を実行するためのコントローラーに完成させなければならない。複数のポッドの起動を制御する必要があるので、Bareなポッドを起動するのではなく、Deploymentコントローラの制御下で起動するべきである。


#### オペレータの修正箇所: cmd/manager/main.go

ここでは、主要な箇所の説明にとどめ、補助的なコードなどを含めた全体は、https://github.com/takara9/takara-operator を参照して欲しい。読者自身でトレースする場合にはGitHubからクローンすると良い。次コードの コントローラーの Reconcile すなわち 調整機能の一部に変更を加え、Deploymentコントローラーを起動するようにする。次のコードの107行目から修正を加える。既存のPodを対象としたコードは削除して、Deploymentコントローラーを起動すること、instance の Spec.Size をチェックして、値が違っていれば、Replicas レプリカ数に、値をセットする。 Go言語もC言語と同じで、ポインタやアドレスを意識したコーディングが必要なので注意が必要あである。

~~~file:pkg/controller/takara/takara_controller.go抜粋
   102			// Error reading the object - requeue the request.
   103			return reconcile.Result{}, err
   104		}
   105	
   106	
   107	        // deploymentの存在をチェックして、既に存在していれば生成しない
   108	        found := &appsv1.Deployment{}
   109	        err = r.client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, found)
   110	        if err != nil && errors.IsNotFound(err) {
   111	                // 新deploymentの定義
   112	                dep := r.deploymentForTakara(instance)
   113	                reqLogger.Info("Creating a new Deployment.", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
   114	                err = r.client.Create(context.TODO(), dep)
   115	                if err != nil {
   116	                        reqLogger.Error(err, "Failed to create new Deployment.", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
   117	                        return reconcile.Result{}, err
   118	                }
   119	                // Deployment生成成功
   120	                return reconcile.Result{Requeue: true}, nil
   121	        } else if err != nil {
   122	                reqLogger.Error(err, "Failed to get Deployment.")
   123	                return reconcile.Result{}, err
   124	        }
   125	
   126	
   127		// デプロイメントのサイズをspecと同じになるように調整する
   128		size := instance.Spec.Size
   129		if *found.Spec.Replicas != size {
   130			found.Spec.Replicas = &size
   131			err = r.client.Update(context.TODO(), found)
   132			if err != nil {
   133				reqLogger.Error(err, "Failed to update Deployment.", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
   134				return reconcile.Result{}, err
   135			}
   136			// Specを更新
   137			return reconcile.Result{Requeue: true}, nil
   138		}
  <中略>
   165		return reconcile.Result{}, nil
   166	}
~~~

上記の部分が、コアになる修正箇所であるが、関連して、DeploymentコントローラーのAPIを操作する部分、ラベルを取得するなど、追加が必要である。


#### APIの修正箇所: pkg/apis/maho/v1alpha1/takara_types.go

APIのコードには、2箇所の変更がある。これで、スペックとして受け取る部分と、ステータスとして

* type TakaraSpecにメンバーを追加: Sizeを追加して、ポッド数を変更可能にする
* type TakaraStatusにメンバーを追加: Nodes配列を追加して、ノードのリストを取得可能にする

~~~
    10	// TakaraSpec defines the desired state of Takara
    11	// +k8s:openapi-gen=true
    12	type TakaraSpec struct {
    13		// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
    14		// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
    15		// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
    16		Size int32 `json:"size"`   <<--- ここ 
    17	}
    18	
    19	// TakaraStatus defines the observed state of Takara
    20	// +k8s:openapi-gen=true
    21	type TakaraStatus struct {
    22		// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
    23		// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
    24		// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
    25		Nodes []string `json:"nodes"`  <<--- ここ
    26	}
~~~

このコードの修正により、Nodesにポッドのリストの表示されるようになる。

~~~
$ kubectl get takaras.maho.takara.org
NAME             AGE
example-takara   26h

$ kubectl describe takaras.maho.takara.org  example-takara
Name:         example-takara
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"maho.takara.org/v1alpha1","kind":"Takara","metadata":{"annotations":{},"name":"example-takara","namespace":"default"},"spec...
API Version:  maho.takara.org/v1alpha1
Kind:         Takara
Metadata:
  Creation Timestamp:  2019-08-18T02:05:38Z
  Generation:          1
  Resource Version:    347217
  Self Link:           /apis/maho.takara.org/v1alpha1/namespaces/default/takaras/example-takara
  UID:                 ac99732e-c15c-11e9-8089-e2eef895ad20
Spec:
  Size:  3
Status:
  Nodes:  <-- 前述のAPIsのステータスの追加などによって、表示されるようになる。
    example-takara-96695d664-5g869
    example-takara-96695d664-5snrz
    example-takara-96695d664-9g7gt
~~~



#### コントローラーの修正箇所: pkg/controller/takara/takara_controller.go

上記はポイントになる場所だけだったので、以下で、その修正点を賀状書きにする。

* func newPodForCR を削除: 単独でポッドを起動することを辞める
* func deploymentForTakara を追加: デプロイメントを通じてポッドを起動する
* func labelsForTakara を追加: コントローラとポッドを繋ぐラベルをリスト
* func getPodNamesを追加: ポッド名のリストを返す
* import にモジュール追加: 詳細はリスト参照
* func Reconcile を修正: Deploymentからポッドを起動、ポッド数をスペックに合わせて調整他


## takaraオペレータを再デプロイして、コンテナをレジストリへ登録

修正を行ったあと、オペレーターのコンテナを再ビルドして、レジストリへアップロードする。

~~~
$ operator-sdk generate k8s
$ operator-sdk build docker.io/maho/takara-operator:v0.0.3
INFO[0005] Building OCI image docker.io/maho/takara-operator:v0.0.3
<中略>
$ docker push docker.io/maho/takara-operator:v0.0.3
~~~

以下、オペレーターのマニフェストのimage を置き換えて、kubectl で apply する。

~~~file:operator.yaml
    14	    spec:
    15	      serviceAccountName: takara-operator
    16	      containers:
    17	        - name: takara-operator
    18	          # Replace this with the built image name
    19	          image: docker.io/maho/takara-operator:v0.0.3  <-- sedなどで修正
    20	          command:
~~~

オペレーターの実行に必要な RBACも設定して、オペレーターをデプロイ

~~~
$ kubectl apply -f role.yaml
role.rbac.authorization.k8s.io/takara-operator configured
$ kubectl apply -f role_binding.yaml
rolebinding.rbac.authorization.k8s.io/takara-operator unchanged
$ kubectl apply -f service_account.yaml
serviceaccount/takara-operator unchanged
$ kubectl apply -f operator.yaml
deployment.apps/takara-operator created
~~~

## カスタムリソースのデプロイ

operator-sdkに作られたカスタム・リソースのマニフェストを適用する。

~~~
$ kubectl apply -f maho_v1alpha1_takara_cr.yaml
takara.maho.takara.org/example-takara created
~~~

オペレーターによって、デプロイメントがデプロイされ、ポッドが起動されたことが確認できます。
もちろん、Sizeを変更すれば、応じてポッド数が変わります。

~~~
$ kubectl get deployment
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
example-takara    3/3     3            3           20s
takara-operator   1/1     1            1           27h

$ kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
example-takara-96695d664-djww8     1/1     Running   0          9s
example-takara-96695d664-k7j94     1/1     Running   0          9s
example-takara-96695d664-zw2ps     1/1     Running   0          9s
takara-operator-7499958656-n6npt   1/1     Running   0          27h
~~~

カスタム・リソース・デフィニッションも表示できます。

~~~
$ kubectl get crd takaras.maho.takara.org
NAME                      CREATED AT
takaras.maho.takara.org   2019-08-17T14:08:53Z
~~~


## まとめ

Go言語のプログラムで、インポートしているライブラリ名に、sigs.k8s.io/controller-runtime/pkg/manager などが見うけられ、まだまだ、これからという印象を受ける。 いくつかの Operator SDK のサンプルコードでは、operator-sdkで生成したコードで、Deploymentコントローラーが動作しているように書かれているが、実施には手順が抜けているだけで、Go言語の K8sクライアントライブラリを利用してコードを書かないと、いけないことが解った。

オペレーターから呼び出されるコントローラーのコードの書き方には作法があるようだ。その作法について調べておかないといけないことを忘れないようにしないと.




# 参考資料

[1] Operator SDK、https://github.com/operator-framework/operator-sdk
[2] Install Operator SDK、https://github.com/operator-framework/operator-sdk/blob/master/doc/user/install-operator-sdk.md
[3] CLI Guide、https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md
[4] Go言語のバージョンを切り替える方法 macOS HomeBrew、https://qiita.com/MahoTakara/items/5095ac721aa054cf1cf8
[5] User Guide、https://github.com/operator-framework/operator-sdk/blob/master/doc/user-guide.md
[6] Memcahcedオペレーター、https://github.com/operator-framework/operator-sdk/tree/master/example/memcached-operator/memcached_controller.go.tmpl