generated at
さようならElasticsearch、よろしくElastic Cloud
2022/5/19 20:25 - 20:40

(明らかに15分で収まる内容の資料ではないですが、資料はモリモリで発表はスカスカでもScrapboxで盛り上がれるか、という仮説の検証を兼ねています)


yuisekiですyuiseki
Gyazoのプロジェクトマネージャー兼ソフトウェアエンジニアです
本日お集まりいただいたみなさん、ありがとうございます
>
>本日お集まりいただいたみなさん


12年間運用を続けているB2C SaaSの検索インフラの実態(14分まで、1分間)
Gyazoは2021年、「画像の瞬間発見」をテーマに、検索に力を入れていた
自前のElasticsearchクラスタがあったが、ほとんどロストテクノロジー化してしまっていた
一応動いてるんだけどなんで動いてるのか誰もわかっていない状態
新しい検索の機能追加やアイデアを試行錯誤したいけど、変更方法が不明のため、諦める
いやそれはダメでしょということで、検索インフラ刷新を検討し、いろいろと模索はしていた
検索エンジン自作しようとしたり…
log4j脆弱性事件が最後の一押しになった
Elastic Cloudへの移行というプロジェクトXが始まったのだった……
偉業👏👏👏akix


検索インフラ移行のゴール(13分まで、1分間)
これまで通りのことができるようにする
高速な検索
関連画像」の表示
オンデマンドなindexing
無料ユーザーの画像は検索を試そうとした時だけindexingすることでコスト節約
追加で実現したいこと
正確な検索
index mappingsを再定義・再構築できるようにする
移行におけるダウンタイムはゼロ
謎のロストテクノロジーにしない
これ全部話すと一時間になるので面白い話とお得な話だけします
やったー!akix
概要を聞いてるだけでもすごく大変な作業をしたというのが伝わるMijinko_SD


失敗から学ぶElastic Cloud(10分まで、3分間)
Elastic Cloudに移行しようとしていた間、ハチャメチャに失敗していました
失敗は重要な概念
(科学においても、工学においても、医学においても、経営・事業においても、政治においても、etc)
>Anyone who has never made a mistake has never tried anything new.
テスト駆動開発も失敗駆動開発
まず落ちるテストを書く=まず失敗する
とはいえガンガンシステム壊しちゃいました!では困る
世の中の企業にもっとCFOChief Failure Officer最高失敗責任者)とかいても良いと思う
ググったらCFOがいる企業は実在する模様
最高財務責任者(Chief Financial Officer)では
おもしろいakixtakeru
「失敗に関する本も読んでいる」
ここでNotaの本の購買支援(?)が生きるのかMijinko_SD
失敗が起きた際の損害が最小化されている
影響範囲が限定されている
すばやく失敗前の状態に戻せる
失敗から得られる情報が最大化されている
情報のノイズが最小化されている
仮説反証可能である
発生条件がコントロールされている
失敗から得た情報を元に再発を防止し、さらに改善できる
コスパの良い失敗?yosider
実際にElastic Cloud移行で良い失敗を計画する
Gyazoユーザーが検索できなくなってしまいましたみたいな事態は絶対に避ける
旧Elasticsearchクラスタは維持したまま、Elastic Cloudへの対応を開発
Elastic Cloudに立ち上げたクラスタへ、旧Elasticsearchクラスタと同じリクエストを送って、負荷に耐えられるか検証
仮説-検証サイクルを回していく
失敗:index mappings更新時にハチャメチャに負荷が跳ね上がってしまう
最高に過酷だった時
CPU Usage 200%超え!
Search Response Times 60k ms超え!
キロミリセック熱いmtane0412
Index Response Times 60k ms超え!
だいぶ過酷だった時
CPU Usage 100%超え!
Search Respones Times 10k ms超え!
Elastic Cloudにこれよりも負荷かけたことあるぞという人がいたら是非ご連絡ください


失敗その1(9分30秒まで、30秒間)
number_of_shardsを16とかにする
仮説: number_of_shards を増やせば増やすほど速くなるのでは
結果:そんなことはない。小さいshardsを増やすと最高に過酷になる(後述)


失敗その2(9分まで、30秒間)
?refresh=true
仮説:オンデマンドなindexingのためには ?refresh=true にしなければならないのでは
結果:そんなことはない。そしてずっとだいぶ過酷だったのは、おそらくこれが原因だった(後述)


Elasticsearch(とLucene)の気持ちになって考える(8分まで、1分間)
複数のdata nodeでクラスタを構成する
検索indexをshardで分割して各data nodeで分散して持つ
shardはさらにreplicaごとにdata nodeに冗長化される
shardの実体はsegmentである
Luceneの概念
indexをファイルシステム上の小さいファイルとして持つ
ファイルなのか〜akix
リクエスト時に何が起きるか……
searchリクエスト時
全shardでindexを検索して、結果を統合する
indexingリクエスト時
メモリ上のIndexing Bufferから、指定したタイミングで、shardごとに、ファイルシステム上のsegmentに反映する
この処理がめちゃくちゃ重いので、可能な限り先延ばししたほうが良い


Elastic Cloud特有の世界観もある(7分まで、1分間)
Elastic Cloudにおいて、Elasticsearchクラスタは、Deploymentと呼ばれる
1つのDeploymentで、data nodeは最大でも3台まで
基本的に1つのregionの各AZに1台まで
たいていのクラウドインフラはAZはregionごとに3つ
data node超大量に立てて負荷分散させるみたいな力技はできない
Elastic Cloudとしてはdata nodeの性能を最大限引き出して3台のクラスタでスケールインすることを推奨
小さいdata nodeを大量に立ち上げるのは実は非推奨
data nodeのCPUとメモリがElasticsearchのパフォーマンスに大きく影響するため


----- ----- ここからは index settings お得情報です ----- -----
(index settingsとは、Elasticsearchがindexをどのように扱うかというindexに関するメタ的な設定項目です)

これはありがたいakix

定番テクニック(6分まで、1分間)
(公式ドキュメントに書いてある)
number_of_shardsdata nodeの倍数にして、それぞれのサイズを10GB以上、50GB以下にする
number_of_shardsのデフォルト値は1
number_of_shardsの値が1だと、shardの数って1つになるんですよ(進次郎構文
wakixnishioMijinko_SDtakano32
data nodeが3台あっても、number_of_shardsの値が1だと、1台しか使われない
ひぇ〜〜〜〜akix
なにそれこわいtetsuya-k
data nodeが3台あるなら、少なくとも3に変更しないと、意味がない
shardのサイズが50GBを超えそうだったら、6, 9, 12, 15...にする
後から変更不可能なので、見積もって設計する必要がある
indexingもsearchも、shardごとに実行される
data nodeの台数が少ないのに、小さいshardsが大量にあると、処理がメッチャ遅くなる
number_of_replicas一時的に0にする
number_of_replicasのデフォルト値は1
number_of_replicasの値が1だと、replicaの数って1つになるんですよ(進次郎構文
number_of_shardsが3でnumber_of_replicasが1だと、indexing時に6つのshardに書き込む必要がある
indexing時は0にしておくとパフォーマンスが向上する
あとで1以上に戻さないと耐障害性が失われる
bulk APIでindexingのリクエスト数を減らす
余談:bulk APIのリクエストボディが巨大すぎるとそれはそれでElasticsearchがご機嫌斜めになる


重要テクニック(5分まで、1分間)
(!!!公式ドキュメントに書いてない!!!)
refresh_interval 30s とかにする
?refresh=true のようなリクエストをすると refresh_interval は無視されて即座にrefreshされるので注意!
?refresh=true というリクエストをすると refresh_interval は無視されて即座にrefreshされるんですよ(進次郎構文
つまりメモリ上のIndexing Bufferからファイルシステム上のsegmentへの書き込みが即座に発生する
ワォakix
罠だ…Mijinko_SD
一方で、テストの際には ?refresh=true にする必要がある
GyazoではCircleCI上でElasticsearchを立ち上げて実際にindexingやsearchをリクエストしてテストしている
?refresh=true しないと、テストで30秒待つとか確率的に落ちるとかいった厳しい感じになる


index settings改善後(4分30秒まで、30秒間)
Indexingリクエストが殺到していても検索レスポンスが150ms以下
👏ナイススクショwakix
開発者のGyazoにこういう画像沢山眠ってそうMijinko_SD


----- ----- ここからは index mappings お得情報です ----- -----
(index mappingsとは、検索対象のドキュメントを実際にどのようなデータ型とデータ構造でindexingするかという設定項目です)



検索の高速化のためのindex mappings(3分まで、1分間)
(公式ドキュメントに書いてある)
全文検索したいフィールドが多数ある場合、copy_toで一つのフィールドにぶち込んで、それのみを検索対象にすると、速くなる
Elasticsearchの気持ちになって考えてみてください
たくさんのフィールドを検索するより、一つにまとめられたフィールドを検索したほうが、検索の回数が減って速いんですよ(進次郎構文
everything フィールドを用意する(名前は何でも良い)
ネーミングの由来は:Everything
ruby
everything: { type: 'text', analyzer: 'ngram_analyzer', }
ネーミング良すぎるakix
神クラスっぽさがあるakix
全文検索したいすべてのフィールドで copy_to: 'everything' する
ruby
title: { type: 'text', analyzer: 'ngram_analyzer', copy_to: 'everything', }, desc: { type: 'text', analyzer: 'ngram_analyzer', copy_to: 'everything', }, ocr: { type: 'text', analyzer: 'ngram_analyzer', copy_to: 'everything', },
everything フィールドに対して検索すると速い
ruby
{ match_phrase: { everything: term } }
逆にこれをやらないですべてのフィールド内の文字列を全文検索しようとするとどうなる…?
multi_matchで各フィールドを指定する
ruby
{ multi_match: { query: term, fields: %w[title desc ocr ...] } }
この検索クエリはElasticsearchにおいて内部的には各fieldsをそれぞれ検索して結果を結合してソートするのと変わらない
フィールド数が多い場合はこのテクニックは重要
Gyazoは様々な画像のメタデータを持っているため、この場合にあたる
Gyazoの全文検索はこれで実現されている


n-gramkuromojiを組み合わせて使い分けるテクニック(2分まで、1分間)
(公式ドキュメントに書いてある)
公式ドキュメントだいじMijinko_SD
へえ両方のインデックスを使えるの熱い daiiz
旧Elasticsearchでは、全て、kuromojiインデックスによる全文検索だった
実は検索できない単語があった
シマチョウ」がなぜか検索できなかったり
2月にhttps://gyazo.com/api/internal/search_result のレスポンス調べてたら image-20-kuromoji-filter という値が入っていたので裏で使っているのかな?と推測していたtakker
Elastic Cloudではn-gramインデックスで、1文字単位で全文検索できるようになった
意図しないTokenizeが行われてしまったり辞書に無い単語では検索できないという問題が解決できた
一方で、n-gramインデックスだと、More Like Thisが使えない
(使えるけど精度が微妙)
Elasticsearchのnested fieldsという概念を使うと、別のanalyzerによるtokenizeの結果を保持できる
n-gramとは別にkuromojiのインデックスを持つことで、いいとこ取りできる
ruby
everything: { type: 'text', analyzer: 'ngram_analyzer', fields: { kuromoji: { type: 'text', analyzer: 'kuromoji_analyzer', } } }
everything.kuromoji のようにdot notationでアクセスできるようになる
もちろんこのフィールドに対して任意の検索クエリが実行できる
nested fieldsに対して実行できないクエリが一部だけあるので注意
ruby
{ more_like_this: { fields: ["everything.kuromoji"], like: [ { _index: "index-name", _id: "document-id" } ] } }
Gyazoの関連画像欄はこれで実現されている



まとめ(0分まで、1分間)
Elastic Cloudに移行して良かった点
Elastic Cloudでハチャメチャな失敗をしたことで、ElasticsearchやLuceneについての理解が深まってよかった
ゼロダウンタイムでElasticsearchからElastic Cloudへ移行できた
すごいakix
Gyazoの検索インフラがフルマネージドオートスケールになった
ElasticsearchのマイナーバージョンアップはElastic Cloudで管理画面でボタンを押すだけで可能になった
Elastic Cloudで困っている点
Kuromojiの辞書を簡単にカスタマイズできない
新語に対応できない
Elastic Cloudの方と話をしたら仕様的には頑張れば辞書カスタマイズできるらしいです
elasticsearch-sudachiを使えない
追加できるカスタムプラグインの形式や容量が制限されている
(Elastic Cloudが機械学習による自然言語処理に対応したらしいけど全然大変)
Nota / Gyazoでは良い失敗ができるエンジニアを募集しています!!
いい話だakix
Elasticsearch / Luceneに詳しい方がいたらこの資料にツッコミお願いします!

オトク情報いっぱいあって嬉しいakix

type:gif
Gif画像を検索
app:"Google Chrome"
これ「同じアプリの画像を表示」から出てくるから極秘情報ではないと思うtakker
site:scrapbox.io
Scrapbox経由で貼り付けた画像を検索
title:typescript
ocr:Gyazo
camera: もある

極秘情報たすかるmtane0412
NANI!?yosider
なるほど、極秘情報で検索をかけるとこういった情報が出てくるのかwMijinko_SD
拡散しようと思ったらなんかショートカットが追加されてたmtane0412

真面目な話ですが、本来は機能追加やUI変更をしたら、ちゃんとユーザーさんに何が変わったのか言わずとも気づいてもらえなければならない/あるいは言葉で伝えなければならないのですが、Gyazoは日々メッチャ改善や機能追加をしているのにそれをお伝えできていません…!極秘情報というのは半分自虐です…!!yuiseki
takker
まあscrapboxもそんな感じですし
専門的な分野過ぎて伝えにくいというのもありそうMijinko_SD

関連リンク