k8s環境上でKUDO(Kubernetes Universal Declarative Operator)を使ってElasticStackをインストールしてみる

この記事は[Elastic Stack (Elasticsearch)その2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/elasticsearch2)、3日目の記事です。

####【お断り】
よくよく手順を見直したら、KUDO CLIがkubectl plugin対応なことを見逃してました。kubectl-kudo*という名前でPATHを通せば、kubectlのサブコマンドとして"kubectl kudo"と実行できます。時間を見て手順修正しておきます。（一応下記の手順でもやっていることは変わりません）
※お断り、ここまで



最近Elasticsearchの環境を構築するにもさまざまな方法が出てきています。パッケージインストール、Dockerコンテナ、ElasticCloudもその一つです。
もしkubernetes(以下、k8s)を使っているのであれば、ElasticsearchやKibanaをOperator経由で管理できるElastic Cloud on K8s(ECK)も今年登場しましたね。
（ちなみに、ECKについては12/23に[Elastic Stack (Elasticsearch)その1 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/elasticsearch)で何か書く予定です。）

k8sではもう１つの方法として、KUDO(Kubernetes Universal Declarative Operator)と呼ばれるOperatorを宣言的に書いてより直感的に管理できる仕組みを利用してElasticStackを導入することができます。
KUDOは昨日初めて知ったのですが、触ってみると確かに簡単に（手元にすぐ使えるk8s環境がある前提ですが）ElasticStackクラスタをセットアップできましたので、手順だけでもご紹介したいと思います。

**【参考】**
・[KUDO](https://kudo.dev/)

## 前提条件
お手元にk8s環境がある前提とします。パブリッククラウドではいくつかマネージドなk8sサービスがありますので手軽に試す場合は利用を検討してみてください。
今回はAzureが提供するAKS(Azure Kubernetes Service)を使って動作確認をしています。

環境セットアップ方法は、[OpenShift Meetup Tokyo #05で発表したスライド](https://www.slideshare.net/TetsuyaSodo1/ocp-meetup-tokyo-05-eck-on-ocp-157494511)の一番最後(p.50 Appendix)にまとめてあるので参考にしてください。
ただし、KUDOでElasticパッケージを使うためにはk8sのバージョンが1.15以上である必要があるため、バージョン指定には注意してください。
2019/12/03現在、AKSの東日本リージョンでは1.15.5あるいは1.15.6のバージョンが利用可能です。

## 利用環境
| product | version |
|---------|---------|
| AKS | 1.15.5 |
| kubectl | 1.15.0 |
| kudo CLI | 0.8.0 | 


## 導入手順
手順の概要は以下の通りです。
　1. AKSのセットアップ（省略。上記slideshare p.50参照）
　2. KUDO CLIインストール
　3. k8s環境へのKUDOのインストール
　4. KUDO CLIを使い任意のOperator（ここではElastic）をインストール
　5. アクセス確認

#### 1. AKSのセットアップ
- ここでは省略します。

#### 2. KUDO CLIインストール
- 環境に合わせてインストールを行います
- MacOSであれば"brew install kudo-cli"で終わり
- 各OSに合わせたバイナリを[ダウンロード](https://github.com/kudobuilder/kudo/releases)できます

```console
$ wget https://github.com/kudobuilder/kudo/releases/download/v0.8.0/kubectl-kudo_0.8.0_linux_x86_64 -O kudo
$ export PATH=$PATH:.
$ kudo version
KUDO Version: version.Info{GitVersion:"0.8.0", GitCommit:"2a585aed", BuildDate:"2019-11-08T16:35:17Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}
```

#### 3. k8s環境へのKUDOのインストール
- kudo環境のセットアップを行います

```console
$ kudo init
$KUDO_HOME has been configured at /mnt/c/Users/tetsuyas/home/.kudo
✅ installing crds
✅ preparing service accounts and other requirements for controller to run
✅ installing kudo controller
$
$ kubectl get crd | grep kudo
instances.kudo.dev          2019-12-03T03:57:38Z
operators.kudo.dev          2019-12-03T03:57:38Z
operatorversions.kudo.dev   2019-12-03T03:57:38Z
$
$ kubectl get all -n kudo-system
NAME                            READY   STATUS    RESTARTS   AGE
pod/kudo-controller-manager-0   1/1     Running   0          79s

NAME                                      TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kudo-controller-manager-service   ClusterIP   10.0.210.80   <none>        443/TCP   79s

NAME                                       READY   AGE
statefulset.apps/kudo-controller-manager   1/1     79s

$
```

- 環境が初期化され、kudo-systemネームスペースにKUDOのcontroller managerが起動します
- 以後のCLIコマンドはこのcontroller managerが受け取り、管理します
- アーキテクチャ図を参考に示します
  - 図の真ん中に点線で囲まれたKUDO CRDsとKUDO controllerがいまインストールしたコンポーネント部分です
  - このOperatorにインストールなどの命令を実行すると右側の例にあるようにkafkaなどのクラスタが自動でセットアップされます
  - インストール情報などはファイル群としてパッケージングされ簡単に利用できるようになっています。これがrepositoryです。

![image.png](https://kudo.dev/images/kudo-architecture.jpg?10x20)

#### 4. KUDO CLIを使い任意のOperator（ここではElastic）をインストール
- KUDOでは[リポジトリ](https://github.com/kudobuilder/operators)に付属のOperatorがいくつか公開されており、これを使うと簡単にインストールを試すことができます
- [Elasticのリポジトリ](https://github.com/kudobuilder/operators/tree/master/repository/elastic)の構成を見てみると主に次のロールのファイルがある
  - operator/operator.yamlファイル：インストールなどの運用管理タスクを抽象化したCustom Resourceを定義
  - operator/params.yamlファイル：構成をカスタマイズするための変数を定義
  - operator/templates/*.yamlファイル：実際のPodやServiceなどのリソース定義をテンプレートとして格納

- 次の手順でインストールを行います

```console
$ kudo install elastic
instance.kudo.dev/v1beta1/elastic-instance created
$
$ kubectl get all
NAME           READY   STATUS     RESTARTS   AGE
pod/master-0   0/1     Init:0/2   0          15s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/kubernetes   ClusterIP   10.0.0.1     <none>        443/TCP    87m
service/master-hs    ClusterIP   None         <none>        9200/TCP   15s

NAME                      READY   AGE
statefulset.apps/master   0/3     15s

$
```

- デフォルトのパラメータ定義に従って、Masterノードx3、Dataノードx1、Coordinatingノードx1、Ingestノードx0が順次起動してきます
- 仕様なのか、ノードは1台ずつ起動するため、すべて起動し終わるまで5-10分ほど待ちます

```console
$ kubectl get all
NAME           READY   STATUS     RESTARTS   AGE
pod/data-0     1/1     Running    0          37s
pod/data-1     0/1     Init:0/2   0          7s
pod/master-0   1/1     Running    0          2m22s
pod/master-1   1/1     Running    0          96s
pod/master-2   1/1     Running    0          66s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/data-hs      ClusterIP   None         <none>        9200/TCP   37s
service/kubernetes   ClusterIP   10.0.0.1     <none>        443/TCP    89m
service/master-hs    ClusterIP   None         <none>        9200/TCP   2m22s

NAME                      READY   AGE
statefulset.apps/data     1/2     38s
statefulset.apps/master   3/3     2m23s

$
```

- もう少し

```console
$ kubectl get all
NAME                READY   STATUS    RESTARTS   AGE
pod/coordinator-0   1/1     Running   0          5m43s
pod/data-0          1/1     Running   0          6m42s
pod/data-1          1/1     Running   0          6m12s
pod/master-0        1/1     Running   0          8m27s
pod/master-1        1/1     Running   0          7m41s
pod/master-2        1/1     Running   0          7m11s

NAME                     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/coordinator-hs   ClusterIP   None         <none>        9200/TCP   5m43s
service/data-hs          ClusterIP   None         <none>        9200/TCP   6m42s
service/ingest-hs        ClusterIP   None         <none>        9200/TCP   5m14s
service/kubernetes       ClusterIP   10.0.0.1     <none>        443/TCP    95m
service/master-hs        ClusterIP   None         <none>        9200/TCP   8m27s

NAME                           READY   AGE
statefulset.apps/coordinator   1/1     5m43s
statefulset.apps/data          2/2     6m42s
statefulset.apps/ingest        0/0     5m14s
statefulset.apps/master        3/3     8m27s

$
```

- 全部起動しました（ingestはデフォルト設定で0台なので0/0となってます）

#### 5. アクセス確認
- Serviceが9200ポートで起動していますので、LoadBalancerに接続して外部から接続も可能です
- ここでは端折ってCoordinatingノードのPodに潜ってcurlコマンドを発行します

```console
$ kubectl exec -it coordinator-0 /bin/bash
[root@coordinator-0 elasticsearch]# curl coordinator-hs:9200
{
  "name" : "coordinator-0",
  "cluster_name" : "elastic-instance-cluster",
  "cluster_uuid" : "_na_",
  "version" : {
    "number" : "7.0.0",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "b7e28a7",
    "build_date" : "2019-04-05T22:55:32.697037Z",
    "build_snapshot" : false,
    "lucene_version" : "8.0.0",
    "minimum_wire_compatibility_version" : "6.7.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
[root@coordinator-0 elasticsearch]# exit
$
```

- バージョンは7.0.0が動いているようですね。。
  - バージョンを上げる場合は、operator.yamlのversionおよび、各template/*.yamlのimageバージョンを上げてkudo upgradeコマンドを実行すればできるようです

- Dataノードのスケールアウト(2->3台)を試してみます

```console
$ kudo update --instance elastic-instance -p DATA_NODE_COUNT=3
$
$ kubectl get all
NAME                READY   STATUS    RESTARTS   AGE
pod/coordinator-0   1/1     Running   0          86s
pod/data-0          1/1     Running   0          2m16s
pod/data-1          1/1     Running   0          3m36s
pod/data-2          1/1     Running   0          4m19s
pod/master-0        1/1     Running   0          5m36s
pod/master-1        1/1     Running   0          7m6s
pod/master-2        1/1     Running   0          7m29s

NAME                     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/coordinator-hs   ClusterIP   None         <none>        9200/TCP   20m
service/data-hs          ClusterIP   None         <none>        9200/TCP   21m
service/ingest-hs        ClusterIP   None         <none>        9200/TCP   19m
service/kubernetes       ClusterIP   10.0.0.1     <none>        443/TCP    110m
service/master-hs        ClusterIP   None         <none>        9200/TCP   23m

NAME                           READY   AGE
statefulset.apps/coordinator   1/1     20m
statefulset.apps/data          3/3     21m
statefulset.apps/ingest        0/0     19m
statefulset.apps/master        3/3     23m

$
```

- コマンド実行によりDataノードが2台から3台にスケールアウトしていることが確認できました

## まとめ
k8s環境ではElastic Cloud on K8s(ECK)の開発が進んでおりまもなく1.0.0バージョンがGAする見込みですが、ここではOperatorをよりわかりやすく作成、管理することを目的に作られたKUDOを触ってみて手順を紹介しました。
使い勝手だけでいうとECKと同等ですが、自分でOperatorを作る場合はGithubのコードを見る限りかなりわかりやすい印象を受けました。
今後どのくらい利用が広がるかわかりませんが、今後の動向を見守りたいと思います。