【Kubernetes】OperatorSDKにサクッと入門

# 概要

- 本記事では、operator-sdkを利用して、deploymentのimageが変更されるたびに、slackに通知する仕組みを作成します。
- サンプルコード： https://github.com/solt9029/deployment-image-watcher
- 挙動： https://www.youtube.com/watch?v=eknVJj8E-HE&feature=youtu.be

# 対象読者

- 基本的なkubernetesの知識がある人
- operatorとは何か、なんとなく把握しているが、開発を行なったことがない人
- go言語の基本的な文法が読める人

# はじめに

kubernetesの拡張機能の1つに、operatorが存在します。
operatorを利用すれば、運用の自動化を行うことができます。
operatorに関する詳しい説明は、下記リンクをご覧ください。 
https://qiita.com/MahoTakara/items/af4ad8ab69c24102bd72

複数人で同一のclusterやnamespace上で開発を行なっている場合に、
誰がいつdeploymentのimageを変更したか分からず困る状況を想定し、
本記事では、deploymentのimageが変更されるたびに、slackに通知する仕組みを作成していきます。

なお、本記事で作成するものは、本来のoperator-sdkの使い方とは言えないため、
あくまでoperator-sdkに触れる目的であることをご留意ください。

# 環境構築

以下の環境が必要です。

- go
- operator-sdk
- docker
- kubectl

dockerやkubectlのインストールについては説明を省きます。
下記手順により、goおよびoperator-sdkをインストールします。

```zsh
brew install go
go version # 筆者はv1.13.3を使用
export Go111MODULE=on # Go Modulesを使用するため
brew install operator-sdk
operator-sdk version # 筆者はv0.15.2を使用
```

また、kubernetesクラスタやdockerhub、slackapi（botによるメッセージ送信）が利用できる状態としてください。

# 開発
## テンプレート生成

下記手順によりテンプレートを生成します。

```zsh
operator-sdk new deployment-image-watcher --repo=github.com/<username>/deployment-image-watcher
cd deployment-image-watcher
operator-sdk add controller --api-version=deployment.k8s.io/v1alpha1 --kind=Deployment
```

通常、operator-sdkを使用する場合、CRD（Custom Resource Definition）を利用するのが一般的ですが、
今回はdeploymentのimageの変更を検知したいだけなので、CRDは生成しません。

## controller

さっそく、根幹となるcontrollerを編集していきます。
controllerは``` pkg/controller/deployment/deployment_controller.go ```に存在します。
重要な部分は以下の通りです。
またファイル全体は https://github.com/solt9029/deployment-image-watcher/blob/master/pkg/controller/deployment/deployment_controller.go から確認できます。

```go
func add(mgr manager.Manager, r reconcile.Reconciler) error {
	c, err := controller.New("deployment-controller", mgr, controller.Options{Reconciler: r})
	if err != nil {
		return err
	}

	slackClient := slack.New(os.Getenv("SLACK_TOKEN"))

	source := &source.Kind{Type: &appsv1.Deployment{}}
	handler := &handler.EnqueueRequestForObject{}
	predicate := predicate.Funcs{
		UpdateFunc: func(e event.UpdateEvent) bool {
			for _, oldContainer := range e.MetaOld.(*appsv1.Deployment).Spec.Template.Spec.Containers {
				for _, newContainer := range e.MetaNew.(*appsv1.Deployment).Spec.Template.Spec.Containers {
					if oldContainer.Name == newContainer.Name && oldContainer.Image != newContainer.Image {
						messageText := createMessage(newContainer.Name, newContainer.Image, oldContainer.Image)
						slackClient.PostMessage(os.Getenv("SLACK_CHANNEL"), slack.MsgOptionText(messageText, true))
					}
				}
			}
			return true
		},
	}

	err = c.Watch(source, handler, predicate)
	if err != nil {
		return err
	}

	return nil
}
```

watch関数のsourceの引数によって、監視対象としてdeploymentを設定します。
predicateは本来、deploymentの変更や追加、削除などのイベントに対して、
reconcileを行うべきかフィルタリングする役割を担っています。
このpredicate内では変更前の状態と変更後の状態を両方取得することができるため、
今回はこの部分でimageが変更されたかどうかを判定し、slack通知を行うロジックを書いています。

``` e.MetaOld.(*appsv1.Deployment).Spec.Template.Spec.Containers ``` によって変更前のdeployment.spec.template.containersを取得できます。
同様に、変更後のdeployment.spec.template.containersも取得できます。
それらを用いて、imageを比較し、変更があればslack通知を行なっています。

## ビルド

これだけで開発は終了です。
下記手順でビルドし、イメージをdocker registryにpushします。

```zsh
operator-sdk build <username>/deployment-image-watcher:latest
docker push <username>/deployment-image-watcher:latest
```

## マニフェスト

``` deploy/secret.yaml ``` を追加し、以下のようなsecretのマニフェストを記述します。
``` SLACK_TOKEN ``` にはslackapiのtokenをbase64化した値を入れてください。

```zsh
echo -n "<your slack token>" | base64
```

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: deployment-image-watcher
type: Opaque
data:
  SLACK_TOKEN: <YOUR_SLACK_TOKEN_BASE64_ENCODED>
```

次に、``` deploy/operator.yaml ``` を編集します。
以下の2つの環境変数を追加してください。

```yaml
            - name: SLACK_TOKEN
              valueFrom:
                secretKeyRef:
                  name: deployment-image-watcher
                  key: SLACK_TOKEN
            - name: SLACK_CHANNEL
              value: "general"
```

# 動作確認

下記手順により、deployment-image-watcherをデプロイします。

```zsh
kubectl apply -f deploy/
```

なんらかのdeploymentをapplyし、imageを変更してみてください。
slackに通知が届くはずです。
 

# おわりに

本記事では、operator-sdkを利用して、deploymentのimageが変更されるたびに、slackに通知する仕組みを作成しました。
operator-sdkを手軽に使うために、CRDを用いずに簡易的なサンプルを実装しました。
本来のoperator-sdkの利用目的とは若干ずれていましたが、本記事によって基本的な使い方がわかれば幸いです。
ありがとうございました。