M3DBの知見をまとめてみた

この記事は [富士通クラウドテクノロジーズ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/fjct) の7日目の記事です。

昨日は @umiiiiins さんの「k8s上にクラスタリングされたPostgreSQLをデプロイする」でした。最近自分もk8sを触っていますが、色々難しいですね。。。こういった記事を読んでより理解を深めていきたいです。

さて、今回は最近M3DBを触る機会がありましたので、現状までの学んだ知見を簡単にまとめようと思います。

## 経緯

ある晴れた昼下がり、私は[prometheus](https://prometheus.io/)というプル型のリソース監視ソフトウェアをサーバーに構築する機会に恵まれました。
まずはprometheusの勉強をしつつ、監視したデータを保持するDBの選定を行っていました。ローカルのストレージを使わずに、別でDBを構築したかったため、以下のドキュメントを参考にしていたのですが、readとwriteどちらもできるDBは限られていました。(2019/12/6現在)
https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage

そこで、readとwriteどちらもできるDBで、かつ他のチームが先に検証を始めていたM3DBというDBを今回用いることにしました。

## M3DBとは

>M3DB, inspired by Gorilla and Cassandra, is a distributed time series database released as open source by Uber Technologies. It can be used for storing realtime metrics at long retention.

参考： https://m3db.github.io/m3/m3db/

上記から、M3DBとは `Uber Technologiesからオープンソースとしてリリースされている分散型時系列データベース` であることが分かります。
そのため、以下のようにgithubにコードも上がっています。
https://github.com/m3db/m3

## シングルノードの構築方法

シングルノード構築時は以下のM3DBの公式手順に沿って、dockerを使えばすぐに構築することができます。
https://m3db.github.io/m3/how_to/single_node/

この公式手順内にある、以下のようなcurlリクエストでdatabaseが構築されます。

```
curl -X POST http://localhost:7201/api/v1/database/create -d '{
  "type": "local",
  "namespaceName": "default",
  "retentionTime": "12h"
}'
```
このリクエスト時に指定している`namespaceName`がDB名になり、`retentionTime`がこのDBで保持するデータの保存期間にあたります。

加えて、シングルノード構築後、`localhost:7201/api/v1/openapi`にアクセスするとOpenAPIドキュメントが見れるとありますが、公式からも以下のリンクから提供されています。構築前に見たい場合はこちらを参照しましょう。
https://www.m3db.io/openapi/

## クラスタの構築方法

以下の公式手順を参考にして、クラスタを構築することができます。
https://m3db.github.io/m3/how_to/cluster_hard_way/

現在、自分はこの手順を参考にして、以下のようなアーキテクチャを組んで運用しています。

<img width="340" alt="m3db_cluster.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165167/e2ef8fa3-6f6b-6411-03cb-ed2978aa9dc2.png">

prometheusと各m3dbnodeにreadとwriteを行うm3coordinatorを同一ホストに、クラスタ構築するためのETCDとストレージ部分のm3dbnodeを各Seed Hostsに配置している構成です。
このような構成の他に、公式手順にあるような、m3dbnodeだけを持ったHostsをクラスタに組み込むことも可能です。

また、この公式手順を行った際、いくつか躓いた箇所がありますので、以降はそこについて説明していきます。

### カーネル周りの設定

公式手順の中にも書かれていますが、以下のドキュメントの`vm.max_map_count`や`rlimits`の設定が必要です。
https://m3db.github.io/m3/operational_guide/kernel_configuration/
こちらをしない場合、エラーログが出続けるため注意してください。

### クラスタ作成

`curl -X POST http://localhost:7201/api/v1/database/create` でNamespaceを作成する前に、クラスタの構築を行う必要があります。
公式手順の `Create Namespace and Initialize Topology` の章まで行ったら、Namespace作成前に、以下のドキュメントを参照してください。
https://m3db.github.io/m3/operational_guide/placement_configuration/

この手順内にある `curl -X POST localhost:7201/api/v1/services/m3db/placement/init` を先に実行して、Placementを初期化してクラスタを作成する必要があります。

### Namespace作成時の注意点

公式手順内にある `curl -X POST http://localhost:7201/api/v1/database/create` でNamespaceを作成する際、`retentionTime` の設定値に注意する必要があります。以下のissueにあるように、`retentionTime`の値が大きければ大きいほど、Namespace作成に時間を要します。
https://github.com/m3db/m3/issues/1733
（自分はretentionTimeを1年にして、他の設定項目をデフォルトのままにしたら、Namespace作成に1～2日かかりました）

こちらの対処法ですが、上記issueに参考となる計算方法が記載されています。
>17520 (retention hours) / 168 (blocksize M3DB will pick for 2 years of retention I think) * 64 (number of shards) * 7 (number of files to create per namespace/shard/block combination)

自分はデータの保持期間を1年などに伸ばしたかったので、計算式を参考に以下の2つの対応を行いました。

- 先ほどのクラスタ作成時、 `curl -X POST localhost:7201/api/v1/services/m3db/placement/init` で指定するパラメータ `num_shards` の値をできるだけ小さくする
- 以下のドキュメントの方のNamespace作成リクエストを使い、`blockSize` の2箇所のパラメータを `blockSizeDuration` という名称に変更して、時間をできるだけ長くする(ex. 24hなど)
    - https://m3db.github.io/m3/operational_guide/namespace_configuration/#advanced-hard-way

これらをすることで、Namespace作成までの時間はかなり短縮されると思います。

## 使ってみて

- まだ若いプロジェクトなのか、公式ドキュメントとリポジトリのissue以外の情報源が少ない
- M3DBはメモリに一旦書き込み内容を載せるため、CPUよりもメモリのリソースを優先的に振るのがよさそう
- ディスクIO性能もかなり求められるので、クラウド上で構築する方は性能が良いディスクを使うことを推奨します