Elasticsearchのメモリーの管理とトラブルシューティング

Elastic Cloudは、Observability、Security、Searchなどのソリューションを提供することで、Elastic Cloudを使用するユーザーを、フルオペレーションチームだけでなく、データエンジニア、セキュリティチーム、コンサルタントにまで広げました。Elasticのサポート担当者として、さまざまなユーザーやユースケースと関わることができ、大変光栄に思っております。

このようなお客様の多様化に伴い、リソース割り当ての管理、特に割り当て状況のトラブルシューティングやサーキットブレーカーの回避に関するご質問が以前より多く寄せられるようになりました。お気持ちはわかります。私もElasticsearchを使い始めたとき、同じ疑問を持っていました。それがきっかけで、初めてJavaヒープと時系列データベースシャードの管理や、自身のインフラの拡張に挑みました。

Elasticに参加したてのころは、ドキュメントだけでなくブログやチュートリアルも用意されており、すぐに馴染めたことに感激したものです。しかし、最初の1か月は、ユーザーのみなさまからチケットでお寄せいただくエラーと、自分の頭の中の知識をすり合わせることがなかなかできませんでした。やがて、他のサポート担当者と同じように、報告されるエラーの大部分は割り当ての問題を示す症状であること、そしてお問い合わせいただいたユーザーのみなさまに特定のリンクを7個ほどご案内すれば、リソース割り当ての問題はあっという間に解決することに気がつきました。

サポート担当者として、ユーザーのみなさまによくお送りしている割り当て管理の理論へのリンク、頻繁に見られる症状、およびリソース割り当ての問題を解決するために更新をお願いしている設定についてご紹介します。

理論

Javaアプリケーションとして、Elasticsearchはシステムの物理メモリからいくらか論理メモリ(ヒープ)を割り当てる必要があります。これは物理RAMの最大半分、上限は32GBです。ヒープ使用量を高く設定するのは、通常高価なクエリやより大きなデータストレージへの対応としてです。 親サーキットブレーカーのデフォルトは95%ですが、常に85%に達したらリソースをスケーリングすることをお勧めします。

詳細については、以下の概要記事をご覧になることを強くお勧めします。

構成

Elasticsearchのデフォルト設定では、ノードロールと総メモリー量に応じてJVMヒープのサイズが自動的に調整されます。ただし、必要に応じて以下の3つの方法で直接設定できます。

1. ローカルのElasticsearchファイルのうち、config > jvm.optionsファイルを編集する:

## JVM configuration

################################################################
## IMPORTANT: JVM heap size
################################################################

…

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms4g
-Xmx4g

2. docker-composeElasticsearch環境変数として

version: '2.2'
services:
  es01:
	image: docker.elastic.co/elasticsearch/elasticsearch:7.12.0
	environment:
  	- node.name=es01
  	- cluster.name=es
  	- bootstrap.memory_lock=true
  	- "ES_JAVA_OPTS=-Xms4g -Xmx4g"
  	- discovery.type=single-node
	ulimits:
  	memlock:
    	soft: -1
    	hard: -1
	ports:
  	- 9200:9200

3. [Elastic Cloud Hosted] > [Deployment](デプロイ) > [Edit view](ビューを編集)の順に進む。注:ドロップダウンによって物理メモリが割り当てられる(その約半分がヒープ向け)。

Elasticsearchのホットデータとコンテンツティア

トラブルシューティング

クラスターのパフォーマンスに問題が発生している場合、たいていは以下が原因であると考えられます。

  • 構成の問題:マスターノードのサイズが小さく、ILMポリシーがない
  • 発生量:リクエストの頻度または負荷が高すぎる、負荷の大きいクエリまたは書き込みが重複している
以下のcURL/APIリクエストはすべて、[Elastic Cloud Hosted] > [Elasticsearch APIコンソール]、Elasticsearch APIに対するcURLとして、または [Kibana] > [Dev Tools] で実行できます。

割り当ての健全性

データインデックスはサブシャードに格納され、メンテナンスおよび検索/書き込みリクエスト中にヒープを使用します。シャードサイズは50GB以下にする必要があります。上記のElastic Cloud Hostedの例(2つのゾーンにまたがる8GBの物理メモリ、合計2つのノードが割り当てられる)を例にとり、これを_cat/allocationに結びつけます。

GET /_cat/allocation?v=true&h=shards,node
shards node
    41 instance-0000000001
    41 instance-0000000000

または、_cluster/healthに結びつけます。

GET /_cluster/health?filter_path=status,*_shards

{
  "status": "green",
  "unassigned_shards": 0,
  "initializing_shards": 0,
  "active_primary_shards": 41,
  "relocating_shards": 0,
  "active_shards": 82,
  "delayed_unassigned_shards": 0
}

いずれかのシャードでactive_shardsまたはactive_primary_shards以外の値が0より大きい場合は、パフォーマンスの問題を引き起こす原因を特定したことになります。

この問題が報告される場合、通常はunassigned_shards>0となります。これらのシャードがプライマリの場合、クラスターはstatus:redと報告し、レプリカのみの場合は status:yellowと報告します。(これがインデックスにレプリカを設定すること が重要な理由です。クラスターが問題に遭遇した場合、データ損失ではなく回復が可能になります。)単一の割り当てられていないシャードでstatus:yellowになっていると仮定しましょう。調査するには、_cat/shards を介してどのインデックスシャードに問題が発生しているかを確認します。

GET _cat/shards?v=true&s=state
index                                 	shard prirep state    	docs   store ip       	node
logs                                  	0 	p  	STARTED     	2  10.1kb 10.42.255.40 instance-0000000001
logs                                  	0 	r  	UNASSIGNED
kibana_sample_data_logs               	0 	p  	STARTED 	14074  10.6mb 10.42.255.40 instance-0000000001
.kibana_1                             	0 	p  	STARTED  	2261   3.8mb 10.42.255.40 instance-0000000001

これは、レプリカシャードが割り当てられていない非システムインデックスログ用です。_cluster/allocation/explainを実行して、何が問題を引き起こしているのかを確認してみましょう。(ヒント:サポートにエスカレーションする際、まさにこの手順を実行します。)

GET _cluster/allocation/explain?pretty&filter_path=index,node_allocation_decisions.node_name,node_allocation_decisions.deciders.*

{ "index": "logs",
  "node_allocation_decisions": [{
      "node_name": "instance-0000000005",
      "deciders": [{
          "decider": "data_tier",
          "decision": "NO",
          "explanation": "node does not match any index setting [index.routing.allocation.include._tier] tier filters [data_hot]"
}]}]}

このエラーメッセージはdata_hotインデックスライフサイクル管理(ILM)ポリシーの一部)を指しており、当社のILMポリシーが現在のインデックス設定と一致しないことを示しています。この場合、このエラーの原因は、指定されたホット・ウォームノードを持たずにホット・ウォームILMポリシーを設定したことにあります。(何らかのエラーが確実に発生するようにしたかったため、これは皆さんにエラー例を強制的に提示するものです。詳細についてはこのトラブルシューティング例の動画をご覧ください。)

未割り当てシャードが存在しない場合にこのコマンドを実行すると、報告する問題が存在しないため、400エラーとともに[unable to find any unassigned shards to explain](問題の原因となる未割り当てシャードを見つけられませんでした)と表示されます。論理的でない原因(たとえば、割り当て中にノードがクラスターを離れたなどの一時的なネットワークエラー)が発生した場合は、Elasticの便利な_cluster/rerouteを使用できます。

POST /_cluster/reroute

このカスタマイズなしのリクエストの起点となる非同期のバックグラウンドプロセスでは、現在のすべてのstate:UNASSIGNEDシャードの割り当てを試みます(私のように、プロセスがすぐ終わると思って完了を待つのは止めましょう。問題があると思って開発チームに連絡したときには、自然に問題は解決していました)。詳細は、こちらの割り当ての健全性の監視用トラブルシューティング動画をご覧ください。

サーキットブレーカー

割り当て済みのヒープが完全に消費されると、クラスターに対するリクエストでタイムアウトまたはエラーが発生する可能性があります。また、多くの場合、クラスターでサーキットブレーカーの例外も発生します。サーキットブレーカーエラーにより、以下のようなelasticsearch.logイベントが発生します。

Caused by: org.elasticsearch.common.breaker.CircuitBreakingException: [parent] Data too large, data for [<transport_request>] would be [num/numGB], which is larger than the limit of [num/numGB], usages [request=0/0b, fielddata=num/numKB, in_flight_requests=num/numGB, accounting=num/numGB]

原因を探るには、heap.percentを調べます。そのためには、_cat/nodesを確認します。

GET /_cat/nodes?v=true&h=name,node*,heap*
# heap = JVM (logical memory reserved for heap)
# ram  = physical memory

name                                node.role heap.current heap.percent heap.max
tiebreaker-0000000002 mv             119.8mb           23    508mb
instance-0000000001   himrst           1.8gb           48    3.9gb
instance-0000000000   himrst           2.8gb           73    3.9gb

または、Kibanaの[Stack Monitoring](スタック監視)を確認します(この機能が有効になっている場合)。

Elasticsearchノード

メモリーのサーキットブレーカーが発動していることを確認できた場合は、調査の時間を確保するために、ヒープを一時的に増やすことをお勧めします。根本原因を調査する際は、監査ログスローログクラスタログ、またはelasticsearch.logを調べて、直前の連続したイベントを確認してください。以下について確認します。

  • 負荷の高いクエリ。特に以下のもの:
    • 高負荷のバケットアグリゲーション
      • 個人的な失敗談として、検索のサイズやバケットのディメンションに基づいてクエリを実行するに検索で特定のヒープのポートを一時的に割り当てており、設定が10,000,000であったために運用チームにストレスをかけたことがありました。
    • 最適化されていないマッピング
      • 同じく失敗談として、フラット化したデータよりも階層構造のレポートの方が検索のパフォーマンスが良くなると考えていた時期がありました(実際にはそうではありません)。
  • リクエストのボリューム/ペース:主にバッチクエリまたは非同期クエリ

拡張の検討

Circuit Breakerの発動が初めてではない場合、または問題が継続的なものと考えられる場合(たとえば、常に85%に達しているのであれば、リソースの拡張を検討するべきです)は、ヒープの長期的指標となるJVMのメモリー負荷を詳しく調べることをお勧めします。これは[Elastic Cloud Hosted] > [デプロイメント] で確認できます。

Elasticsearchインスタンス

または、_nodes/statsの結果から計算することもできます。

GET /_nodes/stats?filter_path=nodes.*.jvm.mem.pools.old

{"nodes": { "node_id": { "jvm": { "mem": { "pools": { "old": {
  "max_in_bytes": 532676608,
  "peak_max_in_bytes": 532676608,
  "peak_used_in_bytes": 104465408,
  "used_in_bytes": 104465408
}}}}}}}

定義:

JVM Memory Pressure = used_in_bytes / max_in_bytes

この場合の症状としては、elasticsearch.logでガーベージコレクター(gc)イベントの頻度と実行時間が増すことが挙げられます。

[timestamp_short_interval_from_last][INFO ][o.e.m.j.JvmGcMonitorService] [node_id] [gc][number] overhead, spent [21s] collecting in the last [40s]

このような状況が認められた場合は、クラスターの拡張、またはクラスターに対する要求の削減を検討する必要があります。以下の対応を調査、検討することをお勧めします。

  • ヒープリソース(ヒープまたはノード、ノード数)を増やす
  • シャードを減らす(不要または古いデータを削除する、ILMを使用してデータをWarmまたはColdストレージに移し縮小する、喪失しても問題のないデータについてレプリカを無効にする)

サポートはこちら

Elastic Supportでの経験に基づき、未割り当てシャード、シャードとヒープの不均衡、Circuit Breaker、ガーベージコレクションの頻度増、および割り当てのエラーという、サポートチケットでよくお問い合わせいただく問題を順番にご紹介しました。これらはすべて、コアリソースの割り当て管理に関するお問い合わせで見られる症状です。この記事で、理論と解決手順に対する理解を深めていただければ幸いです。

ただし、問題解決で行き詰まった際には、お気兼ねなくお知らせください。喜んでサポートいたします。お問い合わせ:

Elastic Stackのセルフサービス形式のリソース割り当て管理機能は、運用チームだけでなく、それ以外のチームにもお勧めです。

2021年4月27日初稿公開、2024年11月5日更新。

本記事に記述されているあらゆる機能ないし性能のリリースおよびタイミングは、Elasticの単独裁量に委ねられます。現時点で提供されていないあらゆる機能ないし性能は、すみやかに提供されない可能性、または一切の提供が行われない可能性があります。