CLOVER🍀

That was when it all began.

ElasticsearchのIndex Lifecycle Management(ILM)を使って、指定された期間を過ぎたインデックスを削除する

これは、なにをしたくて書いたもの?

Elasticsearch 6.6.0から、Index Lifecycle Management(ILM)というものが入っているようなので、こちらを試してみようかなと。

Release Notes / Elasticsearch 6.6.0 / New features

Index Lifecycle Management
・ Adds Index lifecycle feature #35193

以前は、Curatorというものがこの役割を行っていたみたいです。

Curator Reference [5.8] | Elastic

Index Lifecycle Management(ILM)とは?

Index Lifecycle Management(略してILM)について。

Managing the index lifecycle | Elasticsearch Reference [7.5] | Elastic

インデックスの管理の自動化の仕組みのようです。インデックステンプレートに、ILMを使って定義したライフサイクルポリシーを
紐付けることで、インデックステンプレートを元に作成されるインデックスにポリシーを紐付けます。この結果、指定されたスケジュールで
特定のアクションを実行したりすることができるようになったりします。

ILMでは、インデックスのライフサイクルを以下の4つのフェーズで管理します。

  • Hot … このフェーズのインデックスは、よく更新、検索される
  • Warm … インデックスはもう更新されなくなっているが、検索は行われる
  • Cold … インデックスは更新もされないし、ほとんど検索もされない。検索自体は可能であるが、速度が遅くなっていてもかまわない
  • Delete … このフェーズのインデックスは、もはや不要になっていて、削除しても大丈夫なもの

最初は、Hotフェーズで、上から下に向かって遷移します。

これらのフェーズの役割は目安のようなものみたいですが、各フェーズにいつ移行するかはライフサイクルポリシーで定めます。

各フェーズにはアクションを紐付けることができ、どのフェーズでどのようなアクションが実行可能かは決まっています。

Actions | Elasticsearch Reference [7.5] | Elastic

特定のフェーズでしか、実行できないアクションもあります。

たとえばHotフェーズではRolloverを行う、Warmフェーズではインデックスのシュリンクを行う、Deleteフェーズではインデックスの
削除を行ったりします。

クラスタ内で、各フェーズに対するノードを割り当てることで、同一クラスタ内でもHotフェーズのインデックスを持つノードは
スペックを高めに、WarmやColdフェーズのインデックスを持つノードは、Hotフェーズを持つノードと比べて低めのスペックで
構成する、といったことも可能なようです。

Elasticsearchでインデックスライフサイクル管理を使用してHot-Warm-Coldアーキテクチャーを実装 | Elastic Blog

ILMかCuratorか?

Curatorについて調べていくと、ILMと用途が被っているようにも思います。

Curator and Index Lifecycle Management | Curator Reference [5.8] | Elastic

そのあたりは、ドキュメントにも記載があります。

ILM or Curator? | Curator Reference [5.8] | Elastic

Basicライセンス以上を使う場合は、CuratorよりもILMを使った方がよいみたいです。

If ILM provides the functionality to manage your index lifecycle, and you have at least a Basic license, consider using ILM in place of Curator.

Filebeat 7.0以降は、デフォルトでILMを使用するようですし

Starting with version 7.0, Filebeat uses index lifecycle management by default when it connects to a cluster that supports lifecycle management.

LogstashもILMを使用するようです。

Logstash can use Index Lifecycle Management to automate the management of indices over time.

デフォルトで、ILMが利用可能かどうかの検出もするみたいですしね。

The use of Index Lifecycle Management is controlled by the ilm_enabled setting. By default, this will automatically detect whether the Elasticsearch instance supports ILM, and will use it if it is available.

CuratorとILMの併用も可能なようです。

ILM and Curator! | Curator Reference [5.8] | Elastic

併用する場合、allow_ilm_indicesオプションを指定しない限りは、CuratorはILMポリシーに関連付けられたインデックスにはなにもしない
ようです。

ILMとCuratorの大きな違いは、ILMがElasticsearch内で動作する仕組みなのに対して、CuratorはElasticsearchの外で動作する仕組み
ということでしょうか。Curatorを使う場合は、別途実行のためのジョブ(たとえばcronなど)を設定することになります。

試してみる

と、ここまでつらつら書いてきましたが、動かしてみないとよくわからないので、実際に試してみたいと思います。

環境

今回の環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:   bionic


$ java --version
openjdk 11.0.5 2019-10-15
OpenJDK Runtime Environment (build 11.0.5+10-post-Ubuntu-0ubuntu1.118.04)
OpenJDK 64-Bit Server VM (build 11.0.5+10-post-Ubuntu-0ubuntu1.118.04, mixed mode, sharing)

Elasticsearchは、7.5.1を使います。

$ curl localhost:9200
{
  "name" : "ubuntu1804.localdomain",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "SZLLMXCmRjCZlo1SfMd7KA",
  "version" : {
    "number" : "7.5.1",
    "build_flavor" : "default",
    "build_type" : "deb",
    "build_hash" : "3ae9ac9a93c95bd0cdc054951cf95d88e1e18d96",
    "build_date" : "2019-12-16T22:57:37.835892Z",
    "build_snapshot" : false,
    "lucene_version" : "8.3.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

また、Elasticsearchが動作しているサーバーのIPアドレスは、192.168.33.12とします。

このIPアドレスは、あとで作成する、データ投入用のプログラムを動作させる時のみ使用します。

お題

Hotフェーズの例だとすぐRolloverとか出てきますが、ここはシンプルに指定の期間がすぎたらインデックスを削除する、というケースで
試します。

まずは、インデックスポリシーを登録します。

Set up index lifecycle management policy | Elasticsearch Reference [7.5] | Elastic

登録するインデックスポリシーは、こんな感じで作成。インデックスポリシーの名前は、「my_index_policy」とします。

$ curl -XPUT -H 'Content-Type: application/json' localhost:9200/_ilm/policy/my_index_policy -d '
{
    "policy": {                       
        "phases": {
            "hot": {
                "min_age": "1m",          
                "actions": {}
            },
            "warm": {
                "min_age": "3m",          
                "actions": {}
            },
            "delete": {
                "min_age": "7m",           
                "actions": {
                    "delete": {}              
                }
            }
        }
    }
}
'

今回は、Hot、Warm、Deleteの3つのフェーズを使うことにします。Coldフェーズは書かないので、使われません(遷移もしません)。

min_ageは、デフォルトでは「インデックスを作成してからの時間」を指します。

Timing | Elasticsearch Reference [7.5] | Elastic

min_age is usually the time elapsed from the time the index is created, unless the index.lifecycle.origination_date index setting is configured, in which case the min_age will be the time elapsed since that specified date. If the index is rolled over, then min_age is the time elapsed from the time the index is rolled over. The intention here is to execute following phases and actions relative to when data was written last to a rolled over index.

なので、インデックスを作成してから1分後にHotフェーズへ、3分後にWarmフェーズへ、7分後にDeleteフェーズへと移っていきます。
※ちなみに、Hotになる前は「New」というフェーズになります

Deleteフェーズでは、アクションとしてインデックスの削除を行います。それ以外のフェーズでは、なにもアクションを行いません。

ドキュメントではWarmフェーズでインデックスをシュリンクするアクションを設定したり、

Timing | Elasticsearch Reference [7.5] | Elastic

HotフェーズでRolloverを行ったりするような設定例が記載されています。

Full Policy | Elasticsearch Reference [7.5] | Elastic

続いて、インデックステンプレートを登録し、この時にインデックスポリシーを紐付けます。

Applying a policy to an index template | Elasticsearch Reference [7.5] | Elastic

「index.lifecycle.name」で、今回作成したインデックスポリシーを指定しています。

$ curl -XPUT -H 'Content-Type: application/json' localhost:9200/_template/my_index_template -d '
{
    "index_patterns": ["my_index-*"],
    "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 0,
        "index.lifecycle.name": "my_index_policy"
    }
}
'

インデックスを作る時に、インデックスポリシーを紐付けることもできるようです。

Apply a policy to a create index request | Elasticsearch Reference [7.5] | Elastic

ちなみに、ILMはKibanaで管理することができます。

f:id:Kazuhira:20200109011945p:plain

作成も、Kibana上で行うことができます。

f:id:Kazuhira:20200108235939p:plain

f:id:Kazuhira:20200109000113p:plain

なのですが、指定できる時間の単位が日、時間と待ち時間が大きくなるので、今回は直接ElasticsearchにJSONで放り込むことにしました。

フェーズの遷移の管理を、分単位で設定していますからね。

次に、用意したインデックステンプレートを使って、インデックスを作り続けるサンプルプログラムを作りましょう。

Pythonスクリプトを書くことにします。

$ pip3 install elasticsearch

インストールしたElasticsearchライブラリのバージョンと、Pythonのバージョン。

$ pip3 freeze
elasticsearch==7.1.0
pkg-resources==0.0.0
urllib3==1.25.7


$ python3 -V
Python 3.6.9

用意したプログラムはこちら。 ilm_example.py

from datetime import datetime
from elasticsearch import Elasticsearch
import time

index_name_prefix = "my_index"
index_alias = index_name_prefix
sleep_sec = 10

elasticsearch_url = "http://192.168.33.12:9200"

es = Elasticsearch([elasticsearch_url])

while True:
    now = datetime.now()
    now_str = now.strftime('%Y-%m-%d-%H.%M')
    index_name = f"{index_name_prefix}-{now_str}"

    id = int(now.timestamp())

    doc = {"message": f"Hello!! {now_str}"}

    es.index(index = index_name, id = id, body = doc)

    print(f"write {now_str}...")

    time.sleep(sleep_sec)

10秒に1回データを投入しつつ、1分ごとに異なるインデックスを作成するプログラムになります。

実行。

$ python3 ilm_example.py

インデックスにデータが書き込まれていきます。

write 2020-01-09-01.21...
write 2020-01-09-01.21...
write 2020-01-09-01.22...
write 2020-01-09-01.22...

cat APIで、見てみると

$ watch curl -s localhost:9200/_cat/indices?v

インデックスが増えていく様子が確認できます。

Every 2.0s: curl -s localhost:9200/_cat/indices?v                                                                                       ubuntu1804.localdomain: Wed Jan  8 16:22:58 2020

health status index                     uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   .kibana_task_manager_1    HEDAc98pTYiPDjh1yJWbyg   1   0          2            1     51.7kb         51.7kb
green  open   .apm-agent-configuration  bpB7peSqRXmV4lDTDIwzxw   1   0          0            0       283b           283b
green  open   my_index-2020-01-09-01.21 gGs-aFvVQjOtQngIhgdCIw   1   0          2            0      6.9kb          6.9kb
green  open   .kibana_1                 Oyyp7IHlRaKvitTwCYI-_A   1   0          5            0     13.3kb         13.3kb
green  open   my_index-2020-01-09-01.22 eaSRXIwFSYqv11UppfWWsg   1   0          3            0     10.3kb         10.3kb

Kibanaの「Index Management」で見ると、できたばかりのインデックスは「New」と表示されます。

f:id:Kazuhira:20200109012353p:plain

ドキュメントと違って、フェーズではなくフェーズになってますが…。

1分経過すると「Hot」になります。

f:id:Kazuhira:20200109012448p:plain

3分経ったインデックスは「Warm」になり

f:id:Kazuhira:20200109012540p:plain

7分経過したインデックスは「Delete」になります。

f:id:Kazuhira:20200109012938p:plain

この情報は、Kibanaでなくとも、APIで確認できます。

Explain lifecycle API | Elasticsearch Reference [7.5] | Elastic

$ curl localhost:9200/my_index-2020-01-09-01.23/_ilm/explain?pretty
{
  "indices" : {
    "my_index-2020-01-09-01.23" : {
      "index" : "my_index-2020-01-09-01.23",
      "managed" : true,
      "policy" : "my_index_policy",
      "lifecycle_date_millis" : 1578500580240,
      "age" : "3.8m",
      "phase" : "warm",
      "phase_time_millis" : 1578500763864,
      "action" : "complete",
      "action_time_millis" : 1578500580566,
      "step" : "complete",
      "step_time_millis" : 1578500763864,
      "phase_execution" : {
        "policy" : "my_index_policy",
        "phase_definition" : {
          "min_age" : "3m",
          "actions" : { }
        },
        "version" : 1,
        "modified_date_in_millis" : 1578500401430
      }
    }
  }
}

今、どのフェーズにいるか(phase)、min_ageの計算に使われるタイムスタンプ(lifecycle_date_millis)、インデックスを作成してから
どれくらい経過したか(age)、今のフェーズに入った時の時間(phase_time_millis)などを確認することができます。

ところで、「Delete」フェーズに入ったインデックスですが、今回の設定ではすぐに消えるわけではありません。

Every 2.0s: curl -s localhost:9200/_cat/indices?v                                                                                       ubuntu1804.localdomain: Wed Jan  8 16:33:47 2020

health status index                     uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   my_index-2020-01-09-01.30 Iz9JH9ifTtOPzG39WRgYLQ   1   0          3            0     10.3kb         10.3kb
green  open   .apm-agent-configuration  bpB7peSqRXmV4lDTDIwzxw   1   0          0            0       283b           283b
green  open   .kibana_1                 Oyyp7IHlRaKvitTwCYI-_A   1   0          6           11     11.2kb         11.2kb
green  open   my_index-2020-01-09-01.29 6OkP64XRRCmun8Ge4WyPTQ   1   0          3            0     10.3kb         10.3kb
green  open   my_index-2020-01-09-01.28 MhhcLFEtQgW8m_QcBat76Q   1   0          3            0     10.3kb         10.3kb
green  open   .kibana_task_manager_1    HEDAc98pTYiPDjh1yJWbyg   1   0          2            1     51.7kb         51.7kb
green  open   my_index-2020-01-09-01.25 rW-_yzWcSD2Vgb6pSVjCVA   1   0          6            0       14kb           14kb
green  open   my_index-2020-01-09-01.24 ErkpWv7bSuO1M2moNrYMtw   1   0          6            0       14kb           14kb
green  open   my_index-2020-01-09-01.27 najDdeJYTTKCb--X2g0qLA   1   0          6            0       14kb           14kb
green  open   my_index-2020-01-09-01.26 0SICmJlPQX2JNFv5oDA5Dw   1   0          6            0       14kb           14kb
green  open   my_index-2020-01-09-01.32 tP1lQEyHT0aEw8YQ6vCPWQ   1   0          3            0     10.3kb         10.3kb
green  open   my_index-2020-01-09-01.21 gGs-aFvVQjOtQngIhgdCIw   1   0          2            0      7.1kb          7.1kb
green  open   my_index-2020-01-09-01.31 yw7Idha-RECTZnF9Ztu7uA   1   0          3            0     10.3kb         10.3kb
green  open   my_index-2020-01-09-01.23 mTwSYlBARqqf5zrsVCN4Mg   1   0          6            0       14kb           14kb
green  open   my_index-2020-01-09-01.22 eaSRXIwFSYqv11UppfWWsg   1   0          5            0       14kb           14kb
green  open   my_index-2020-01-09-01.33 6Chi4ylkTm2DGL39IKmP_w   1   0          3            0     10.3kb         10.3kb

「Delete」フェーズに入った、つまり作成してから7分経過したインデックスも、残っています。

これは、ILMがポリシーに対してインデックスを確認するタイミングが、デフォルトで10分間隔だからです。

Index lifecycle management settings | Elasticsearch Reference [7.5] | Elastic

「indices.lifecycle.poll_interval」という設定です。

今回は、分刻みでmin_ageを設定しているので、しばらく残るように見えてしまいます。

この間隔をたとえば1分間隔にして、すぐにElasticsearchに確認してもらうには、elasticsearch.ymlに以下のように設定して
Elasticsearchを再起動します。

indices.lifecycle.poll_interval: "1m"

こうすると、今回の設定では7分を超えて「Delete」フェーズになったインデックスは割とすぐに消えていきます。

とはいえ、今回のように分単位でフェーズを切り替えるなんてことはほぼないと思うので、こんなに短くすることはないと思いますが。

ちょっと設定方法、感覚がわかった気がします。

その他

Elasticsearchに対して、ILM自体の有効無効を切り替える場合は、こちらに記載のある「xpack.ilm.enabled」を設定します。

Index lifecycle management settings | Elasticsearch Reference [7.5] | Elastic

ILMをメンテナンスなどの理由で一時停止したり、停止したILMを再開する場合はこちら。

Start and stop index lifecycle management | Elasticsearch Reference [7.5] | Elastic

Infinispan ServerのデフォルトのJGroups Stackが、UDPからTCPになっていたという話

これは、なにをしたくて書いたもの?

最近のInfinispan Serverでクラスタを構成しようとして、ちょっと変化に気づいたので。

Infinispan Serverは10.0.0.Finalから新しいサーバーモジュールになっているのですが、この時にデフォルトのJGroups Stackが
UDPからTCPに変わっていたようです。

Blog: Infinispan 10.0.0.Final - Infinispan

環境

今回の環境は、こちら。

$ java --version
openjdk 11.0.5 2019-10-15
OpenJDK Runtime Environment 18.9 (build 11.0.5+10)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.5+10, mixed mode)

Infinispan Serverは、10.1.0.Finalを使用します。

Cache ContainerのTransport Stackを見る

Infinispan Serverに含まれている設定ファイルの一覧は、こちら。

$ ls -l server/conf
total 24
-rw-r--r-- 1 xxxxx xxxxx  569 Dec 23 07:55 groups.properties
-rw-r--r-- 1 xxxxx xxxxx 2209 Dec 23 07:55 infinispan-local.xml
-rw-r--r-- 1 xxxxx xxxxx 2731 Dec 23 07:55 infinispan-xsite.xml
-rw-r--r-- 1 xxxxx xxxxx 2196 Dec 23 07:55 infinispan.xml
-rw-r--r-- 1 xxxxx xxxxx 2425 Dec 23 07:55 logging.properties
-rw-r--r-- 1 xxxxx xxxxx  802 Dec 23 07:55 users.properties

「server/conf/infinispan.xml」の中に記載されている、「cache-container」を見てみます。

   <cache-container name="default" statistics="true">
      <transport cluster="${infinispan.cluster.name}" stack="${infinispan.cluster.stack:tcp}"/>
   </cache-container>

特に指定がなければ、TCP Stackを使うように設定してありますね。

Infinispan Serverに含まれる設定ファイルは、こちら。

https://github.com/infinispan/infinispan/tree/10.1.0.Final/server/runtime/src/main/server/server/conf

「infinispan.xml」は、こちらですね。

https://github.com/infinispan/infinispan/blob/10.1.0.Final/server/runtime/src/main/server/server/conf/infinispan.xml

で、ここで指定されているTCP Stackとは、こちらのファイルのことになります。

https://github.com/infinispan/infinispan/blob/10.1.0.Final/core/src/main/resources/default-configs/default-jgroups-tcp.xml

その他のJGroups Stackについては、こちらのディレクトリを参照してください。

https://github.com/infinispan/infinispan/tree/10.1.0.Final/core/src/main/resources/default-configs

ドキュメントでの説明は、こちら。

Default JGroups Stacks

Infinispan Server 10.0.0.Final以前(要は9.x以前で、WildFlyベースのサーバーの頃)は、デフォルトのJGroups StackはUDPでした。

こんな感じですね。

        <stacks default="${jboss.default.jgroups.stack:udp}">

https://github.com/infinispan/infinispan/blob/9.4.17.Final/server/integration/jgroups/src/main/resources/subsystem-templates/infinispan-jgroups.xml#L10

https://github.com/infinispan/infinispan/blob/10.1.0.Final/wildfly/jgroups/src/main/resources/subsystem-templates/infinispan-jgroups.xml#L10

ちなみに、こんな感じでEmbedded ModeでAPIを使い、デフォルト設定のクラスタを組もうとするとUDPが使われます。

        GlobalConfiguration globalConfiguration = GlobalConfigurationBuilder.defaultClusteredBuilder().build();
        EmbeddedCacheManager manager = new DefaultCacheManager(globalConfiguration);

https://github.com/infinispan/infinispan/blob/10.1.0.Final/core/src/main/java/org/infinispan/remoting/transport/jgroups/JGroupsTransport.java#L137

このあたりは、互換性の関係でしょうか?

TCP or UDP

さて、どうしてInfinispan Server 10.0.0.Finalから、デフォルトのJGroups StackがTCPになったのかはわかりませんが…。

デフォルトのStackがTCPになっているissue、Pull Requestは、見つけましたけどね…。

[ISPN-10450] ServerNG: improve the initial experience - Red Hat Issue Tracker

ISPN-10450 Improve the initial ServerNG experience (and other assorted stuff) by tristantarrant · Pull Request #7174 · infinispan/infinispan · GitHub

デフォルトのUDPTCPの違いについては、ドキュメントを参照しましょう。

Default JGroups Stacks

default-jgroups-udp.xml

Uses UDP for transport and UDP multicast for discovery. Suitable for larger clusters (over 100 nodes) or if you are using replicated caches or invalidation mode. Minimises the number of open sockets.

default-jgroups-tcp.xml

Uses TCP for transport and UDP multicast for discovery. Suitable for smaller clusters (under 100 nodes) only if you are using distributed caches because TCP is more efficient than UDP as a point-to-point protocol.

UDPでもTCPでも、Node Discoveryにはマルチキャストを使います。

UDPは、100 Nodeより大きなクラスタ、またはReplicated CacheやInvalidation Modeに向くようです。

TCPは、100 Node以下の小さいクラスタでDistributed Cacheを使っている時に適します。

だそうで。

使い分けはわかるのですが、新しいサーバーモジュールでどうしてこうしたのかは、どこかに書いてあると(とても個人的にですが)
嬉しかったかなぁと思います。

デフォルトのTCP Stackでクラスタを組む

一応。

デフォルトでは、MPINGのbind_addrが127.0.0.1となっているため、他のホスト上のNodeとクラスタを組むことができません。

   <MPING bind_addr="${jgroups.tcp.address:127.0.0.1}"
          mcast_addr="${jgroups.mcast_addr:228.6.7.8}"
          mcast_port="${jgroups.mcast_port:46655}"
          num_discovery_runs="3"
          ip_ttl="${jgroups.udp.ip_ttl:2}"
   />

https://github.com/infinispan/infinispan/blob/10.1.0.Final/core/src/main/resources/default-configs/default-jgroups-tcp.xml#L16-L21

これを変更するには、ドキュメントにもあるように「jgroups.tcp.address」システムプロパティに、バインドするIPアドレスを指定します。

-Djgroups.tcp.address=[バインドするIPアドレス]

Tuning JGroups Stacks with System Properties

こんな感じですね。

$ curl -O -L https://downloads.jboss.org/infinispan/10.1.0.Final/infinispan-server-10.1.0.Final.zip
$ unzip infinispan-server-10.1.0.Final.zip
$ cd infinispan-server-10.1.0.Final
$ bin/server.sh -Djgroups.tcp.address=[バインドするIPアドレス]

オマケですが、クラスタに関係なく、デフォルトではInfinispan Serverは他のホストからアクセスすることもできません。
Hot Rodなどで使用されるIPアドレスも、デフォルトで127.0.0.1が使われるからです。バインドするアドレスを変更するには、
ドキュメントの以下を参照しましょう。

Changing the Default Bind Address for Infinispan Servers

「-b」オプションまたは「infinispan.bind.address」システムプロパティを使って、バインドするIPアドレスを指定します。