GKEでNATS Operatorを試してみる

# はじめに

Kubernetes上でメッセージングのサービスが使いたくて、NATSを動かしてみました。
最初はHelmのChartを探したのですが見当たらず、NATS公式のGithubアカウントにOperatorを見つけたので、こちらを試してみました。

**NATS Operator**
https://github.com/nats-io/nats-operator

# NATSとは

NATSは、シンプルかつ高性能なオープンソースのメッセージングサービスで、Kubernetesと同じくCNCF(Cloud Native Computing Foundation)傘下のプロジェクトです。公式ページの見解によれば、クラウドネイティブなアプリケーション、IoTメッセージング、マイクロサービスアーキテクチャ向けに作られています。

NATSサーバ自体はGo言語で実装されていて、NATSクライアントは様々な言語で実装されたものが存在します。

**NATS**
https://nats.io/

# Operatorとは

Operatorは、CoreOS社が提唱するKubernetesの[カスタムリソース](https://kubernetes.io/docs/concepts/api-extension/custom-resources/)の仕組みを通じてKubernetes APIを拡張し、複雑なステートフルアプリケーションのインスタンスを作成、構成、管理するための運用の知見を組み込んだアプリケーション固有のコントローラです。Operatorを用いることで、（Deployment、ReplicaSet等の）Kubernetesの組み込みリソースと同様に、宣言的な設定により複雑なステートフルアプリケーションの作成、構成、管理を行うことができます。

**Introducing Operators: Putting Operational Knowledge into Software**
https://coreos.com/blog/introducing-operators.html

**Operators**
https://coreos.com/operators/

Operatorの実装例としては、etcdクラスタを作成、設定、管理するためのetcd OperatorやPrometheusクラスタを作成、構成、管理するためのPrometheus Operatorがあります。

**etcd Operator**
https://github.com/coreos/etcd-operator

**Prometheus Operator**
https://github.com/coreos/prometheus-operator

# NATS Operatorのデプロイ

何はともあれ、GKE上にKubernetesクラスタを作成します。

```console
$ gcloud container clusters create nats-cluster \
    --zone asia-northeast1-a
```

自身のユーザーにAuthorizationを許可するために`cluster-admin`の権限を付与します。
NATS Operatorの[README](https://github.com/nats-io/nats-operator/blob/master/README.md)にある手順には書かれていないのですが、GKEの場合、このステップが必要のようです。

**ISSUE #36**
https://github.com/nats-io/nats-operator/issues/36

```console
$ kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin --user=$(gcloud config get-value core/account)
clusterrolebinding "cluster-admin-binding" created
```

Operatorをデプロイします。OperatorはKubernetes上のPodとして動作します。
このマニフェストの場合、`nats-io`というNamespaceにデプロイされます。

```console
$ kubectl apply -f https://raw.githubusercontent.com/nats-io/nats-operator/master/example/deployment-rbac.yaml
namespace "nats-io" created
serviceaccount "nats-operator" created
deployment "nats-operator" created
clusterrolebinding "nats-io:nats-operator-binding" created
clusterrole "nats-io:nats-operator" created
```

NATSクラスタを表すカスタムリソースとそのコントローラであるOperatorがデプロイされました。

```console
$ kubectl -n nats-io get crd
NAME                   AGE
natsclusters.nats.io   10s
```

```console
$ kubectl -n nats-io get pod
NAME                            READY     STATUS    RESTARTS   AGE
nats-operator-8df7578c4-5cb2t   1/1       Running   0          5s
```

3ノードからなるNATSクラスタを作成します。
`NatsCluster`というカスタムリソースでNATSクラスタを宣言的に作成、構成、管理できます。便利！

```console
$ echo '
apiVersion: "nats.io/v1alpha2"
kind: "NatsCluster"
metadata:
  name: "example-nats-cluster"
spec:
  size: 3
  version: "1.1.0"
' | kubectl -n nats-io apply -f -
natscluster "example-nats-cluster" created
```

NATSクラスタが作成されたことを確認します。

```console
$ kubectl -n nats-io get natsclusters
NAME                   AGE
example-nats-cluster   10s
```

```console
$ kubectl -n nats-io get pod
NAME                            READY     STATUS    RESTARTS   AGE
nats-46m7xr10zr                 1/1       Running   0          42s
nats-operator-8df7578c4-5cb2t   1/1       Running   0          2m
nats-tjrt32ggsh                 1/1       Running   0          32s
nats-tv9q9x0xrk                 1/1       Running   0          22s
```

NATクラスタのためのServiceを確認します。
NATSクライアントは先に`NatsCluster`カスタムリソースで定義した`example-nats-cluster`という名前でNATSクラスタにアクセスできます。

```console
$ kubectl -n nats-io get svc
NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
example-nats-cluster        ClusterIP   10.47.246.213   <none>        4222/TCP            1m
example-nats-cluster-mgmt   ClusterIP   None            <none>        6222/TCP,8222/TCP   1m
```

# テスト

NATSサブスクライバのためのコンテナを立ち上げます。

```console
$ kubectl -n nats-io run -i --tty nats-sub --image=golang --restart=Never

If you don't see a command prompt, try pressing enter.

root@nats-sub:/go
```

テストのために、公式Githubリポジトリにあるサブスクライバのサンプルプログラムを用意します。

```console:Subscriber
root@nats-sub:/go# wget https://raw.githubusercontent.com/nats-io/go-nats/master/examples/nats-sub.go
root@nats-sub:/go# go get "github.com/nats-io/go-nats"
```

サブスクライバから`hoge`サブジェクトにサブスクライブします。

```console:Subscriber
root@nats-sub:/go# go run nats-sub.go -s nats://example-nats-cluster:4222 hoge
Listening on [hoge]
```

別のコンソールからNATSパブリッシャのためのコンテナを立ち上げます。

```console
$ kubectl -n nats-io run -i --tty nats-pub --image=golang --restart=Never

If you don't see a command prompt, try pressing enter.

root@nats-sub:/go
```

サブスクライバと同様に、公式Githubリポジトリにあるサンプルのパブリッシャのプログラムを用意します。

```console:Publisher 
root@nats-pub:/go# wget https://raw.githubusercontent.com/nats-io/go-nats/master/examples/nats-pub.go
root@nats-pub:/go# go get "github.com/nats-io/go-nats"
```

パブリッシャから`hoge`サブジェクトにメッセージをパブリッシュします。

```console:Publisher
root@nats-pub:/go# go run nats-pub.go -s nats://example-nats-cluster:4222 hoge hello
Published [hoge] : 'hello'
```

サブスクライバでは、以下のようにメッセージを受信できます。

```console:Subscriber
[#1] Received on [hoge]: 'hello'
```

おしまい

# 参考

**Explore NATS Pub Sub**
https://nats.io/documentation/tutorials/nats-pub-sub/