【ElasticSearch Operator編】Kubernetes Operatorコードリーディング

# ElasticSearch Operatorとは

ElasticSearch Operatorとは、ElasticSearch向けのKubernetes Operatorであり、OpenshiftのClusterLoggingの機能の一部として動作するOperatorである。

ElasticSearchを自動運用するためのOperationがこのOperatorとして実装されている。

openshift/elasticsearch-operatorというGithubリポジトリのためOpenshiftコミュニティによりメンテされているようである。

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator)

## 【補足】elasticsearch-operatorのコード量

Goのファイルが2500強あり、コードが1000k lineにも及ぶ

```bash
❯ cloc .
    3297 text files.
    3170 unique files.                                          
     367 files ignored.

github.com/AlDanial/cloc v 1.80  T=10.38 s (283.8 files/s, 117872.8 lines/s)
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
Go                             2539          81218         112652         974429
~
--------------------------------------------------------------------------------
SUM:                           2945          89341         127274        1006349
--------------------------------------------------------------------------------
```

vendorディレクトリ内で同様に見てみると、950 k lineはvendor関連コードという風に取れるため、実質20 k lineくらいがelasticsearch-operatorのコードといえそう。

```bash
❯ cloc .
    3070 text files.
    2948 unique files.                                          
     343 files ignored.

github.com/AlDanial/cloc v 1.80  T=7.37 s (371.4 files/s, 161408.3 lines/s)
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
Go                             2411          77974         111744         956992
~
--------------------------------------------------------------------------------
SUM:                           2738          85146         124716         980068
--------------------------------------------------------------------------------
```



# ElasticSearchのReconcile処理詳解

## Reconcile処理概要

- elasticsearchのNode(Elasticsearchクラスタのメンバ)ごとに、正常性確認を行う

- Elasticsearch nodeごとにdeploymentを持っていて、そのdeploymentごとにreconcileを行う

- deployment内のpodステータスを確認する際に、コンテナのステータスも確認しreconcileを行っている。



## Reconcile処理に関連するコードの呼び出しフロー

主な処理の流れとしては

- controller/erlasticsearch packageのadd()関数
- controller packageのreconcileHandler経由でReconcile()関数が呼ばれ
- さらにk8shandler内のReconcile()が呼ばれ
- KubernetesのDeployment, Pod, ContainerレベルのReconcileが行われる

となっている。(図1 下記の黒矢印部分)


![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/6b390fe4-ff12-dcae-0b1c-b3407b5ab86e.png)
図1. Reconcile処理に関連するコードの呼び出しフロー図



上記のコールグラフは下記のツールを使用して描画した。インタラクティブに処理を追えるが処理が重いため上記1枚に留めた。

https://github.com/ofabry/go-callvis



## 処理の入り口 elasticsearch package内のadd()により、Controllerを登録

add()のコード(図2)をみると、reconcilerを与えることで新しくcontrollerを追加する、という関数である。

ここでElasticSearchをReconcileするためのcontrollerが生成される。

![carbon-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/335ccdb2-d4c7-f806-8436-3c969dba09a8.png)
図2. add()の定義

**このadd()によって追加されたcontrollerのReconcileが、いわゆるReconcilation Loopと呼ばれる、KubernetesならびにKubernetes Operatorの主な役割である。**



## Reconcillation Loop

Reconcile()の定義は以下に記載されている。

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/controller/elasticsearch/controller.go#L69-L118)

図1で示した通り、Reconcile()からk8shandler package内のReconcile()を呼び出している。(図3)

![carbon_(2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/0c18b4e0-1e0e-234b-6f33-507d93cd81d2.png)
図3. Reconcile()の実装の一部抜粋



このk8shandler.Reconcile()では、主にKubernetesリソースレベルでのReconcileを行う。

具体的には、

- ServiceAccount
- RBAC
- ConfigMaps
- Prometheus向けのServiceMonitor, PrometheusRule

などがある。

また、これに加えて

- **Elasticsearch Cluster**
- IndexManagement

についてのReconcile処理が定義されている。



これらについて、**CreateOrUpdateHoge()という関数が用意されており、該当のリソースがなければ作り、理想状態じゃなければ更新をかける処理(Reconcilation Loop)が実装されている**ことになる。

CreateOrUpdateHoge郡の該当のコードは下記である。

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/k8shandler/reconciler.go#L17-L72)

**ここでは、メインのReconcile処理であるCreateOrUpdateElasticsearchCluster()についてさらにDeep Diveすることにする。**

## ElasticSearchのReconcile処理であるCreateOrUpdateElasticsearchCluster()の実装確認

ここまで説明した通り、CreateOrUpdateElasticsearchCluster()とはElasticSearchを理想状態に保つための関数である。

実際にはDeploymentとして動作するためElasticSearch  Deploymentを理想状態にすることをReconcileといっても大きな齟齬はないと思われる。
![carbon-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/74f307d3-b95d-d438-394a-d4029e40bb9c.png)


具体的な処理が気になる場合は、下記の処理から追うことができる。

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/k8shandler/cluster.go#L33-L175)

**ここでは、ElasticSearchに不具合が起きた場合にどのようにPodに対してReconcileを行うのかについて注目する。**

k8shandler package内のcluster.goにて、ScheduledForUpgradeを監視することで、更新が必要なノード(elasticsearchクラスタのメンバの意でありk8s nodeではない)をupgradeNodesとして記憶している。(ScheduledForUpgradeの更新のされ方は後述して補足する)

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/k8shandler/cluster.go#L312-L328)

上記でupgradeNodeとして認識されたNodeはk8shandler.UpgradeStatus()にて、Reconcile処理が適用される。



## Reconcile処理内における、具体的な実装を確認

UpdateClusterStatus() 内にて、status.go内のupdateNodeConditions()を呼ぶことで、いよいよelasticsearch Nodeごとのreconcile処理に入る

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/k8shandler/status.go#L249-L406)

### Podの正常性を確認

#### Podがスケジュールされているかを確認

「Podがscheduleされていつつ、podのConditionがFalse」となっている場合に、podをunschedulableとしてフラグ付する。

unschedulableフラグがたった場合は後続処理である、Pod内のコンテナステータスを確認していく。

該当コードは下記

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/k8shandler/status.go#L285-L295)

#### Pod内コンテナのステータスを確認

Pod内のelasticsearchコンテナの、StateがWaitingかTerminatedかをまず確認する

この場合は、updatePodNotReadyCondition()を呼ぶことで、Podが壊れている正確なReasonとMessageを付加している。

もう１つのproxyコンテナについても同様に確認をしている。

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/k8shandler/status.go#L305-L370)

### ディスク使用率観点での正常性確認

コンテナの正常性確認が終わるとディスク使用量観点の確認を行う

ディスク使用量が閾値を超えているかどうかを確認し、Disk Watermark Highもしくは、Disk Watermark Lowなどに引っかかっているかを確認する。

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/k8shandler/status.go#L377-L402)



# まとめ

- Openshiftで用いられるElastichSearch Operatorの役割について、PodのReconcile部分に焦点を当てて実装を確認した。

- 実際のReconcile部分に限っていうとElasticsearch特有という部分は余り目立たず、他のOperatorのコードを読む上でも同様に読み進めるための材料として読むことは有用と感じる。

- Operatorhub.ioにおいてelasticsearch-operatorは公開されておらず、"Elastic Cloud on Kubernetes" Operatorが公開されているため、こちらとの関連やその差分などについても考察の余地があるが、OperatorHub.ioに登録されている方がよりデファクトであると現時点では考えられる。そのため、本記事で紹介したelasticsearch-operatorはOpenshiftに特化した内容になっていると考えるのが妥当である。

  https://operatorhub.io/operator/elastic-cloud-eck



# 参考資料

- Elasticsearch OperatorのGoDoc

[elasticsearch-operator - GoDoc](https://godoc.org/github.com/openshift/elasticsearch-operator)

- openshift/elasticsearch-operator Github

https://github.com/openshift/elasticsearch-operator

- openshiftのcluster-loggingのリポジトリ 

https://github.com/openshift/cluster-logging-operator

##【補足】ScheduledForUpgradeの更新のされ方

まずk8shandler package内のdeployment.goでdeploymentの状態を監視している

[openshift/elasticsearch-operator](https://github.com/openshift/elasticsearch-operator/blob/master/pkg/k8shandler/deployment.go#L101-L133)

node.isChanged()にて、純粋にdeploymentのdesiredとcurrentを比較することで、更新が必要かを判断している。

node.isChanged()がtrueとなった場合に、api.ElasticsearchNodeStatus.UpgradeStatus.ScheduledForUpgradeというフラグを立てることで、reconcilationLoop内で再起動処理をフックする仕組みになっている。