Elasticsearch実運用時の注意点とアンチパターンまとめ
Elasticsearch実運用時に個人的に気をつけている点をまとめました。 自分でも整理できていない部分がありますので、間違っている点・追加した方が良い点などありましたら教えていただけると非常に助かります。
目次
インデックス・スキーマ設定
スキーマを事前に定義しておく
Elasticsearchはスキーマレスじゃないのか!といきなり突っ込まれそうな感じですが、個人的にはデータ投入前にスキーマを厳密に定義しておくのをお勧めします。
理由としては、主に以下の2点です。
- スキーマを定義せずに投入されたデータはElasticsearchが型を判断するためエラーの元になりやすい
- not_analyzed・doc_valueなどの詳細設定(後述)を行ったほうが性能が良くなる
_allの使用は必要な時のみ行う
デフォルトで全フィールドのデータが入る_allというメタフィールドがありますが、ディスク容量・性能の観点から全フィールドを跨いだ検索を行う予定が無い場合は無効にしたほうが良いです。
{ "mappings" : { "sample_type" : { "_all" : {"enabled" : false} } } }
部分一致検索をしないstringフィールドにはnot_analyzedをつける
stringフィールドはデフォルトではインデックス時に形態素解析などの処理が行われてしまうため、完全一致検索時に想定しない結果が返ってきたりインデックス処理が重くなったりしてしまいます。
完全一致検索のみを行いたいstringフィールドに対しては、マッピング時にnot_analyzedをつけることを推奨します。
"string_field" : { "type" : "string", "index" : "not_analyzed" }
doc_value:trueをつける
マッピングの際にdoc_values:trueを設定すると、cacheをメモリではなくディスクに乗せることができます。メモリに乗せる場合よりも20%程低速になりますが、ヒープメモリに影響されなくなり、挙動が安定するのでオススメです。
なお、not_analyzedではないstringフィールドには適用できないので注意してください。
Disk-Based Field Data a.k.a. Doc Values | Elastic
"string_field" : { "type" : "string", "index" : "not_analyzed", "doc_values":true }
できるだけエイリアスを使う
アプリからインデックスを参照する際に、インデックス名を直接参照するのではなくエイリアスを設定してそちらを参照することで、変更に強くなります。
一般的に検索仕様の変更などを行う際はインデックス再作成を行う必要があるのですが、エイリアスを利用すればダウンタイム無しで柔軟に対応することができます。
Changing Mapping with Zero Downtime | Elastic
Elasticsearch インデックス・エイリアス – Hello! Elasticsearch. – Medium
Elasticsearch のインデックスを無停止で再構築する - クックパッド開発者ブログ
以下は、test_indexに紐付いていたエイリアスをtest_index_2に切り替える際のコマンドです。アプリからは常にtest_index_aliasを参照するようにしておけば、ダウンタイム無しでアプリから新しいindexを参照できるようになります。
curl -XPOST 'http://localhost:9200/_aliases' -d ' { "actions" : [ {"remove": {"index": "test_index", "alias": "test_index_alias" }}, {"add": {"index": "test_index2", "alias": "test_index_alias" }} ] }'
必要に応じてルーティングを行う
デフォルトではデータがどのシャードに配置されるかはidのハッシュ値をシャード総数で割った値で決定されますが、マッピング時に任意のフィールドを_routingに指定する事で、そのフィールドの値によってシャードの配置を決めることができます。
適切に設定すると検索時の負荷を減らすことができますが、不適切なフィールドを指定するとホットシャードが生まれてしまうので注意して設定してください。自信が無いならデフォルト設定のままで良いです。
対象のフィールドは、store:trueかつindex:not_analyzedである必要があります。また、required:trueでなくてはなりません。
_routing field | Elasticsearch Reference [6.1] | Elastic
{ "mappings" : { "sample_type" : { "_routing" : {"required" : true, "path" : "route_param"}, "properties" : { "route_param" : { "type" : "string", "index" : "not_analyzed", "required" : "true", "store" : true } } } } }
本体の設定
ヒープメモリを適切に設定する
公式に書いてある通り、ヒープメモリは物理メモリの半分までで32GBを超えないようにしましょう。
Heap: Sizing and Swapping | Elasticsearch: The Definitive Guide [2.x] | Elastic
/etc/sysconfig/elasticsearch
ES_HEAP_SIZE:4g
スワップしないように設定
公式で「起動時にメモリを確保し、スワップしないようにする」という設定が推奨されています。
http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html
起動時のメモリ確保は上に書いたES_HEAP_SIZEの設定でOKなので、後はスワップしないようにする設定を行います。
/etc/elasticsearch/elasticsearch.yml
bootsrap.mlockall:true
環境によって上手くいかない時があるので、設定が効いているかどうかhttp://localhost:9200/_nodes/process?prettyを見て確認しましょう。
上手くいかない時は以下の記事も見てみてください。
ElasticSearchでmlockall:trueが効かない時にチェックすること - サナギわさわさ.json
スプリットブレイン対策を行う
スプリットブレイン対策のために、マスターノードを選ぶ際に必要となる接続ノードの数をノード数/2+1
に設定しておきましょう。例えば6台構成の場合は4になります。
/etc/elasticsearch/elasticsearch.yml
discovery.zen.minimum_master_nodes:4
curlから設定する場合はこっち
curl -XPUT http://localhost:9200/_cluster/settings -d '{ "persistent": {"discovery.zen.minimum_master_nodes": 4} }'
field data cacheに上限を与える
Elasticsearchでは各fieldのデータをメモリに載せることで検索を高速化しているのですが、デフォルトでは無制限にメモリに載るため、OutOfMemoryErrorが発生することがあります。 以下は、使用可能なメモリをHEAP_SIZEの75%に変更する例です。
/etc/elasticsearch/elasticsearch.yml
indices.fielddata.cache.size: 75%
検索高速化
Warmerを登録する
各fieldのデータがメモリに載ることで検索が高速化されるのは上述した通りですが、fieldのデータをメモリに載せる処理はインデックス再構築後1回目のクエリが投げられた時に行われるため、1回目のクエリがかなり重くなることが多いです。
Warmerクエリを設定しておくと、インデックス再構築後に自動でクエリを発行してfield cacheを作成してくれるので便利です。
Warmers | Elasticsearch Reference [6.1] | Elastic
curl -XPUT localhost:9200/testindex/_warmer/warmer_1 -d '{ "query" : { "match_all" : {} }, "aggs" : { "aggs_1" : { "terms" : { "field" : "field" } } } }'
queryとfilterを使い分ける
条件検索はqueryでもfilterでも行えますが、スコア計算を用いない検索条件(完全一致やrangeなど)ならqueryではなくfilterを用いた方が早いです。
Optimizing Elasticsearch Searches | Elastic
filterのキャッシュ設定を使い分ける
filterを使うとフィルタで引っかかったリクエストをデフォルトでキャッシュしてくれます。
ただ全てキャッシュしているとメモリが足りなくなる事が多いので、明らかにキャッシュしない方が良い絞り込み条件に関しては検索の際にキャッシュしないように明示的に設定しましょう。
必要無いフィールドは検索の際に取得しない
言うまでも無いですね。。。
インデクシング高速化
困ったら公式を見ましょう。
Performance Considerations for Elasticsearch Indexing | Elastic
あと以下の記事にほとんどまとまっているので、詳しくはそちらを見た方が良いかもしれません。
Elasticsearchインデクシングパフォーマンスのための考慮事項 - Qiita
bulk APIを使う
大規模データの投入には必ずbulk APIを使いましょう。なお、公式では1つのbulk Importのサイズは5~15MBにすることを推奨しています。
curl -XPOST http://localhost:9200/_bulk --data-binary @databatch.json >/dev/null
コミット頻度の変更
ElasticSearchはNRT(Near Real-Time Search)機能を搭載しており、デフォルトで1秒ごとにソフトコミットが実行され、更新結果が検索に反映されます。
インデクシング時にはこれが結構重いため、更新結果をそこまでリアルタイムに反映する必要がない場合は、ソフトコミットの頻度は30sぐらいに設定した方が早くなります。
curl -XPUT http://localhost:9200/testindex/_settings -d ' { "index" : { "refresh_interval" : "30s" } }'
レプリカシャードを一時的に0に設定
インデクシング中のみレプリカシャードを作らないようにすることで、 データ投入にかかる時間が短くなります。リスクとのトレードオフです。
curl -XPUT http://localhost:9200/testindex/_settings -d ' { "index" : { "number_of_replicas" : 0 } }'
ノード追加・削除
ノード追加・削除時はスプリットブレインが起こりやすいので注意してオペレーションしましょう。また、オペレーション後にdiscovery.zen.minimum_master_nodes
を変更するのを忘れないようにしてください。
シャードの自動配置を停止
ノードの追加・削除処理を行う際に、デフォルトだとシャードの再配置が走って負荷が一気に上がります。処理の前にシャードの自動配置を停止しておきましょう。
Elasticsearchのクラスタにノードを追加するときにやっていること - すずけんメモ
curl -XPUT http://localhost:9200/_cluster/settings -d '{ "persistent": {"cluster.routing.allocation.enable": "none"} }'
ちなみにオペレーション終了時に自動配置を有効に戻す場合は以下です。
curl -XPUT http://localhost:9200/_cluster/settings -d '{ "persistent": {"cluster.routing.allocation.enable": "all"} }'
シャードを手動で再配置
ノード追加の際は以下のオペレーションで問題ありませんが、
- シャードの自動配置を停止
- ノードを追加
- シャードの自動配置を有効に戻す
ノード削除の際は一手順増えます。
- シャードの自動配置を停止
- 削除対象ノードからシャードを移動
- ノードを削除
- シャードの自動配置を有効に戻す
シャードの手動再配置は以下のようにして行えます。面倒ですが1つずつ移動させるのをお勧めします。
curl -XPOST 'localhost:9200/_cluster/reroute' -d '{ "commands" : [ { "move" : { "index" : "test", "shard" : 0, "from_node" : "node1", "to_node" : "node2" } } ] }'
終わり
以上です。冒頭にも書きましたが間違っている点・追加した方が良い点などありましたら教えていただけると助かります!
弊社では現在エンジニアを募集中です!
Serverless Framework V.1 Alphaのかなり丁寧なチュートリアル
サーバーレスアーキテクチャという言葉を聞くようになってしばらく経ちました。 サーバーレスアーキテクチャに関しては以下の記事が非常に綺麗なのでそちらをご覧ください。
サーバーレスアーキテクチャという技術分野についての簡単な調査 - Qiita
サーバーレス・アーキテクチャの話 - yoshidashingo
個人的にも、サーバーを一番簡単に建てる方法はサーバーを建てない事で、サーバーを一番簡単に管理する方法もサーバーを建てない事だと思っているので、サーバーレスアーキテクチャは使えるところでは使っていきたいです。(パフォーマンスが求められる部分ではまだ厳しいですが)
というわけで、Serverless Frameworkが 先日V.1 Alphaをリリースしたようなので試してみます。AWSアカウントの準備から行っているので、既にある程度の知識がある方にとっては冗長な記事になっているかと思います。ご容赦ください。
また、AWSの無料枠(アカウント開設後1年間有効)を持っている方でしたら本記事の内容を全て試してもお金はかからないはずです。
Serverless Frameworkとは
AWS Lambdaを主に活用してサーバレスなアプリケーションサービスを構築するためのフレームワークです。今後はGoogle Cloud Functions, Microsoft Azure,IBM OpenWhiskなどにも対応していくとの事です。Serverless Frameworkってまさにそのままの名前付けられるのも先駆者の特権というやつでしょうか。
環境
- OSX 10.11
- Serverless V.1 Alpha
事前準備
本当に1からやるので、ほとんどの方は飛ばしていただいて大丈夫だと思います。
AWSアカウント作成
以下を参考にして行ってください。面倒くさいかもしれませんが、二段階認証と請求情報のアラームは絶対に設定しておきましょう。
AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ - Qiita
IAMユーザ作成
https://console.aws.amazon.com/iam/home#usersからServerless Frameworkが使用するためのIAMユーザーを作成します。アクセスキー・シークレットキーを安全な場所に保管しておいてください。
※公式のTutorialでは簡易化のため作成したユーザーにAdministratorAccessのポリシーを付けていますが、本番で使う時は適切な権限だけをつけるようにしてください。
AWS CLIインストール
AWS CLIをインストールします。インストール済みの方はスキップでOKですが、公式では[default]にServerless Frameworkが使用するIAMユーザーを設定するよう推奨していますので、そこだけ設定を変更したほうが良いかもしれません。
serverless/provider-account-setup.md at v1.0 · serverless/serverless · GitHub
We'd recommend to create a default AWS profile on your local machine as Serverless uses this as a default which makes developing a lot faster and easier.
$ sudo pip install awscli --ignore-installed six $ aws configure # 先ほど作成したIAMユーザのアクセスキーを入力 AWS Access Key ID [None]: xxxxxxxxxxxx AWS Secret Access Key [None]: xxxxxxxxxxxx Default region name [None]: us-west-2 Default output format [None]: ENTER $ cat ~/.aws/credentials [default] #ここがServerless FrameworkのIAMになっていること推奨 aws_access_key_id = xxxxxxxxxxxx aws_secret_access_key = xxxxxxxxxxxx [user_2] aws_access_key_id = xxxxxxxxxxxx aws_secret_access_key = Fxxxxxxxxxxxx
node環境構築
Serverless Frameworkのインストールにnpmが必要なので、node環境を構築します。 nodeはnodebrewを使って管理しますが、OSX 10.11ではnodebrewはhomebrewでインストールすると正常に動かないので注意してください。公式に従ってcurlを使ってインストールしましょう。
GitHub - hokaccha/nodebrew: Node.js version manager
$ curl -L git.io/nodebrew | perl - setup
~/.bash_profileに以下を追加
export PATH=$HOME/.nodebrew/current/bin:$PATH
設定を反映
$ source ~/.bash_profile
nodebrewを使ってnodeの安定版をインストール(今回はv6.3.0)
$ nodebrew install-binary stable
fetch: http://nodejs.org/dist/v6.3.0/node-v6.3.0-darwin-x64.tar.gz
######################################################################## 100.0%
Install successful
インストールされたバージョンを使うように設定
$ nodebrew ls v6.3.0 current: none $ nodebrew use v6.3.0 use v6.3.0 $ node -v v6.3.0
一応npmもupdateしておく。
$ npm install -g npm $ npm --version 3.10.5
Serverless Frameworkインストール
事前準備が終わったので、実際にServerless FrameworkをインストールしてHello Worldを行ってみます。ここからは公式のセットアップ手順に従って行います。
インストール自体はコマンド1つ打つだけで完了します。
$ npm install -g serverless@alpha $ sls --help _______ __ | _ .-----.----.--.--.-----.----| .-----.-----.-----. | |___| -__| _| | | -__| _| | -__|__ --|__ --| |____ |_____|__| \___/|_____|__| |__|_____|_____|_____| | | | The Serverless Application Framework | | serverless.com, v1.0.0-alpha.1 -------'
プロジェクト作成
$ sls create --name first-service --provider aws Serverless: Creating new Serverless service... Serverless: Successfully created service "first-service" Serverless: |- serverless.yaml Serverless: |- serverless.env.yaml Serverless: |- handler.js
設定ファイル書き換え
公式のドキュメントを参考にして設定ファイルを書き換えます。今回はランタイムをnodejs4.3で、HTTP経由でアクセスできるようにします。
serverless.yaml
service: first-service provider: aws runtime: nodejs4.3 defaults: # overwrite defaults stage: dev region: ap-northeast-1 memory: ${memoryVar} # reference a Serverless variable timeout: 30 functions: hello: handler: handler.hello events: - http: path: greet method: get
serverless.env.yaml
vars: memoryVar: 256 stages: dev: vars: regions: ap-northeast-1: vars:
デプロイ
設定ファイルを変更したらデプロイします。
$ sls deploy Serverless: Creating Stack... Serverless: Checking stack creation progress... Serverless: Stack successfully created. Serverless: Zipping functions... Serverless: Uploading zip files to S3... Serverless: Adding function resources to CF... Serverless: Updating Stack... Serverless: Checking stack update progress... Serverless: Deployment successful!
デプロイが成功したら、AWSのコンソールにアクセスして確認してみます。 LambdaのコンソールとAPI Gatewayのコンソールにアクセスして、実際に登録されていればOKです。
Lambdaコンソール
API Gatewayコンソール
最後に実際にHTTP経由でアクセスできるかどうか確認してみます。きちんと結果が返ってきますね!
$ curl https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/greet {"message":"Go Serverless v1.0! Your function executed successfully!","event":{"body":{},"method":"GET","headers":{...}}
終わり
以上でServerless Frameworkのチュートリアルは終了です。AWS LambdaとAPI Gatewayを手動で連携させようとするとかなり面倒くさいので、個人的には非常に良い感じです。 今後も使えるところでは是非使っていきたいと思います。
re:dashでEMR上のHive・Prestoクエリを可視化する
BIツールには色々なものがありますが、今回はOSSのre:dashを使ってEMR上で実行するHive・Prestoクエリを可視化してみます。
re:dashとは
re:dashはOSSのPython製可視化ツールです。Web UIからRedshift・BigQuery・MySQL・Hiveなど様々なデータソースのクエリを可視化できます。(対応してるデータソースの数がすごく多いです)
今回はEMR上のHive・Prestoと接続して可視化を行ってみます。
環境
EMR立ち上げ
EMRの立ち上げに関しては割愛します、公式を参考にしてやれば大丈夫かと思います。今回はマスターノードをm3.xlarge1台・コアノードをm3.xlarge2台にして立ち上げました。
https://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/gsg-launch-cluster.html
注意点
アプリケーションにHive, Presto-Sandboxを入れるのを忘れないようにして下さい。 HiveServer2に関してはEMR4.7.1では勝手に立ち上がるので大丈夫です。
また、セキュリティグループでHiveとPrestoのポートをre:dashのインスタンスに対して解放しておきましょう。Hiveがデフォルト10000で、Prestoがデフォルト8889です。
re:dashのインスタンス立ち上げ
公式のセットアップ手順は以下です。
http://docs.redash.io/en/latest/setup.html
上のURLにAWSのセットアップ済みAMIが公開されているので、それを使ってインスタンスを立ち上げます。 今回はami-78967519を使いました。
インスタンスタイプはデモならt2.microで十分です。また、セキュリティグループで22,80,443ポートを開けておいて下さい。
依存関係インストール
AMIからインスタンスを作成した場合は特に依存関係のインストールはいらないと思っていたのですが、pythonでHiveと接続するpyHive周りで色々とエラーが出てしまったので依存関係を手動でインストールします。(AMIのバージョンによっては必要ないかもしれません。また、Hiveを使わない場合は必要ない手順だと思います)
$ sudo apt-get update $ sudo apt-get install libsasl2-dev python-dev libldap2-dev libssl-dev $ sudo apt-get install git $ sudo pip install git+https://github.com/dropbox/PyHive.git $ sudo pip install pyhive[hive]
※pyHiveをpipからインストールしたら
No module named TCLIService
エラーが出てしまったのでgitから直接インストールしています。
re:dashのアップデート
re:dashはかなりアップデートが頻繁で、AMIに入っているバージョンが少し古くなっているので最新版へのアップデートを行います。
公式がアップデート用のfabricスクリプトを提供してくれているので今回はそれを使います。詳しくは以下に記載してあります。
http://docs.redash.io/en/latest/upgrade.html
fabricを用いたアップデート手順
以下のfabfile.pyを使ってください。僕はローカルから実行しました。
$ pip install fabric requests $ fab -H {$hostname} -u {$username} -i {$sshkey(任意)} deploy_latest_release
アクセス
ここまでやったらhttp://[public ip]/login
にアクセスし、以下の画面が表示されることを確認します。
初期ではEmail,Passwordともadminでログインできます。
Hive・PrestoのData Sourcesへの追加
EMR上のHive・Prestoをデータソースに追加します。その他のデータソースに関してはhttp://docs.redash.io/en/latest/datasources.htmlをご覧ください。
http://[public ip]/data_sources
にアクセスし、「New Data Source」を選択します。
Typeから「Hive」「Presto」を選択し、必要項目を入力します。 HostにはEMRクラスタのマスタノードのIPを入力して下さい。
クエリの可視化
データソース選択
実際にクエリを可視化してみます。http://[public ip]/queries/new
にアクセスし、「Data Source」で先ほど登録したHiveデータソースを選択してみます。
Hiveに登録済みのテーブルがあれば画面左側に表示されます。
クエリ可視化
最後に実際にクエリを入力し、「Execute」を押します。 画面下にクエリ結果が出て来れば成功です。あとは「NEW VISUALIZATION」から適当に可視化してください。
クエリの最後に;が入っているとエラーになるので注意してください。それ以外の理由で上手くいかない時は/opt/redash/logs
を見てみましょう。OSSなのでコードとログを両方見れば何とかなるはずです。
終わり
以上です。最近可視化ツールも増えてきましたが、re:dashはクエリのスケジュール実行ができたりデータソースが多かったり、何よりOSSで面白いので是非触ってみてください。