退役コーダーおじさん、Elasticserchをお勉強:1(Elasticserch環境構築編)

まったくElasticserchを触ったことがない状態で構築から色々とやって行きます。ほぼ個人の備忘録です。
<!---
# Elasticserchを始める経緯

<details><summary>退役コーダあるある話です。長文となったので、読みたい人だけ読んでね(折りたたみ)</summary><div>

- 経緯。
  - トレンドの言語を動かす最新IDEやクラウドサービスを使用する最前線でのお役目からは引退となり、今度は自社が何も考えずに導入した２−3世代前の枯れたオンプレ系業務システム郡のマイグレ案件＋未だシステム化されていないウィルダネスなMS-Office製品文書群と紙管理データ群を含めたTo-Be対応にアサインが決まったよ。やったね！！
  - 正直マイグレ対応自体は、業務系APP提供支援を生業とするベンダーを間にかまして、利用者側要件のまとめと管理に注力すれば良い(自前で導入とかはありえないので、それ以外やりたくない)。
     - だが、マイグレ案件って成否が怪しい。そこまで有能なベンダーが来るという幸運のガチャを引けるはずもなく、業務担当も慣れ親しんだシステムからの移行を嫌がり難癖をつけて1−2年は既存システムと併走するケースが考えられる。
     - そうなった場合にこれもよくある話で「マイグレ成功箇所(仮で、新規システム側でデータを持つ「A」)」と、「マイグレがうまく行かず、当面既存システム間での運用(旧データ保存箇所となる「B」）」。このA-B間でデータを収集・管理・統計・相互連携を行う方式を求められるので、この手法を模索しておいた方が良いと判断(ここまでが前提)。
  - 上記データ集計作業で新旧システムのRDBMSやKVS参照と、連携不可能な場合にJSONダンプやCSVダンプをこねくり回す泥作業(*1)から、必要な集計結果を導き出す為に書く「複雑奇怪でメンテが嫌になる糞SQL(*2)」が、以前から気になっていたElasticserchと周辺ツールでスマートにビジネス分析等の実現ができないかのPoCをしてみたいと思います。
      - 個人的には*1と*2のどちらかが達成できれば導入の方向で検討中です。
</div></details>
--->
## 実行環境

- 前提条件
    - Elasticに関する理解度
      - 「[ELK strackって？](https://www.elastic.co/jp/what-is/elk-stack)」というページとそのリンク先[Elasticsearchについて](https://www.elastic.co/jp/what-is/elasticsearch)を読んだ程度です。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
　　 
    - 検証方法
      - Dockerとdocker-composeで再利用可能な環境構築を目指す。
      - Elasticserchの基本の「キ」もわかってないので、先ずは動く環境を手に入れてから、あーだこうだヤリたいと思う。
      - なるべく公式HPの情報をもとに手順検証する。
          - 書籍は発行されたタイミングの情報に閉じている為、誤記入の訂正も最新のトレンドに対応出来てないケースが過去に多々あったので。 
- HW
    - MacBook Pro(2018)
      - CPU:2.9GHz 6core
      - MEM:32GB(Dockerに4GB割り当て) [^1]
      - HDD:物理
- SW
  - MacOS Monterey(12.0.1)
  - Docker desktop(3.0.3)
      - Engin: 20.10.0
      - Compose: 1.27.4
  - elasticserch 7.16.3

- その他、bashでのコマンドalias設定 [^2]
  - d: docker
  - dc: docker-compose


[^1]: memory容量が足りないとes01/es02/es03とクラスター起動する筈のコンテナが一部起動しない事象が発生したのでデフォ値から少し増やしました。

[^2]: 説明文上では短縮表記でない正式なコマンドで記載しますが、コマンド実行結果は上記aliasのまま貼り付けを行うケースがある為、適宜読み替え要です。

## 環境構築
- ユーザーのHomeディレクトリに「elasticserch」ディレクトリを作成しそこで検証開始。
- 公式サイトの[このページ(Install Elasticsearch with Docker)](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-compose-file)でdocker-compose.yamlをコピペして、上記elasticserchディレクトリ配下に取得。
- とりあえずコマンド`docker-compose up`実行でコンテナ立ち上げてみたが失敗。。。
  - 問題点
      - es01が起動しない
          - 解決方法: portsの記述にクォート囲みがなかったので修正。
      - contenarが母艦側ディレクトリをmountする「volumes」パラメータ設定が絶対パスとなっている。
          - 解決方法: elasticserchディレクトリ配下に閉じたいので修正。
- 以下、上述の問題点を修正してcontener起動が可能となったdocker-compose.yaml    

```yaml
version: '2.2'
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.16.3
    container_name: es01
    environment:
      - node.name=es01
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      # data01->./data01に修正
      - ./data01:/usr/share/elasticsearch/data
    ports:
      # 公式コピペだとここが原因でなぜか起動しなかった(MAC環境独自の問題？原因不明)、クォート囲みに修正
      - "9200:9200"
    networks:
      - elastic
  es02:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.16.3
    container_name: es02
    environment:
      - node.name=es02
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      # 修正箇所
      - ./data02:/usr/share/elasticsearch/data
    networks:
      - elastic
  es03:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.16.3
    container_name: es03
    environment:
      - node.name=es03
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01,es02
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      # 修正箇所
      - ./data03:/usr/share/elasticsearch/data
    networks:
      - elastic

# ここの表記は要否不明(別途調査)
volumes:
  data01:
    driver: local
  data02:
    driver: local
  data03:
    driver: local

networks:
  elastic:
    driver: bridge
```

で、上記修正後に再度`docker-compose up -d`コマンドでelasticserchをバックグラウンド起動。

```bash
❯❯❯ dc up -d
Creating network "elasticserch_elastic" with driver "bridge"
Creating es03 ... done
Creating es02 ... done
Creating es01 ... done
```
コンテナを立ち上げたら一応`docker ps`コマンドでコンテナが適切に起動したか確認。

```bash
❯❯❯ d ps
CONTAINER ID   IMAGE                                                  COMMAND                  CREATED              STATUS              PORTS                              NAMES
47aa9219c2cc   docker.elastic.co/elasticsearch/elasticsearch:7.16.3   "/bin/tini -- /usr/l…"   About a minute ago   Up About a minute   9200/tcp, 9300/tcp                 es02
80202c1c86c7   docker.elastic.co/elasticsearch/elasticsearch:7.16.3   "/bin/tini -- /usr/l…"   About a minute ago   Up About a minute   0.0.0.0:9200->9200/tcp, 9300/tcp   es01
b5b324cb5a30   docker.elastic.co/elasticsearch/elasticsearch:7.16.3   "/bin/tini -- /usr/l…"   About a minute ago   Up About a minute   9200/tcp, 9300/tcp                 es03
```

起動後に公式チュートリアルのコマンド(`_cat/node`にリクエストを送信してノードが起動している事を確認)を実行。

```bash
~/elasticserch
❯❯❯ curl -X GET "localhost:9200/_cat/nodes?v=true&pretty"
ip         heap.percent ram.percent cpu load_1m load_5m load_15m node.role   master name
172.19.0.2           32          97   0    0.08    0.08     0.06 cdfhilmrstw -      es03
172.19.0.4           27          97   0    0.08    0.08     0.06 cdfhilmrstw *      es02
172.19.0.3           46          97   0    0.08    0.08     0.06 cdfhilmrstw -      es01
```

とりあえず、無事環境構築できた様子なので、[Quick start](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html)ページのサンプル「Add a single documentedit（単一のドキュメントを追加する)」を実行してみます。以下、ページの説明文をGoogle翻訳に掛けた物を引用

>　logs-my_app-default次のインデックス作成リクエストを送信して、データストリームに単一のログエントリを追加し ます。logs-my_app-default存在しないため、リクエストは組み込みのインデックステンプレートを使用して自動的に作成しますlogs-*-*。

よくわからんがindexっていうのがRDBMSで言うところのテーブルらしい。で、document(RDBMSでのレコードと思われる）を追加対象となるindexが存在しない場合には自動で作成してくれる様子っぽいです。この辺はjsonベースなのでmongodbと同じなのかな？POSTデータを送信するcurlコマンドをコピペ実行してみる。

```bash
❯❯❯ curl -X POST "localhost:9200/logs-my_app-default/_doc?pretty" -H 'Content-Type: application/json' -d'
{
  "@timestamp": "2099-05-06T16:21:15.000Z",
  "event": {
    "original": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
  }
}
'
```
尚、Quick startページではKibanaを使用して実行との記載があったが方法がわからないので別途調査。。。
参考までに上記curlコマンドをKibanaというGUIのツールで実行する場合、少し目に優しい構文でできるような記載でした。

```bash
# kibanaでのサンプル。HTTPメソッドと対象となるindex(/_docはお約束なのかな？)にINSERTする実データで良いらしい。
POST logs-my_app-default/_doc
{
  "@timestamp": "2099-05-06T16:21:15.000Z",
  "event": {
    "original": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
  }
}
```

上記curlコマンドのレスポンス値。

```bash
# curlコマンドの戻り値
{
  "_index" : ".ds-logs-my_app-default-2022.02.04-000001",
  "_type" : "_doc",
  "_id" : "npmIwn4BrcdMpf7_-mmB",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}
```
戻り値をざっと確認してみると"successful"の値が1で、"failed"の値は0。この２つのパラメーターの値はゼロイチでTrue/Falseを表現しているらしく結果として成功っぽい。なにやら"index"の値(テーブル名にあたる部分)として生成された名称は`ds-logs-my_app-default-2022.02.04-000001`となってます。で、idが発番されたらしい。その他"version"等もあるので更新履歴なのか、はたまた楽観ロックで使うのかは別途調査が必要ですが、とりあえずはデータ挿入成功です。
お次、このdocumentを表示する方法は？こちらもQuick Startページの[Search data](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html#qs-search-data)をコピペ実行してみる。

```bash
❯❯❯ curl -X GET "localhost:9200/logs-my_app-default/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": { }
  },
  "sort": [
    {
      "@timestamp": "desc"
    }
  ]
}
'
```

どうやらURIの第一ディレクティブに検索対象のindexを指定するらしい。以降第二ディレクティブで指定している'_search'はRESTfulの第一ディレクティブで指定したindexへのアクションの指定で_searchが検索アクションらしい。アクション名以降にクエストリンクとして渡している'pretty'は必須値ではないがJSON戻り値に成形オプション有効化を指してます(省略すると整形されない状態で戻る)。で、-dオプションで渡しているgetでのパラメータ。こちらもJSON形式でキー名称"query"で渡している値（ネストされた連想配列）のキー"match_all"の値(これも連想配列)に何も指定しないことにより、~~全てのdocumentを引っ張ってきているっぽい~~(訂正:defaultで10件指定で取得しているらしいです。SQLだとLimit/offset句が適用された状態かと)。で、キー名称"sort"の値が配列で複数のソート条件を渡せるっぽいね。これらを親の顔より見て慣れ親しんだSQLのDMLで書くとこんな感じだろうか。

```sql
SELECT * from `logs-my_app-default` LIMIT 10 OFFSET 0 order by `@timestamp` desc;
```

で、上記curlコマンド実行結果のレスポンス値

```json
{
  "took" : 26,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [
      {
        "_index" : ".ds-logs-my_app-default-2022.02.04-000001",
        "_type" : "_doc",
        "_id" : "npmIwn4BrcdMpf7_-mmB",
        "_score" : null,
        "_source" : {
          "@timestamp" : "2099-05-06T16:21:15.000Z",
          "event" : {
            "original" : "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
          }
        },
        "sort" : [
          4081767675000
        ]
      }
    ]
  }
}
```
ステータスやhits件数等もデータ内に含まれている。RESTfulなWebAPIだとデータに付随する情報はHeaderに`X-tortal-count`や`Link`等で持たせろとか[WebAPI設計のベストプラクティス](https://qiita.com/mserizawa/items/b833e407d89abd21ee72)で紹介されていた気がするが、httpベースでの用途だけではないから良いのかな？で、データは連想配列第一階層の'hits'Objectの中に同名称'hits'配列があり、この配列内にElasticserchのdocument毎にObjectとして格納される形かな？なんとなくJSONレスポンスの設計をSwaggerに貼ったら設計上良くない作りだと指摘されそうな構成ですが、まぁなんとなくレスポンス内容は掴めました。

一応複数データ挿入もサンプルがあったので実行。endpointのアクション名は'_balk'で、改行で区切られたJSON（NDJSON）というらしい。各データの終端記号は(\n)で、最後の行を含め(\n)終わる必要がありとの事。

```bash
curl -X PUT "localhost:9200/logs-my_app-default/_bulk?pretty" -H 'Content-Type: application/json' -d'
{ "create": { } }
{ "@timestamp": "2099-05-07T16:24:32.000Z", "event": { "original": "192.0.2.242 - - [07/May/2020:16:24:32 -0500] \"GET /images/hm_nbg.jpg HTTP/1.0\" 304 0" } }
{ "create": { } }
{ "@timestamp": "2099-05-08T16:25:42.000Z", "event": { "original": "192.0.2.255 - - [08/May/2099:16:25:42 +0000] \"GET /favicon.ico HTTP/1.0\" 200 3638" } }
'
```
(上記複数データ挿入の戻り値は省略してますが、無事に成功しております。)

とりあえず、アプリケーション起動とデータ単一挿入、複数挿入、データ抽出まで簡単に検証できたので今回はここまで。