AQ Tech Blog

OpenSearchのシャード再配置のために_reindex apiを使ったら意外と面倒だった

作成者: tsuyoshi.watanabe|2024年01月26日

 

はじめに

クラウドインテグレーション部の渡邊です。 今回はAWS OpenSearchのindex APIについて説明します。

AWS OpenSearchのシャード数の偏りにより、パフォーマンスに影響が発生する恐れがあります。

偏りがある場合、シャード数とノード数のバランスが悪いことが考えられます。ベストプラクティスにしたがってシャード数がノード数の倍数になるように設定してください。シャードの変更には_reindex api や _shrink api を用いることができます。

引用元:Amazon OpenSearch Service のパフォーマンストラブル解決のためのファーストステップ

以上の引用から、既存インデックスのシャード数をノード数の倍数へ変更するために、_reindex apiを使用したのですが、意外と面倒だったので記述します。 ドキュメントにも簡単ではない旨の記載がありました。

既存のインデックスに対してプライマリシャードの数を簡単に変更することはできません。

引用元:シャード数の選択

なお、新規インデックスの場合は事前にインデックステンプレート(Index templates)を作成しておくことで、作成時にテンプレートの設定が適用されるため、そちらを利用することをオススメします。

以前執筆した、OpenSearchのスローログ設定時の注意点でもインデックステンプレートを利用しました。

_shrink apiによる検証は記事「OpenSearchのシャード再配置のために_shrink apiを使用する際の注意点」を参考にしてください。

環境情報

  • OpenSearch 2.7
  • Amazon Linux 2023

ハマった点と対処方法

インデックスについて以下の制約がありました。

  • インデックス自身に対して_reindex apiを使用できない
  • 既存のインデックスと同名のインデックスを新規のインデックスとして作成できない

たとえば、インデックスAのシャード数を変更するためにインデックス自身(インデックスA)のシャード数を_reindex apiで変更することはできません。 よって、既存のインデックスと同じ名前のインデックスのシャード数を変更するために、以下の手順で対処しました。

① 仮のインデックスを希望するシャード数で作成する
② 既存のインデックスを仮のインデックスへコピー(_reindex apiを使用)
③ 既存のインデックスを削除
④ 指定のシャード数の既存のインデックスと同じ名前のものを作成
⑤ ①でコピーしたインデックスを④で作成したインデックスへ移す

また、④と⑤に関してはIndex aliasesという設定を利用することで、割愛できる場合があります。 「OpenSearchのシャード再配置のために_shrink apiを使用する際の注意点」の「余談:aliasでも似たようなことを実現することができる」に記載があるのでそちらも併せて参考にしてください。

実際にやってみた


事前準備

任意ですが、頻繁に使用する値を環境変数として設定しました。

USERNAME=<マスターユーザ名>
PASSWORD=<マスターユーザパスワード>
ENDPOINT=<OpenSearchエンドポイント>

既存のインデックスとしてindex-testを作成します。

$ curl -XPUT -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test/_doc/1"  -d '{ "name" : "TestIndex" }'

{"_index":"index-test","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":0,"_primary_term":1}

シャード数の設定を確認します。

$ curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test/_settings?pretty"

{
"index-test" : {
"settings" : {
"index" : {
"replication" : {
"type" : "DOCUMENT"
},
"number_of_shards" : "5",
"provided_name" : "index-test",
"creation_date" : "1700781382862",
"number_of_replicas" : "1",
"uuid" : "EfF5dzNaSdCuOzRcJvtICQ",
"version" : {
"created" : "136327827"
}
}
}
}
}

設定から、プライマリシャード数が5、レプリカシャードが各プライマリシャードに対して1つ作成されるため、

合計で10のシャードが作成されているはずです。 シャードの配置を確認すると、インデックスの設定通りに作成されていました。

$ curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/_cat/shards?v"

(略)
index-test 0 r STARTED 0 208b x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
index-test 0 p STARTED 0 208b x.x.x.x fdfd42dea316faeb14b056f7c31461d9
index-test 1 r STARTED 0 208b x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
index-test 1 p STARTED 0 208b x.x.x.x f2071b3f4d78a45206a83fd20122cd88
index-test 2 p STARTED 0 208b x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
index-test 2 r STARTED 0 208b x.x.x.x fdfd42dea316faeb14b056f7c31461d9
index-test 3 p STARTED 0 208b x.x.x.x fdfd42dea316faeb14b056f7c31461d9
index-test 3 r STARTED 0 208b x.x.x.x f2071b3f4d78a45206a83fd20122cd88
index-test 4 r STARTED 1 3.8kb x.x.x.x fdfd42dea316faeb14b056f7c31461d9
index-test 4 p STARTED 1 3.8kb x.x.x.x f2071b3f4d78a45206a83fd20122cd88
(略)

では、初期状態を確認したところで、index-testのシャード数を変更します。


① 仮のインデックスを希望するシャード数で作成する

以下の記述にしたがって、ノードの倍数になるようにシャード数を設定します。 今回はデータノードを3つ作成しているため、プライマリシャード数を3とします。

ベストプラクティスにしたがってシャード数がノード数の倍数になるように設定してください。 引用元:Amazon OpenSearch Service のパフォーマンストラブル解決のためのファーストステップ

$ curl -XPUT -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test-tmp" -d '{
"settings": {
"number_of_shards": 3
}
}'

シャード数の設定を確認します。 number_of_shardsに注目すると、想定のシャード数となっています。

$ curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test-tmp/_settings?pretty"

{
"index-test-tmp" : {
"settings" : {
"index" : {
"replication" : {
"type" : "DOCUMENT"
},
"number_of_shards" : "3",
"provided_name" : "index-test-tmp",
"creation_date" : "1700781571211",
"number_of_replicas" : "1",
"uuid" : "vdgoq9YoRamTxDZNdrsxUQ",
"version" : {
"created" : "136327827"
}
}
}
}
}

シャードの配置を確認すると、インデックスの設定通りに作成されていました。 レプリカシャード数はプライマリシャード数が変更されたことに伴い、5つから3つに変更されていました。

(略)
index-test-tmp 0 r STARTED 0 208b x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
index-test-tmp 0 p STARTED 0 208b x.x.x.x f2071b3f4d78a45206a83fd20122cd88
index-test-tmp 1 p STARTED 0 208b x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
index-test-tmp 1 r STARTED 0 208b x.x.x.x fdfd42dea316faeb14b056f7c31461d9
index-test-tmp 2 p STARTED 1 3.8kb x.x.x.x fdfd42dea316faeb14b056f7c31461d9
index-test-tmp 2 r STARTED 0 208b x.x.x.x f2071b3f4d78a45206a83fd20122cd88
.opendistro_security 0 p STARTED 10 53.2kb x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
(略)


② 既存のインデックスを仮のインデックスへコピー(_reindex apiを使用)

_reindex apiを使用して既存のインデックスを仮のインデックスへコピーします。 sourceはコピー元、destはコピー先です。

$ curl -XPOST -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/_reindex" -d '{
"source": {
"index": "index-test"
},
"dest": {
"index": "index-test-tmp"
}
}'

{"took":3389,"timed_out":false,"total":1,"updated":0,"created":1,"deleted":0,"batches":1,"version_conflicts":0,"noops":0,"retries":{"bulk":0,"search":0},"throttled_millis":0,"requests_per_second":-1.0,"throttled_until_millis":0,"failures":[]}

既存インデックスと仮のインデックスの中身を見比べると、問題なくコピーできていることが分かります。

$ curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test/_doc/1?pretty"

{
"_index" : "index-test",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "TestIndex"
}
}
$ curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test-tmp/_doc/1?pretty"

{
"_index" : "index-test-tmp",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "TestIndex"
}
}


③ 既存のインデックスを削除

既存のインデックスと同名かつ希望するシャード数のものを新規作成するために、既存のインデックスを削除します。

$ curl -XDELETE -u "${USERNAME}:${PASSWORD}" "https://${ENDPOINT}/index-test"


④ 指定のシャード数の既存のインデックスと同じ名前のものを作成

$ curl -XPUT -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test" -d '{
"settings": {
"number_of_shards": 3
}
}'

シャード数の設定を確認します。既存のインデックスの同名の新規インデックスのシャード数が想定通りになっています。

$ curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test/_settings?pretty"

{
"index-test" : {
"settings" : {
"index" : {
"replication" : {
"type" : "DOCUMENT"
},
"number_of_shards" : "3",
"provided_name" : "index-test",
"creation_date" : "1700781888617",
"number_of_replicas" : "1",
"uuid" : "o8PiFYXXQD-PmaBC9qcXsw",
"version" : {
"created" : "136327827"
}
}
}
}
}


⑤ ①でコピーしたインデックスを④で作成したインデックスへ移す

最後に、仮のインデックスindex-test-tmpのものをindex-testへコピーします。

$ curl -XPOST -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/_reindex" -d '{
"source": {
"index": "index-test-tmp"
},
"dest": {
"index": "index-test"
}
}'

{"took":1069,"timed_out":false,"total":1,"updated":0,"created":1,"deleted":0,"batches":1,"version_conflicts":0,"noops":0,"retries":{"bulk":0,"search":0},"throttled_millis":0,"requests_per_second":-1.0,"throttled_until_millis":0,"failures":[]}


シャードが変更された状態でインデックスが元の状態に戻っていることを確認

インデックスの中身が想定のものになっています。

$ curl -XGET -u "${USERNAME}:${PASSWORD}" "https://${ENDPOINT}/index-test/_doc/1?pretty"

{
"_index" : "index-test",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "TestIndex"
}
}

シャード数の設定を確認します。

{
"index-test" : {
"settings" : {
"index" : {
"replication" : {
"type" : "DOCUMENT"
},
"number_of_shards" : "3",
"provided_name" : "index-test",
"creation_date" : "1700781888617",
"number_of_replicas" : "1",
"uuid" : "o8PiFYXXQD-PmaBC9qcXsw",
"version" : {
"created" : "136327827"
}
}
}
}
}

シャードの配置を確認すると、インデックスの設定通りに作成されていました。

(略)
index-test 0 p STARTED 0 208b x.x.x.x fdfd42dea316faeb14b056f7c31461d9
index-test 0 r STARTED 0 208b x.x.x.x f2071b3f4d78a45206a83fd20122cd88
index-test 1 r STARTED 0 208b x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
index-test 1 p STARTED 0 208b x.x.x.x f2071b3f4d78a45206a83fd20122cd88
index-test 2 p STARTED 0 208b x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
index-test 2 r STARTED 1 3.8kb x.x.x.x fdfd42dea316faeb14b056f7c31461d9
index-test-tmp 0 r STARTED 0 208b x.x.x.x 41489d33fe64d3807e1b2dbdd4f8a237
(略)

余談:aliasでも似たようなことを実現することができる

Index aliasesという設定を利用することで、インデックスにエイリアスを指定できます。
たとえば、index-testにindex-test-3というエイリアスを登録します。

curl -XPOST -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test/_alias/index-test-3"

index-test-3をインデックスとして指定してリクエストを送ると、index-testへそのリクエストが反映されます。
index-test-3へインデックス設定を表示するリクエストを送るとindex-testの設定が表示されます。

curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/index-test-3/_settings?pretty"

{
"index-test" : {
"settings" : {
"index" : {
"replication" : {
"type" : "DOCUMENT"
},
"routing" : {
"allocation" : {
"initial_recovery" : {
"_id" : null
},
"require" : {
"_name" : null
}
}
},
"number_of_shards" : "3",
"routing_partition_size" : "1",
"blocks" : {
"read_only_allow_delete" : "false",
"read_only" : "false",
"write" : "false"
},
"provided_name" : "index-test",
"resize" : {
"source" : {
"name" : "index-test-shrink",
"uuid" : "xxiGBhs3R3umbZaPloLRKA"
}
},
"creation_date" : "1700869528292",
"number_of_replicas" : "1",
"uuid" : "qrYnWWXBQoSyRXgTTY3E7Q",
"version" : {
"created" : "136327827",
"upgraded" : "136327827"
}
}
}
}
}

これを利用して、ソースインデックスとは別名のインデックスを_shrink apiで作成しても、エイリアスとしてソースインデックス名を登録することで、④の手間が省けるかと思います。
ただし、インデックスの一覧を確認しても、インデックスに設定したエイリアス名index-test-3は出てきません。

$ curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/_cat/indices?pretty"
green open index-test-1125-notallocate rOJ3Jnm5T92cEMrMKdiCrg 3 1 1 0 8.6kb 4.3kb
green open .opensearch-observability V7SoTD9lTM-BaRnDBZpZhA 1 2 0 0 624b 208b
green open .plugins-ml-config 7JrgF6XeQNSi3UN1goU6Nw 5 1 1 0 9.4kb 4.7kb
yellow open index-test-shrink xxiGBhs3R3umbZaPloLRKA 1 1 1 0 3.8kb 3.8kb
yellow open index-test-shrink-1057 mbgkgDkWQee8iYzMhLeBHg 1 1 1 0 3.8kb 3.8kb
yellow open index-test-1125 EIt--n6-ShaMGuwAtlfN2g 3 1 1 0 4.3kb 4.3kb
green open index-test qrYnWWXBQoSyRXgTTY3E7Q 3 1 1 0 8.6kb 4.3kb
green open index-test-tmp vdgoq9YoRamTxDZNdrsxUQ 3 1 1 0 8.6kb 4.3kb
green open index-test-2 6TupyNBEQWSavxR26y27Aw 6 1 0 0 2.4kb 1.2kb
green open .opendistro_security DYyz6zEbQ5aV_OGWzNe9SQ 1 2 10 0 166.5kb 53.2kb
green open index-test-shrink-cp 75MpLnlMQhC2mScCw9uUHA 1 1 1 0 7.7kb 3.8kb
green open .kibana_1 lOTu3GxzQiW9aDjwgPQgMA 1 2 1 0 15.6kb 5.2kb

エイリアスを確認するには以下のようにします。

$  curl -XGET -u "${USERNAME}:${PASSWORD}" -H "Content-Type: application/json" "https://${ENDPOINT}/_cat/aliases?pretty"

index-test-3 index-test - - - -
.kibana .kibana_1 - - - -

まとめ

  • AWS OpenSearchのシャード数の偏りにより、パフォーマンスに影響が発生する恐れがある
    • パフォーマンスを改善するためにシャード数がノード数の倍数になるように設定する
  • シャードの変更には_reindex api や _shrink api を用いる
  • 新規インデックスの場合は事前にインデックステンプレートを作成しておくことで、作成時にテンプレートの設定が適用されるため推奨
  • インデックスについて以下の制約があることに注意する
    • インデックス自身に対して_reindex apiを使用できない
    • 既存のインデックスと同名のインデックスを新規のインデックスとして作成できない
  • 既存インデックスのシャード数を変更するために、以下の手順を実施することで実現できる
    • ① 仮のインデックスを希望するシャード数で作成する
    • ② 既存のインデックスを仮のインデックスへコピー(_reindex apiを使用)
    • ③ 既存のインデックスを削除
    • ④ 指定のシャード数の既存のインデックスと同じ名前のものを作成
    • ⑤ ①でコピーしたインデックスを④で作成したインデックスへ移す
    • エイリアスを使用することで、④と⑤の手順を実施しないで済む場合がある。

【参考】 Reindex API