読者です 読者をやめる 読者になる 読者になる

サナギわさわさ.json

サナギさんとキルミーベイベーとプログラミングが好きです

Elasticsearch実運用時の注意点とアンチパターンまとめ

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 [2.3] | 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

スワップしないように設定

公式で「起動時にメモリを確保し、スワップしないようにする」という設定が推奨されています。

Configuration | Elasticsearch Reference [2.3] | Elastic

起動時のメモリ確保は上に書いた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 [2.3] | 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のかなり丁寧なチュートリアル

AWS プログラミング Serverless Framework

サーバーレスアーキテクチャという言葉を聞くようになってしばらく経ちました。 サーバーレスアーキテクチャに関しては以下の記事が非常に綺麗なのでそちらをご覧ください。

サーバーレスアーキテクチャという技術分野についての簡単な調査 - Qiita

サーバーレス・アーキテクチャの話 - yoshidashingo

個人的にも、サーバーを一番簡単に建てる方法はサーバーを建てない事で、サーバーを一番簡単に管理する方法もサーバーを建てない事だと思っているので、サーバーレスアーキテクチャは使えるところでは使っていきたいです。(パフォーマンスが求められる部分ではまだ厳しいですが)

というわけで、Serverless Frameworkが 先日V.1 Alphaをリリースしたようなので試してみます。AWSアカウントの準備から行っているので、既にある程度の知識がある方にとっては冗長な記事になっているかと思います。ご容赦ください。

また、AWSの無料枠(アカウント開設後1年間有効)を持っている方でしたら本記事の内容を全て試してもお金はかからないはずです。

Serverless Frameworkとは

AWS Lambdaを主に活用してサーバレスなアプリケーションサービスを構築するためのフレームワークです。今後はGoogle Cloud Functions, Microsoft Azure,IBM OpenWhiskなどにも対応していくとの事です。Serverless Frameworkってまさにそのままの名前付けられるのも先駆者の特権というやつでしょうか。

blog.serverless.com

環境

  • OSX 10.11
  • Serverless V.1 Alpha

事前準備

本当に1からやるので、ほとんどの方は飛ばしていただいて大丈夫だと思います。

AWSアカウント作成

以下を参考にして行ってください。面倒くさいかもしれませんが、二段階認証と請求情報のアラームは絶対に設定しておきましょう。

AWS アカウント作成の流れとポイント | 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コンソール

f:id:kakakazuma:20160716162603p:plain

API Gatewayコンソール

f:id:kakakazuma:20160716162719p:plain

最後に実際に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クエリを可視化する

AWS EMR プログラミング

BIツールには色々なものがありますが、今回はOSSのre:dashを使ってEMR上で実行するHive・Prestoクエリを可視化してみます。

re:dashとは

re:dashはOSSPython製可視化ツールです。Web UIからRedshift・BigQuery・MySQL・Hiveなど様々なデータソースのクエリを可視化できます。(対応してるデータソースの数がすごく多いです)

redash.io

今回はEMR上のHive・Prestoと接続して可視化を行ってみます。

環境

  • EMR : 4.7.1
  • Hadoop : Amazon 2.7.2
  • Hive : 1.0.0
  • Presto : 0.147
  • re:dash : v0.11.0.b2016

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を使ってください。僕はローカルから実行しました。

re:dash fabfile · GitHub

$ pip install fabric requests
$ fab -H {$hostname} -u {$username} -i {$sshkey(任意)} deploy_latest_release

アクセス

ここまでやったらhttp://[public ip]/loginにアクセスし、以下の画面が表示されることを確認します。

f:id:kakakazuma:20160715202613p:plain

初期では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を入力して下さい。

f:id:kakakazuma:20160715202625p:plain

f:id:kakakazuma:20160715202639p:plain

クエリの可視化

データソース選択

実際にクエリを可視化してみます。http://[public ip]/queries/newにアクセスし、「Data Source」で先ほど登録したHiveデータソースを選択してみます。

Hiveに登録済みのテーブルがあれば画面左側に表示されます。

f:id:kakakazuma:20160715202025p:plain

クエリ可視化

最後に実際にクエリを入力し、「Execute」を押します。 画面下にクエリ結果が出て来れば成功です。あとは「NEW VISUALIZATION」から適当に可視化してください。

クエリの最後に;が入っているとエラーになるので注意してください。それ以外の理由で上手くいかない時は/opt/redash/logsを見てみましょう。OSSなのでコードとログを両方見れば何とかなるはずです。

f:id:kakakazuma:20160715202036p:plain

終わり

以上です。最近可視化ツールも増えてきましたが、re:dashはクエリのスケジュール実行ができたりデータソースが多かったり、何よりOSSで面白いので是非触ってみてください。