Java
Elasticsearch
Ansible

Elasticsearch導入前に気を付けておきたいこと!

More than 1 year has passed since last update.

Elasticsearch導入前に気を付けておきたいこと

はじめに

  • Elasticsearchを仕事で使うことになったので導入前に考えるであろうことを調査・検証し、まとめてみました。
  • 記載されている内容はほぼ公式ドキュメントから引っ張ってきています。それぞれの章にリンクを付けてありますので、記載されていること以外に気になる部分があればそちらを参照されるとよいと思います。

インストールについて

  • ここで設定している内容はすべてansibleのスクリプトに落としてあります。
  • 5分あればcurl,javaのインストール、Elasticsearchのインストール・初期設定、プラグインのインストールが終わるはずです。
  • https://github.com/uzresk/ansible-elasticsearch2

環境

  • CentOS6.7
  • Java1.8.0_45
  • Elasticsearch 2.1.1

HeapSizeの設定

  • Elasticsearchが使うJavaのヒープサイズを設定する。目安は物理メモリの半分弱。xmsとxmxは自動的に同じ値が設定されます。嫌ならES_MIN_MEMとES_MAX_MEMを設定します。
/etc/init.d/elasticsearch
export ES_HEAP_SIZE=512m

File Descriptors

  • Openできるファイルの数。「java.io.IOException: Too many open files」なんてメッセージはは大抵これが原因。
  • 32K、64Kがおすすめ。Elasticsearch2.1のデフォルトでは65535になっており、起動スクリプト内で設定してあるので不足するようであれば追加する。
/etc/init.d/elasticsearch
MAX_OPEN_FILES=65535
  • 設定の確認(OSからみる)
[root@es1 vagrant]# cat /proc/[ES processID]/limits
Limit                     Soft Limit           Hard Limit           Units
Max open files            65535                65535                files
~
  • 現在どのくらいオープンしているか?
[root@es1 vagrant]# ls -l /proc/12831/fd/ | wc -l
93
  • 設定の確認(ESからみる)
[root@es1 tms]# curl http://localhost:9200/_nodes/stats?pretty

仮想メモリ

  • 1つのプロセスで使えるメモリマップの数の上限は「sysctl vm.max_map_count」で確認できる。デフォルトは65530
  • linuxのデフォルトで足りない恐れがあるから上げたほうがよいとのこと。ちなみにrpmでインストールした場合は自動的にこの値が設定されるので設定は不要です。
  • この値の妥当性がイマイチわかっていない。
sysctl -w vm.max_map_count=262144

メモリの設定

  • JVMがスワップし始めるとElasticsearch(Lucene?)は重くなるようです。
  • mlockallオプションを有効にすることで固定メモリで動くようにするのがよさそう。
  • 起動スクリプトの中ではES_HEAP_SIZEの値をMAX_LOCKED_MEMORYに設定しているようでした。
/etc/elasticsearch/elasticsearch.yml
bootstrap.mlockall: true
  • 設定を確認する(mlockall : trueになっていることを確認する)
[root@es1 vagrant]# curl http://localhost:9200/_nodes/process?pretty
{
  "cluster_name" : "elasticsearch",
  "nodes" : {
    "qIwzWBwQSyessRMxjKM4GQ" : {
      "name" : "Nitro",
      "transport_address" : "10.0.2.15:9300",
      "host" : "10.0.2.15",
      "ip" : "10.0.2.15",
      "version" : "2.1.1",
      "build" : "40e2c53",
      "http_address" : "10.0.2.15:9200",
      "process" : {
        "refresh_interval_in_millis" : 1000,
        "id" : 13058,
        "mlockall" : true
      }
    }
  }
}
  • これが有効にならない場合にどうするかという記事をネット上でちらほら見る。
  • 公式ドキュメントによるとulimitの設定を行うとかtemporary directoryの場所を変更しろと書いてある。

Elasticsearchの設定

クラスタ名

  • デフォルトはelasticsearch
/etc/elasticsearch/elasticsearch.yml
cluster.name: test-cluster

ノード名

  • ノード名はデフォルトではMarvelのキャラクタ名がランダムに振られる。わかりにくいので設定しておく。
  • ホスト名と一緒が良いのであれば${HOSTNAME}という感じで設定する
  • ${prompt.text}を指定すると起動時にプロンプトが表示されノード名が入力できる。
/etc/elasticsearch/elasticsearch.yml
node.name: ${HOSTNAME}

ネットワークホストの設定

  • これを設定しないとlocalhostからしかhttp接続できないので必ず設定する。
  • どのホストからでもつなげる場合はこのようにする
/etc/elasticsearch/elasticsearch.yml
network.host: 0.0.0.0
  • vagrant上ではnetwork.hostがeth0を指してしまうのでeth1を指すように変更しておく必要がある。
/etc/elasticsearch/elasticsearch.yml
network.host: '_eth1:ipv4_'
  • network.hostを設定することで、network.bind_host(httpリクエストを受け付けるhost)、network.publish_host(クラスタ時にノード接続するために利用するhost)は同じものが設定される。詳しくはこちらを参照
  • 接続要求はどこからでも受け付けるようにし、クラスタで利用するIPはeth1を利用するには以下のように設定する。
network.bind_host: 0.0.0.0
network.publish_host: _eth1:ipv4_

インデックスの格納場所

  • デフォルトは/var/lib/elasticsearch。この下にnodes/0/_state/global-0.stという感じでインデックスが格納される。これを変更するにはこのようにする
/etc/elasticsearch/elasticsearch.yml
path.data: /var/lib/elasticsearch/
  • data.pathはカンマ区切りで書くこともでき、複数のSSDに対してRAID0(ストライピング)するようなことも可能。どちらかが故障するとデータロストするので性能とのトレードオフ。

ログの出力場所

  • デフォルトは/var/log/elasticsearch。これを変更するにはこのようにする
  • 起動スクリプト内でもLOG_DIRという環境変数があるがelasticsearch.ymlの設定の方が強い
/etc/elasticsearch/elasticsearch.yml
path.logs: /var/log/elasticsearch/

クラスタ関連

/etc/elasticsearch/elasticsearch.yml
discovery.zen.ping.unicast.hosts: ['127.0.0.1', '[::1]']
  • スプリットブレイン対策。最小のマスタノード数を決める設定になり、計算式はn/2+1で設定する。1台構成の場合は1/2+1=1となります。
/etc/elasticsearch/elasticsearch.yml
discovery.zen.minimum_master_nodes: 1

ソフト・ハードコミット頻度の変更

  • デフォルトでは1sec毎にコミットされるわけですがそんなに高頻度じゃなくても要件満たせる場合や巨大なindexを登録する場合は長く設定しておくことでindex更新時の負荷を減らす。
  • 単位はs or msec(書かないとmsec) -1を設定することでコミットしなくなります。
  • Dynamic Index Settings
[root@es1 tms]# curl -XPUT http://localhost:9200/[index name]/_settings -d '
index:
    refresh_interval: 30s
'
{"acknowledged":true}
  • ハードコミットの頻度はTranslogに記載されている。
  • デフォルトのflushタイミングは、サイズが512mになったタイミングと5秒おき。
  • その他操作の回数でflushするタイミングを決めることもできます。

Shard、Replicaについて

Shard

  • indexを物理的に分割したもの。ノードごとに配置すると考えたほうがシンプル。ただし、一つのノードで複数shard持つことは可能。
  • デフォルトは5。index作成後には変更できないのでindex作成時に指定してあげる。
  • mapping.jsonで指定する場合は以下のようにする
{
  "settings": {
    "index": {
      number_of_shards: 3,
      number_of_replicas: 0
    },

Replica

  • PrimaryShardのコピーのことをReplicaShardという。PrimaryShardがダウンしたらReplicaShardがPrimaryShardに昇格する。
  • 2台構成で冗長化を担保しようとするとshardの数が2.Replicaが1となる。
  • 1台構成でelasticsearchを起動するとreplica先がないのでcluster healthがunhealthyになっている。これを解消するにはreplicaの数を0にすればよい。
  • replicaの数については後で変更が可能
[root@es1 tms]# curl -XPUT 'http://localhost:9200/tms/_settings' -d '
index:
  number_of_replicas: 1
'

memo

  • ハードウェア故障に対する冗長性への対応はノード(=elasticsearchのプロセス)の数を増やし、ReplicaShardを増やすことで比較的対応は簡単。
  • indexのサイズが非常に大きいとか更新頻度が多いという部分に関してはShardの数がポイントになってくるが、後では変更できないので将来を見越したサイジングが必要
  • 参考になりそうなのはCapacity PlanningElasticsearchのインデキシングに関するパフォーマンス検討

_allフィールドの無効化

"_all" : {"enabled" :  false},

ログ

  • ログの設定は/etc/elasticsearch/logging.ymlで行う。log4jの要領で書けるので設定は難しくない。ローテートはやってくれるがファイルの削除はやってくれないのでshellなどを仕込みましょう。
  • スローログの出力も可能なので必要に応じて設定する。

バックアップ&リストア

  • 2台構成以上でレプリケーションを組んでいる状態であれば、サーバの物理障害に対する備えとしては十分だろう。2重障害を考えるのであればElasticsearchおすすめの3nodeでクラスタを組むのが良い。
  • インデックスが壊れたとかおかしなデータが入ってしまったという所謂論理障害に対して備えをしておく必要がある。
  • マニュアルを見る限りロールフォワードの機能はないのでバックアップ間隔分データがロストすることを許容する。RPOに厳しい場合だと別のデータソース(RDBやKVS)から差分データを戻してあげることを検討する必要があることが注意点。
  • ここではelasticsearchのsnapshot機能を使ったバックアップ・リストアを考える

Backup

  • snapshotを作るにはリポジトリをまずは作る必要がある。複数のバックアップを取得する場合は配列で記載することも可能。またパスの書き方はUNCでも書ける。
/etc/elasticsearch/elasticsearch.yml
path.repo: /var/lib/elasticsearch/backup
  • my_backupという名前で/var/lib/elasticsearch/backupを認識させる
[root@es1 tms]# curl -XPUT 'http://192.168.1.41:9200/_snapshot/my_backup' -d '{
  "type": "fs",
  "settings": {
    "location": "/var/lib/elasticsearch/backup",
    "compress": true
  }
}'
  • リポジトリを登録する(/var/lib/elasticsearch/backup/my_backupディレクトリができるはず)
[root@es1 tms]# curl -XPUT 'http://192.168.1.41:9200/_snapshot/my_backup' -d '{
  "type": "fs",
  "settings": {
    "location": "my_backup",
    "compress": true
  }
}'
[root@es1 my_backup]# curl -XPUT http://192.168.1.41:9200/_snapshot/my_backup/snapshot_1?WAIT_FOR_COMPLETION=true
{"accepted":true}

[root@es1 my_backup]# ls -l /var/lib/elasticsearch/backup/my_backup/
total 16
-rw-r--r--. 1 elasticsearch elasticsearch   28 Jan  7 01:09 index
drwxr-xr-x. 3 elasticsearch elasticsearch 4096 Jan  7 01:09 indices
-rw-r--r--. 1 elasticsearch elasticsearch  121 Jan  7 01:09 meta-snapshot_1.dat
-rw-r--r--. 1 elasticsearch elasticsearch  178 Jan  7 01:09 snap-snapshot_1.dat
  • 取得したsnapshotの情報を個別、全件で取得するには以下のようにする
curl -XGET http://192.168.1.41:9200/_snapshot/my_backup/snapshot_1
curl -XGET http://192.168.1.41:9200/_snapshot/my_backup/_all
  • snapshotの削除
curl -XDELETE http://192.168.1.41:9200/_snapshot/my_backup/snapshot_1

Restore

  • snapshotからのリストアが可能です。
  • ここではtmsという名前のindexをrestored_tmsという名前でリストアしている
[root@es1 my_backup]# curl -XPOST http://192.168.1.41:9200/_snapshot/my_backup/snapshot_1/_restore -d '
{
  "indices": "tms",
  "ignore_unavailable": "true",
  "include_global_state": false,
  "rename_pattern": "tms",
  "rename_replacement": "restored_tms"
}'
  • 名前を変えつつreplicaの数を変更することなんかもできます。
  • ここではreplicaですけどrefresh_intervalの値など変えることもできます。当たり前かもしれませんがshardの数は変えられません。
[root@es1 my_backup]# curl -XPOST http://192.168.1.41:9200/_snapshot/my_backup/snapshot_1/_restore -d '
{
  "indices": "tms",
  "index_settings": {
    "index.number_of_replicas": 1
  },
  "ignore_unavailable": "true",
  "include_global_state": false,
  "rename_pattern": "tms",
  "rename_replacement": "restored_tms2"
}'

監視

リソース周り

  • top(CPUがあふれてないか)
  • iostat(IOがあふれていないか)
  • ps,free,sar(メモリの使用量が一定か?swapしていないか)
  • open_file_descriptors
  • jvm(jstatとかで取得する。mlockall=trueなら優先度低)
  • このへんも実はcurlで取れちゃうのです - NodeStats参照

サービス監視

[root@es1 tms]# curl -XGET http://localhost:9200/_cluster/health?pretty
{
  "cluster_name" : "test-cluster",
  "status" : "yellow",
  ・・・・
}

LBからのヘルスチェック

  • LB配下に複数のノードをぶら下げるときにLBからのヘルスチェックのURLをどうするかを考える必要がある。がこれはちょっと悩んだ。
  • _nodesにはノードが良い状態なのかを判断する術は用意されていないみたいだった。http://[IP]:9200/で200が返却されることでOKとみなして良さそう。つまり上記(サービス監視)に記載されているクラスタの監視のURLと同じでよさそう。

JMX

  • 起動スクリプト内でES_JAVA_OPTSを設定する。
  • mbeansの情報でelasticsearchの情報が特に何も得られるわけではないのと、そもそもelasticsearchの標準APIで色々取れるのであえてJMX経由で定期的に取るものはなさそうだ。
/etc/init.d/elasticsearch
export ES_JAVA_OPTS="-Djava.rmi.server.hostname=192.168.1.41 -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=7085 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

その他

  • CAT APIは便利そうなので押さえておきたい

コマンド(参考)

elasticsearchの状態を確認する

  • クラスタ名、ノード名、NW周りの設定が見れる
  • [root@es1 ~]# curl http://localhost:9200/_nodes/process?pretty

mappingの登録

  • mappingが作られた時点でシャードが作られる
[root@es1 tms]# curl -XPOST http://localhost:9200/[index name] -d @mapping.json

indexの登録(bulk)

[root@es1 tms]# curl -X POST http://localhost:9200/_bulk --data-binary @data.json

indexの削除

[root@es1 index]# curl -XDELETE 'http://localhost:9200/[index name]*'

indexの設定を確認する

[root@es1 tms]# curl -XGET 'http://localhost:9200/tms/_settings?pretty'