1
/
5

Elasticsearchのreindexとalias作成を平日日中にやってみた

みなさん、こんにちは。那須です。

気がつけば技術記事を3ヶ月も書かずに仕事してました。もう何年もブログを書いてきたので、書いてない期間が長ければ長いほど自分の中に「書かないと気持ち悪い」という思いが出てきます。その思いをこの記事で消してしまおうと思ってこれを書いています。

今回はAmazon Elasticsearch Service(以下、Elasticsearch)の運用についてのお話です。実際にElasticsearchを運用されている方にとっては当たり前の内容なのかもしれません。しかし私にとっては初めての経験でとても大変な思いをしたので、忘れないようにここにまとめておきます。

なぜreindexをすることにしたのか?

私が所属するdotDではonedogというサービスを提供していて、その一部の機能でデータストアにElasticsearchを使っています。2021年6月現在、Elasticsearchは以下のような使い方をしています。

また、以下のAWSのドキュメントにはこのような記載があります。

Sizing Amazon OpenSearch Service domains
There's no perfect method of sizing Amazon OpenSearch Service domains. However, by starting with an understanding of your storage needs, the service, and OpenSearch itself, you can make an educated initial estimate on your hardware needs. This estimate ca
https://docs.aws.amazon.com/opensearch-service/latest/developerguide/sizing-domains.html

AWSサポートの方によると、シャードサイズが50GBを超えているとシャードの再配置や移行で問題が発生する可能性が高くなることがあるそうです。Elasticsearchの構成変更やアップグレード時にドメインが処理中のままになる等ですね。ただ、必ずそうなるというわけではなさそうです。

というわけで、放っておくとElasticsearch運用の観点から危険な状況になる可能性があることがわかりました。reindexでシャード数を増やしてシャードサイズを減らせることがわかったので、データがシャードから溢れることを防ぐためにやってみることにしました。

なぜalias作成をしたのか?

reindexで同じマッピング設定で新インデックスにドキュメントをコピーして、アプリから新インデックスにアクセスするようにすれば何も問題はないのですが、こういった作業をするたびにアプリの接続先を変更するのは正直面倒です。また、作業ミス等で変更に失敗すると、その時間だけサービスを止めてしまうことになります。

Elasticsearchにはaliasという便利なものがあって、インデックスに対してaliasを作れます。実際のインデックス名がindex1で、alias名がindexだった場合、アプリからはindexという名前のインデックスにアクセスするようにしておけば、裏でインデックス名が変わってもアプリには何も影響がないようにできます。こうすることで、サービス停止することなくインデックスに対する作業を行うことができますね。

何が問題だったのか?

2021年6月からめでたくonedogを海外のユーザにもご利用いただけるようになりました! ですので、単純にreindexとalias作成するだけではなく、いかにサービスを停止することなく、もしくは停止時間を短くできるかが重要なポイントになっています。日本国内だけにしかユーザがいない場合は夜間にある程度サービスを止めても影響は小さいですが、グローバル展開後はそうもいかないですよね。

また、reindex実施中はインデックス内のドキュメントの丸ごとコピーが行われるので、CPU負荷とIOが激増します。それが一瞬で終わればいいのですが、Elasticsearchには以下のドキュメントがありました。

一度本番環境のElasticsearchのレプリカを作って何もチューニングせずにreindexのテストをしたところ、1日経過しても終わりませんでした。CPU利用率も90%近くまで上がります。しかも6時間くらい経過するとCPU負荷もIOも1/4程度まで下がってしまって、reindexのスピードがかなり低下します。これではサービスへの影響が大きすぎますね。

AWSサポートに上記の事象を説明し、どうすれば最短時間でreindexを完了できるかを問い合わせてみたところ、以下のように遅くなった原因の回答がありました。

最近、EC2インスタンスの運用をしていなかったのでEBSのバーストクレジットの存在を忘れていました。しかも、まさかElasticsearchにもそれが適用されるとは… というわけで、以下のようにElasticsearchドメインを変更しました。

データノードのインスタンスサイズを4倍、EBSサイズを10倍以上にしました。これだけでもかなり時間短縮できたんですが、さらにElasticsearch内の新インデックスのチューニングを以下のように行いました。

これで再度reindexのテストをしたところ、約3時間でreindex作業を完了することができました!

いつやるか?

あとは手順を正確に書いて実行するだけですね。最初は何かあった時のために夜間作業にしようと思っていました。ただ、インフラ運用に携わっている方ならわかると思いますが、夜間作業って安全なようで安全じゃないんですよね。

無停止でできる作業ならなるべく平日日中にやりたい。そう思うのは当然です。今回の作業は、事前のテストで負荷がかかるのは約3時間とわかっていますし、サービスの停止も必要ありません。そして、以下のCloudWatchメトリクスが作業対象のElasticsearchのCPU利用率とIndexingRateなんですが、ピークが1日に2回あってほぼちょうど12時間周期なんです。

もしかして平日日中にできるのでは…と思ったので、一番頭が元気な月曜の日中にすることにしました。平日日中であればエラーにもすぐ気づけますし、他のメンバーのサポートも受けることができるので安心です。

実際の作業の流れ

最初にElasticsearchのスケールアップです。スケールアップ中はインスタンス数が2倍になりますが、その時にシャードの状態を見てみると新しくできたデータノードにRELOCATINGしている様子がわかりますね。

curl https://xxx.ap-northeast-1.es.amazonaws.com/_cat/shards?v
index shard prirep state docs store ip node
aaa 3 p STARTED 171386089 40.2gb x.x.x.x tFeRJ2T
aaa 3 r STARTED 171386089 40.2gb x.x.x.x WDfvLeZ
aaa 1 p STARTED 171383176 40.2gb x.x.x.x WDfvLeZ
aaa 1 r RELOCATING 171383176 40.3gb x.x.x.x 7ZU3PTa -> x.x.x.x tFeRJ2TKRP6_mNettuLwOw tFeRJ2T
aaa 4 p RELOCATING 171374950 40.2gb x.x.x.x qUSFDz6 -> x.x.x.x rLpedxzSQQ-ZWUtVF44QKA rLpedxz
aaa 4 r RELOCATING 171374950 40.3gb x.x.x.x xslgACl -> x.x.x.x tFeRJ2TKRP6_mNettuLwOw tFeRJ2T
aaa 2 p STARTED 171377573 40.2gb x.x.x.x tFeRJ2T
aaa 2 r RELOCATING 171377573 40.3gb x.x.x.x 7ZU3PTa -> x.x.x.x rLpedxzSQQ-ZWUtVF44QKA rLpedxz
aaa 0 p STARTED 171356113 40.4gb x.x.x.x rLpedxz
aaa 0 r RELOCATING 171356113 40.3gb x.x.x.x xslgACl -> x.x.x.x WDfvLeZoTKmuvNVL2HQhgA WDfvLeZcurl https://xxx.ap-northeast-1.es.amazonaws.com/_cat/shards?v
index shard prirep state docs store ip node
aaa 3 p STARTED 171386089 40.2gb x.x.x.x tFeRJ2T
aaa 3 r STARTED 171386089 40.2gb x.x.x.x WDfvLeZ
aaa 1 p STARTED 171383176 40.2gb x.x.x.x WDfvLeZ
aaa 1 r RELOCATING 171383176 40.3gb x.x.x.x 7ZU3PTa -> x.x.x.x tFeRJ2TKRP6_mNettuLwOw tFeRJ2T
aaa 4 p RELOCATING 171374950 40.2gb x.x.x.x qUSFDz6 -> x.x.x.x rLpedxzSQQ-ZWUtVF44QKA rLpedxz
aaa 4 r RELOCATING 171374950 40.3gb x.x.x.x xslgACl -> x.x.x.x tFeRJ2TKRP6_mNettuLwOw tFeRJ2T
aaa 2 p STARTED 171377573 40.2gb x.x.x.x tFeRJ2T
aaa 2 r RELOCATING 171377573 40.3gb x.x.x.x 7ZU3PTa -> x.x.x.x rLpedxzSQQ-ZWUtVF44QKA rLpedxz
aaa 0 p STARTED 171356113 40.4gb x.x.x.x rLpedxz
aaa 0 r RELOCATING 171356113 40.3gb x.x.x.x xslgACl -> x.x.x.x WDfvLeZoTKmuvNVL2HQhgA WDfvLeZ

スケールアップが完了したら、作業前の状態を確認しておきます。

# index数確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_cat/indices?v

# shardサイズ確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_cat/shards?v

# index settings確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/aaa/_settings?pretty

# mapping確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/aaa/_mapping?pretty# index数確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_cat/indices?v

# shardサイズ確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_cat/shards?v

# index settings確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/aaa/_settings?pretty

# mapping確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/aaa/_mapping?pretty

ではreindexの宛先となる新インデックスを作成しましょう。アプリからの接続とElasticsearchの内部の状態の図は以下になります。旧インデックスは今まで使っていたインデックス、新インデックスは今作った空のインデックスです。

# 新index作成
curl -XPUT 'https://xxx.ap-northeast-1.es.amazonaws.com/bbb' -H 'Content-Type: application/json' -d'
{
"settings": {
"refresh_interval" : "-1",
"number_of_shards" : "12",
"number_of_replicas" : "0",
"translog.durability" : "async"
},
"mappings": {
...省略
}
}
'

# index数確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_cat/indices?v
--
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open aaa uEsxWninRgOyhF1RN_V6tg 5 1 856959620 1017639 20.1kb 10kb
green open bbb EzBqmn7pRUuM56fpi5ynaQ 12 0 0 0 2.6kb 2.6kb# 新index作成
curl -XPUT 'https://xxx.ap-northeast-1.es.amazonaws.com/bbb' -H 'Content-Type: application/json' -d'
{
"settings": {
"refresh_interval" : "-1",
"number_of_shards" : "12",
"number_of_replicas" : "0",
"translog.durability" : "async"
},
"mappings": {
...省略
}
}
'

# index数確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_cat/indices?v
--
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open aaa uEsxWninRgOyhF1RN_V6tg 5 1 856959620 1017639 20.1kb 10kb
green open bbb EzBqmn7pRUuM56fpi5ynaQ 12 0 0 0 2.6kb 2.6kb

それではreindexを実施しましょう。1回目のreindexで実行時の全データをコピー、2回目のreindexで1回目のreindex中に発生した差分だけをコピーしています。

# reindex実施
curl -X POST "https://xxx.ap-northeast-1.es.amazonaws.com/_reindex?slices=auto&wait_for_completion=false&pretty" -H 'Content-Type: application/json' -d'
{
"conflicts": "proceed",
"source": {
"index": "aaa"
},
"dest": {
"index": "bbb",
"version_type": "external"
}
}
'

# reindex task一覧を確認する
curl https://xxx.ap-northeast-1.es.amazonaws.com/_tasks?actions=*reindex

# reindex 親タスクで完了確認する
curl https://xxx.ap-northeast-1.es.amazonaws.com/_tasks/<task_id>?pretty

# 差分だけreindex実施
curl -X POST "https://xxx.ap-northeast-1.es.amazonaws.com/_reindex?slices=auto&pretty" -H 'Content-Type: application/json' -d'
{
"conflicts": "proceed",
"source": {
"index": "aaa",
"query": {
"range": {
"timestamp": {
"gte": "now-4h"
}
}
}
},
"dest": {
"index": "bbb",
"version_type": "external"
}
}
'# reindex実施
curl -X POST "https://xxx.ap-northeast-1.es.amazonaws.com/_reindex?slices=auto&wait_for_completion=false&pretty" -H 'Content-Type: application/json' -d'
{
"conflicts": "proceed",
"source": {
"index": "aaa"
},
"dest": {
"index": "bbb",
"version_type": "external"
}
}
'

# reindex task一覧を確認する
curl https://xxx.ap-northeast-1.es.amazonaws.com/_tasks?actions=*reindex

# reindex 親タスクで完了確認する
curl https://xxx.ap-northeast-1.es.amazonaws.com/_tasks/<task_id>?pretty

# 差分だけreindex実施
curl -X POST "https://xxx.ap-northeast-1.es.amazonaws.com/_reindex?slices=auto&pretty" -H 'Content-Type: application/json' -d'
{
"conflicts": "proceed",
"source": {
"index": "aaa",
"query": {
"range": {
"timestamp": {
"gte": "now-4h"
}
}
}
},
"dest": {
"index": "bbb",
"version_type": "external"
}
}
'

これでほぼ全てのデータが新インデックスにコピーできました。新インデックスを使うように切り替える前に、障害発生してもデータが消えないようにレプリカシャードを作成しておきましょう。その他のインデックス設定も通常運用の状態にしておきます。

# index設定を戻す
# レプリカ作成完了までstatusはyellowになる
curl -X PUT 'https://xxx.ap-northeast-1.es.amazonaws.com/bbb/_settings?pretty' -H 'Content-Type: application/json' -d'
{
"index" : {
"refresh_interval" : null,
"number_of_replicas" : "1",
"translog.durability" : null
}
}
'# index設定を戻す
# レプリカ作成完了までstatusはyellowになる
curl -X PUT 'https://xxx.ap-northeast-1.es.amazonaws.com/bbb/_settings?pretty' -H 'Content-Type: application/json' -d'
{
"index" : {
"refresh_interval" : null,
"number_of_replicas" : "1",
"translog.durability" : null
}
}
'

本来であれば、ここでalias作成とともに旧インデックスを削除するのですが、最後のreindex実施からここまでで僅かながら差分データが発生しています。この差分も取り込みつつ今も流れてくるデータを新インデックスに入れたいので、今回はアプリのElasticsearchの接続先を一時的に切り替えます。その後、最後のreindexを実施して新インデックスが最新の状態になるようにしましょう。

これでalias作成の準備が整いました。以下のコマンドでaliasを作成しましょう。aliasを作成すると同時に旧インデックスは削除します(名前が重複するため)。alias作成後は、アプリのElasticsearchの接続先を元の名前(今作ったalias名)に戻しましょう。

# snapshotが実行されていないことを確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_snapshot/_status

# alias作成
curl -X POST "https://xxx.ap-northeast-1.es.amazonaws.com/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "add": { "index": "bbb", "alias": "aaa" }},
{ "remove_index": { "index": "aaa" }}
]
}'

# alias確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_aliases?pretty# snapshotが実行されていないことを確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_snapshot/_status

# alias作成
curl -X POST "https://xxx.ap-northeast-1.es.amazonaws.com/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "add": { "index": "bbb", "alias": "aaa" }},
{ "remove_index": { "index": "aaa" }}
]
}'

# alias確認
curl https://xxx.ap-northeast-1.es.amazonaws.com/_aliases?pretty

これでメインの作業は完了です。reindexを実施すると.tasksというインデックスが作成されていると思いますが、これはタスクの状況を確認できるようにするためのインデックスなので削除してもOKです。

# .task index削除
curl -X DELETE "https://xxx.ap-northeast-1.es.amazonaws.com/.tasks"# .task index削除
curl -X DELETE "https://xxx.ap-northeast-1.es.amazonaws.com/.tasks"

最後のステップです。最初にElasticsearchをスケールアップしているので、元のインスタンスタイプとEBSサイズに戻しましょう。そのままでもサービスは大丈夫ですが、コスト的には大丈夫じゃないです。

作業結果

サービス無停止、データロスもなし、ピーク時間帯に負荷をかけることもなく、平日日中に作業を完了させることができました! CloudWatchメトリクスでみるとCPUは50%程度の増加だったので、ピーク時間帯にかかってもギリギリ大丈夫だったのかもしれません。シャード数は5から12に増えているので、CPU利用率が若干上がっているような雰囲気もありますが、負荷への影響もほとんどなさそうです。

これでしばらくはデータ量の増加にも耐えられますし、aliasを利用することで背後のインデックスを考慮する場面もかなり減りました。

これからのこと

今回の作業でシャード数が増えてデータ保存量は増えましたが、根本的には同じ問題を抱えたままです。古いデータを別のデータストアにアーカイブするのか、ユーザによって残すデータを定義して保存量を制御していくのか、そもそもElasticsearchではなく別のデータストアに変更するなど、どうするかはこれから考えていきます。データ量が一定量を保つようになれば今回のようなreindex作業はほぼなくなると思いますので、真剣に考えていきます。

さいごに

Elasticsearchのreindexとalias作成について書きました。やってみて初めてわかりましたが、Elasticsearchの運用って意外と難しいですね。誰か1人にでもこの記事が役に立てば嬉しいです。

今回はElasticsearchについて書きましたが、弊社では主にAWSサービスを活用して様々なサービスを開発しています。いい感じで運用できている部分やまだまだ改善の余地ありな部分までいろいろありますが、日々改善を重ねています。
一緒にインフラの運用や改善活動をやっていただけるエンジニアの方を大募集しています!少しでも興味があればお声がけください!


SREエンジニア
SREとして新規事業のプラットフォームを支えるエンジニア募集!!
【世の中に新しい価値を生み出し続ける事業創造ファーム】 私たちは自社事業と共創事業の2つを軸に、技術・知識・経験を相互に作用させることで、新しい事業を生み出し、常にアップデートし続けることで、ひとりひとりの「当たり前」に変化をもたらす価値を創造し続けます。 ①共創事業 大企業様の新規事業戦略立案から開発まで、先方の持っている課題感や構想をヒヤリングすることから始め、事業を成功に導くためのお手伝いをしています。大企業の社会的影響力や利用可能な資源の量、dotDの強みである発想力や敏捷性・柔軟性と、その両方を掛け合わせることでひとりひとりの「当たり前」に変化をもたらす価値を創造し続けます。 ▼直近の案件例 “クルマのデジタルキー”から“スマートシティを支えるサービス”へ「TOKAI RIKA Digitalkey」 https://digitalkey.jp/ TOKAI RIKA Digitalkeyは、クルマ以外のモビリティ、オフィスなどの建物、駐車場などもデジタルキー化を促進させ、物理的な「鍵」のない安全で便利な社会の実現を目指します。様々な場所をデジタルキー化することで、それぞれが繋がり、お客様へのシームレスなサービス提供と課金、事業者様における人員削減などに貢献します。 ②自社事業 dotDには、社会や身近に課題意識を持ったメンバーがおり、課題が深く難易度が高い領域への事業化に可能性やビジョンをもってチャレンジしながらサービス開発をしています。 現在はPetTech(ペット x テクノロジー)、EdTech(エデュケーション x テクノロジー)での新サービス開発を中心に取り組んでいますが、今後もどんどんチャレンジする領域を広げていきます。 愛犬とオーナーに幸せを届けるプラットフォーム「onedog」 https://onedog.io/ja/app/index.html 子どもの本当の好きに出会える課外活動マッチングサービス「meepa」 https://meepa.io/ フィジタル空間を実現し、新たなオンライン体験を模索するプロジェクト「iDovatar」 https://project.idovatar.com/
株式会社dotD


株式会社dotDからお誘い
この話題に共感したら、メンバーと話してみませんか?
株式会社dotDでは一緒に働く仲間を募集しています

同じタグの記事

今週のランキング

funabashi yukikoさんにいいねを伝えよう
funabashi yukikoさんや会社があなたに興味を持つかも