ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)

シリーズ
[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (1)](https://qiita.com/nobuhikosekiya/items/dd2ce836b184730f8d70)
[ElasticsearchのFrozenデータティアにデータが入るのをテストしてみた (2)](https://qiita.com/nobuhikosekiya/items/8ab82e70953b6b5d1736)

# はじめに
こんにちは。Elasticのソリューションアーキテクトをしている関屋です。
Elasticsearchバージョン7.12からFrozenデータティアが使えるようになりました。Frozenティアを使えば、検索する頻度の少ない古いデータに関してはスナップショットとしてオブジェクトストレージに保管しつつも、そのまま検索可能できるようになりました。（Frozenを使うにはEnterpriseライセンスが必要です。）

（Opensearchでは同じ機能は存在しないので、ご注意ください）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109197/44a0ee84-8fc4-3294-cc9f-bb39fb837f82.png)
引用: https://www.elastic.co/blog/introducing-elasticsearch-frozen-tier-searchbox-on-s3

このブログでは、Frozenデータティアを使うための必要な最低限の設定と、実際にデータがFrozenに入る様子を確認していきます。環境はElastic Cloudのバージョン8.7のEnterpriseライセンスを使っています。

セルフマネージド（オンプレ）のElasticsearchでも、Minioなどのオブジェクトストレージを組むことで実現はできるものの、Elastic Cloudであれば手間なくFrozenを使うことができます。以下のようにFrozen data tierを設定するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109197/e0ae52fb-5ed0-b183-9bb0-4a1cde8280d0.png)


# テストステップの概要
詳細が長くなるので、本記事の構成として、こちらの概要編と、下の詳細手順編で分けてます。
### 1.準備: Data StreamとIndex Lifecycle Management(ILM)の理解
データティア構成を使う場合、基本的に２つの設定が必要です。ILM Policyと、Index Templateです。
ILM機能によって自動的にデータティア間のライフサイクルを管理できるようになります。例えば、HotのIndexがxx日以上経過あるいはxxGBのサイズ以上になったら、次のWarm/Cold/Frozenのいずれかのティアに移動するなど。この設定をILM Policyとしてユーザーが定義します。ILMを使い際は、v7.9で登場したData Streamと共に使うのが推奨です。Data StreamはIndexのようなものなのですが、IndexをBacking Indexとしてロールオーバーして扱うことができます。詳しくは下の文書を参考にしてください。
https://www.elastic.co/blog/an-introduction-to-the-elastic-data-stream-naming-scheme
https://www.elastic.co/guide/en/elasticsearch/reference/current/data-streams.html

Data Streamを使うために、Index Templateの作成が必要です。これは、通常のIndexであればIndex SettingsやIndex Mapppingを定義するのと同じように、Data Streamの同等の設定をIndex Templateとして作成します。（TemplateからBacking Indexとして一つ以上のIndexがData Streamの中で作られるわけです）

ここまで理解した上で、概要レベルですが、どのようにデータが流れていくかの例を以下に説明します。
1. クライアントからData Stream(名前はmydataとします）にデータを投入
1. Data Streamの内部で隠れIndexが .ds-mydata-2023.05.26-000001のような名前で作成され、実際のデータはこのIndexに入る
2. ILM Policyに定義されたロールオーバー条件までデータが溜まったら、この１世代目のIndex(.ds-mydata-2023.05.26-000001)は書き込み対象ではなくなり、新たな2世代目のIndexが.ds-mydata-2023.05.27-000002のような名前で新たに作成され、次からはそこにデータが書き込まれるようになります。
3. 1世代目のIndex(.ds-mydata-2023.05.26-000001)はILM Policyの定義に従い、Warm/Cold/Frozenなどに移動されます。


### 2.Index TemplateとIndex Lifecycle Policyの作成
ここからは作業としてやることの概要をまとめます。詳しいコードは下の手順詳細の方に記載しています。
1. Index Lifecycle Policyの作成
1. Index Templateの作成
このテンプレート内でData Streamを有効化し、また作ったILM Policyを紐づけておきます。

### 3. Elasticsearchにデータを投入し、データティア間の移動を確認
1. データをData Streamに投入
1. Hotデータティアにデータが保存されていることを確認
1. ロールオーバー条件に合致するまでデータをさらにData Streamに投入し続ける
1. Data StreamのIndexがロールオーバーされたことを確認
1. ロールオーバーされたIndexがFrozenデータティアへの移動したことを確認

# 手順詳細

では、実際の手順です。こちらのコードに関してはKibanaのDev Toolsから実行できるコマンドとして用意しています。

### ILM Policyの作成
Dev Toolsから以下を実行してください。ilm_mylogsはポリシー名なので、任意の名前をつけてください。
```
PUT /_ilm/policy/ilm_mylogs
{
    "policy": {
        "phases": {
            "hot": {
                "actions": {
                    "rollover": {
                        "max_docs": 10
                    }
                }
            },
            "frozen": {
                "min_age": "0d",
                "actions": {
                    "searchable_snapshot": {
                        "snapshot_repository": "found-snapshots"
                    }
                }
            },
            "delete": {
                "min_age": "365d",
                "actions": {
                    "delete": {
                        "delete_searchable_snapshot": "true"
                    }
                }
            }
        }
    }
}
```
上記では下のようにライフサイクルを定義しています。
* Index内のドキュメント数が10を超えるとIndexのロールオーバーを行う　（　テストでロールオーバーが簡単に行われるような条件に設定していますが、普通はこのような条件にしないでくださいw）
* ロールオーバーの直後（0days)でFrozenに移動する
* ロールオーバーの365日後にIndexを削除する

### Index Templateの作成
テンプレート名mylogs_templateとして作成します。これも任意の名前でいいです。
```
PUT /_index_template/mylogs_template
{
    "index_patterns": ["mylogs*"],
    "priority": 100,
    "data_stream": {},
    "template": {
        "settings": {
            "index": {
                "lifecycle": {
                    "name": "ilm_mylogs"
                },
                "routing": {
                    "allocation": {
                        "include": {
                            "_tier_preference": "data_hot"
                        }
                    }
                },
                "number_of_shards": "1",
                "number_of_replicas": "0"
            }
        },
        "mappings": {
            "properties": {
                "@timestamp": {
                    "type": "date"
                },
                "message": {
                    "type": "text"
                }
            }
        },
        "aliases": {}
    }
}
```
このテンプレートのポイント
* Index Templateに、ILMポリシーをリンクする
```
                "lifecycle": {
                    "name": "ily_mylogs"
                },
```
* Data Streamを有効化
```
"data_stream": {}
```
* このIndexテンプレートを適用してほしいData Stream名の文字列パターンを設定
```
"index_patterns": ["mylogs*"],
```


### データをData Streamに投入

宛先のData Stream名を指定してドキュメントをPostします。Index_patterns (mylogs*)にマッチするData Stream名（例：mylogs-test01)であれば何でも良いです。

```
POST /mylogs-test01/_doc
{
    "@timestamp": "2023-01-01T00:00:00+0900",
    "message": "hello"
}
```

### Hotデータティアにデータが保存されていることを確認
統計値を確認する前は必ず_refreshを実行し、最新の数値を確認できるようにします。
```
POST /mylogs-test01/_refresh
```

mylogs-test01のShard情報を確認すると、データの格納ノードを確認できます。
```
GET /_cat/shards/mylogs-test01?format=yaml
```

```:出力結果
---
- index: ".ds-mylogs-test01-2023.05.26-000001"
  shard: "0"
  prirep: "p"
  state: "STARTED"
  docs: "1"
  store: "3.7kb"
  ip: "10.46.32.91"
  node: "instance-0000000014"


```
下のコマンドにて、instance-0000000014がHotデータティアのノードであることがわかります。(node.roleに'h'のシンボルがあるのがHot)

```
GET /_cat/nodes?v
```

```: 出力結果
ip           heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
10.46.32.102           51         100   0    0.68    1.88     2.98 lr        -      instance-0000000002
10.46.47.224           79         100   7    1.42    1.94     2.38 himrst    *      instance-0000000013
10.46.32.163           32          93   0    0.72    0.85     0.97 rw        -      instance-0000000003
10.46.32.91            72         100   6    1.79    2.44     2.79 himrst    -      instance-0000000014
10.46.32.26            49         100   0    4.36    2.74     2.21 mv        -      tiebreaker-0000000012
10.46.32.209           36          66   0    0.43    0.49     0.59 f         -      instance-0000000015

```

現状のIndexのライフサイクルの状態をexplainコマンドで確認します。
```
GET /mylogs-test01/_ilm/explain
```
```:出力結果
{
  "indices": {
    ".ds-mylogs-test01-2023.05.26-000001": {
      "index": ".ds-mylogs-test01-2023.05.26-000001",
      "managed": true,
      "policy": "ilm_mylogs",
      "index_creation_date_millis": 1685080936153,
      "time_since_index_creation": "3.04m",
      "lifecycle_date_millis": 1685080936153,
      "age": "3.04m",
      "phase": "hot",
      "phase_time_millis": 1685080936617,
      "action": "rollover",
      "action_time_millis": 1685080936617,
      "step": "check-rollover-ready",
      "step_time_millis": 1685080936617,
      "phase_execution": {
        "policy": "ilm_mylogs",
        "phase_definition": {
          "min_age": "0ms",
          "actions": {
            "rollover": {
              "max_docs": 10
            }
          }
        },
        "version": 2,
        "modified_date_in_millis": 1685079122762
      }
    }
  }
}
```
ポイント
* Indexの"phase": "hot"である
* "step": "check-rollover-ready"というところで、このIndexがロールオーバー前であり、ロールオーバー条件に合致するかのチェックがされていることがわかります


### ロールオーバー条件に合致するまでデータをData Streamに投入


以下のPOSTを繰り返し、Index内のドキュメントが10を超えるようにします。
```
POST /mylogs-test01/_doc
{
    "@timestamp": "2023-01-01T00:00:00+0900",
    "message": "hello"
}
```

```
POST /mylogs-test01/_refresh
```

```
GET /mylogs-test01/_ilm/explain
```

```:explain出力結果
{
  "indices": {
    ".ds-mylogs-test01-2023.05.26-000001": {
      "index": ".ds-mylogs-test01-2023.05.26-000001",
      "managed": true,
      "policy": "ilm_mylogs",
      "index_creation_date_millis": 1685080936153,
      "time_since_index_creation": "6.81m",
      "lifecycle_date_millis": 1685080936153,
      "age": "6.81m",
      "phase": "hot",
      "phase_time_millis": 1685080936617,
      "action": "rollover",
      "action_time_millis": 1685080936617,
      "step": "check-rollover-ready",
      "step_time_millis": 1685080936617,
      "phase_execution": {
        "policy": "ilm_mylogs",
        "phase_definition": {
          "min_age": "0ms",
          "actions": {
            "rollover": {
              "max_docs": 10
            }
          }
        },
        "version": 2,
        "modified_date_in_millis": 1685079122762
      }
    }
  }
}
```
ポイント
* Rollover条件(max_docs:10)を満たしているが、この時点ではまだphase: Hotかつstep: check-rollover-readyと、ロールオーバーが発生していない。

ロールオーバーは`indices.lifecycle.poll_interval`（デフォルト10分）のサイクル毎にチェックされ、行われるので、10分経過下あたりにもう一度チェックしましょう。

```
GET /mylogs-test01/_ilm/explain
```

```:explain出力
{
  "indices": {
    ".ds-mylogs-test01-2023.05.26-000002": {
      "index": ".ds-mylogs-test01-2023.05.26-000002",
      "managed": true,
      "policy": "ilm_mylogs",
      "index_creation_date_millis": 1685081592247,
      "time_since_index_creation": "11.71s",
      "lifecycle_date_millis": 1685081592247,
      "age": "11.71s",
      "phase": "hot",
      "phase_time_millis": 1685081592618,
      "action": "rollover",
      "action_time_millis": 1685081593018,
      "step": "check-rollover-ready",
      "step_time_millis": 1685081593018,
      "phase_execution": {
        "policy": "ilm_mylogs",
        "phase_definition": {
          "min_age": "0ms",
          "actions": {
            "rollover": {
              "max_docs": 10
            }
          }
        },
        "version": 2,
        "modified_date_in_millis": 1685079122762
      }
    },
    ".ds-mylogs-test01-2023.05.26-000001": {
      "index": ".ds-mylogs-test01-2023.05.26-000001",
      "managed": true,
      "policy": "ilm_mylogs",
      "index_creation_date_millis": 1685080936153,
      "time_since_index_creation": "11.13m",
      "lifecycle_date_millis": 1685081592218,
      "age": "11.74s",
      "phase": "frozen",
      "phase_time_millis": 1685081593218,
      "action": "searchable_snapshot",
      "action_time_millis": 1685081593418,
      "step": "wait-for-shard-history-leases",
      "step_time_millis": 1685081593418,
      "phase_execution": {
        "policy": "ilm_mylogs",
        "phase_definition": {
          "min_age": "0d",
          "actions": {
            "searchable_snapshot": {
              "snapshot_repository": "found-snapshots",
              "force_merge_index": true
            }
          }
        },
        "version": 2,
        "modified_date_in_millis": 1685079122762
      }
    }
  }
}
```
上のポイント
* ロールオーバーが行われて、*-000001のIndexは"phase": "frozen"に変化し、"step": "wait-for-shard-history-leases"となっています。
* 2世代目のIndex *-000002が新たに作成され、こちらは"phase": "hot"です。

```
GET /_cat/shards/mylogs-test01?format=yaml
```

```:出力
---
- index: ".ds-mylogs-test01-2023.05.26-000002"
  shard: "0"
  prirep: "p"
  state: "STARTED"
  docs: "0"
  store: "225b"
  ip: "10.46.47.224"
  node: "instance-0000000013"
- index: ".ds-mylogs-test01-2023.05.26-000001"
  shard: "0"
  prirep: "p"
  state: "STARTED"
  docs: "12"
  store: "8.1kb"
  ip: "10.46.32.91"
  node: "instance-0000000014"
```
上のポイント
* 1世代目の*-000001のIndexはexplain結果ではphaseがfrozenであったものの、まだ実際にはHotノードinstance-0000000014に居ました。

PhaseがFrozenになった際に、"step": "wait-for-shard-history-leases"でしばらく状態が止まるのがここで観察されました。これはどうやら下の文書に書いてある、Frozenに実際にデータを移動する前に、ShardのHistoryの有効期限を待つ必要があるためのようです。
https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-history-retention.html

次、7分後くらいにまたexplainを実行してみました。1世代目のFrozenフェーズのstepが"step": "segment-count"に進んでました。
```:exlain出力
{
  "indices": {
    ".ds-mylogs-test01-2023.05.26-000002": {
      "index": ".ds-mylogs-test01-2023.05.26-000002",
      "managed": true,
      "policy": "ilm_mylogs",
      "index_creation_date_millis": 1685081592247,
      "time_since_index_creation": "7.53m",
      "lifecycle_date_millis": 1685081592247,
      "age": "7.53m",
      "phase": "hot",
      "phase_time_millis": 1685081592618,
      "action": "rollover",
      "action_time_millis": 1685081593018,
      "step": "check-rollover-ready",
      "step_time_millis": 1685081593018,
      "phase_execution": {
        "policy": "ilm_mylogs",
        "phase_definition": {
          "min_age": "0ms",
          "actions": {
            "rollover": {
              "max_docs": 10
            }
          }
        },
        "version": 2,
        "modified_date_in_millis": 1685079122762
      }
    },
    ".ds-mylogs-test01-2023.05.26-000001": {
      "index": ".ds-mylogs-test01-2023.05.26-000001",
      "managed": true,
      "policy": "ilm_mylogs",
      "index_creation_date_millis": 1685080936153,
      "time_since_index_creation": "18.46m",
      "lifecycle_date_millis": 1685081592218,
      "age": "7.53m",
      "phase": "frozen",
      "phase_time_millis": 1685081593218,
      "action": "searchable_snapshot",
      "action_time_millis": 1685081593418,
      "step": "segment-count",
      "step_time_millis": 1685081892377,
      "phase_execution": {
        "policy": "ilm_mylogs",
        "phase_definition": {
          "min_age": "0d",
          "actions": {
            "searchable_snapshot": {
              "snapshot_repository": "found-snapshots",
              "force_merge_index": true
            }
          }
        },
        "version": 2,
        "modified_date_in_millis": 1685079122762
      }
    }
  }
}
```

さらに、3分後くらいにまたexplainを実行してみました。1世代目のIndex名が変わり、partial-*というプリフィックスがつきました。"phase": "frozen", "action": "complete", "step": "complete"とあり、Frozenへの移行が完了したのがわかります。
```
{
  "indices": {
    "partial-.ds-mylogs-test01-2023.05.26-000001": {
      "index": "partial-.ds-mylogs-test01-2023.05.26-000001",
      "managed": true,
      "policy": "ilm_mylogs",
      "index_creation_date_millis": 1685080936153,
      "time_since_index_creation": "21.4m",
      "lifecycle_date_millis": 1685081592218,
      "age": "10.47m",
      "phase": "frozen",
      "phase_time_millis": 1685081593218,
      "action": "complete",
      "action_time_millis": 1685081593418,
      "step": "complete",
      "step_time_millis": 1685082195648,
      "repository_name": "found-snapshots",
      "snapshot_name": "2023.05.26-.ds-mylogs-test01-2023.05.26-000001-ilm_mylogs-di1fqot-qzkqvg1hoqkwzw",
      "phase_execution": {
        "policy": "ilm_mylogs",
        "phase_definition": {
          "min_age": "0d",
          "actions": {
            "searchable_snapshot": {
              "snapshot_repository": "found-snapshots",
              "force_merge_index": true
            }
          }
        },
        "version": 2,
        "modified_date_in_millis": 1685079122762
      }
    },
    ".ds-mylogs-test01-2023.05.26-000002": {
      "index": ".ds-mylogs-test01-2023.05.26-000002",
      "managed": true,
      "policy": "ilm_mylogs",
      "index_creation_date_millis": 1685081592247,
      "time_since_index_creation": "10.47m",
      "lifecycle_date_millis": 1685081592247,
      "age": "10.47m",
      "phase": "hot",
      "phase_time_millis": 1685081592618,
      "action": "rollover",
      "action_time_millis": 1685081593018,
      "step": "check-rollover-ready",
      "step_time_millis": 1685081593018,
      "phase_execution": {
        "policy": "ilm_mylogs",
        "phase_definition": {
          "min_age": "0ms",
          "actions": {
            "rollover": {
              "max_docs": 10
            }
          }
        },
        "version": 2,
        "modified_date_in_millis": 1685079122762
      }
    }
  }
}
```

次、Shardの状態を確認すると、instance-0000000014(Hotノード）にあったものが、instance-0000000015（Frozenノードに移ったことがわかります）
```
GET /_cat/shards/mylogs-test01?format=yaml
```

```:output
---
- index: "partial-.ds-mylogs-test01-2023.05.26-000001"
  shard: "0"
  prirep: "p"
  state: "STARTED"
  docs: "12"
  store: "0b"
  ip: "10.46.32.209"
  node: "instance-0000000015"
- index: ".ds-mylogs-test01-2023.05.26-000002"
  shard: "0"
  prirep: "p"
  state: "STARTED"
  docs: "0"
  store: "225b"
  ip: "10.46.47.224"
  node: "instance-0000000013"
```

# まとめ
いかがでしたでしょうか。

実際にFrozenデータティアを使いたいと思っても、Frozenノードをプロビジョンしただけではデフォルトではデータは入っていきませんので、今回の手順を参考にILMの設定、Data Streamの設定をしてください。

なお、Elastic AgentのIntegrationsを使ったログ収集では、自動的にIndex TemplateとData Streamが構成され、ILMも'logs'という名前のILM Policyが自動的に作成されています。ですので、この場合は'logs'のILM Policyを変更してFrozenを有効にするか、あるいは少し手間ですが、Policyを別に作り、Index Templateがその新しいPolicyを使うように変更する形となります。