@johtaniの日記 2nd

@johtani ‘s blog 2nd edition

Groovyスクリプトをダイナミックスクリプトなしで実行(日本語訳)

※この記事は次のブログを翻訳したものになります。

原文:running groovy scripts without dynamic scripting

Elasticsearch1.3.8と1.4.3のリリースにより、デフォルトで、リクエストに含まれるGroovyスクリプトや インデックスに保存されたスクリプトを動的に実行する機能をオフにしました。 しかし、Groovyはまだデフォルトのスクリプト言語です。 本ブログ記事では、少しだけダイナミックだが、サンドボックスではない言語のためのスクリプトを どのように使い続けるかを説明します。

本ブログ記事は、それが何を意味し、さらに重要なのは、安全に重要なタスクを実行させるためにスクリプトを どのように使用し続けるかを理解する助けとなるはずです。

ダイナミックスクリプトとは?

Elasticsearchに詳しくない方のために、Elasticsearchでは、 さまざまなリクエストの一部としてスクリプトを送信することができます。 search、aggregation、update、upsert、delete by queryなどです。 あなたのユースケースのために、通常の動作よりも拡張した動作をさせるためにスクリプトを追加できます。

例えば、以下のリクエストは、ダイナミックスクリプトを含んでいます。 field1field2 + shiftが同じ値を持っている時だけドキュメントを返します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /_search
{
  "query":{
    "filtered":{
      "filter":{
        "script":{
          "script":"doc['field1'].value == (doc['field2'].value + shift)",
          "lang":"groovy",
          "params":{
            "shift":3
          }
        }
      }
    }
  }
}

言語を変えることもできます。 それは、当然、シンタックスが変わったり、制限が追加(例えば、Groovyスクリプトの代わりにLucene Expressionsに変更)されることもあります。 langパラメータによって言語を指定できます。

なぜそれはダイナミック?

上記の例はダイナミックスクリプトです。 それは、実際のスクリプトの部分はサーバサイドで動的に解釈されコンパイルされる必要があるからです。 ダイナミックスクリプトはElasticsearchのAPIによってデータノードに送信されます。 これは、インデックスされたスクリプト(indexed script)も含みます。

言い換えると、もし、スクリプトがデータノード全てに保存されていなければ、 それは、ダイナミックスクリプトとして扱われます。

dynamic scriptingをオフにするとどうなるか?

最新のリリースでの変更により、Groovyのdynaic scriptingはデフォルトでオフになりました。 先ほどのスクリプトについても同様で、もし、先ほどのリクエストを実行すると、次のようなエラーが発生します。 (一部省略してあります。)

1
2
3
4
{
   "error":"SearchPhaseExecutionException[Failed to execute phase [query], all shards failed; shardFailures {[8FJ02MofSnqVvOQ10BXxhQ][test][0]: SearchParseException[[test][0]: from[-1],size[-1]: Parse Failure [Failed to parse source [{...}]]]; nested: ScriptException[dynamic scripting for [groovy] disabled]...",
   "status":400
}

エラーメッセージの重要な箇所は「ScriptException[dynamic scripting for [groovy] disabled]」です。

スクリプティングを使い続けるには?

Elasticsearchでスクリプトを実行するには3つの方法があります。 2つのダイナミックな方法は、リクエストごとのスクリプト(上述)かインデックスされた スクリプト(indexed script)を使う方法です。 インデックスされたスクリプトを使うことは、Elasticsearch自身にGroovyスクリプトを保管することで 利用で、それらを要求に応じて利用することです。 (これは、実際には十分機能しますが、これではまだ、信頼できないユーザに対して彼らのスクリプトを実行できます) RDBのように保存されたプロシージャとして同じ方法で実行させるものと同様です。 前もって、スクリプトを記述しておき、リクエストの一部として後から、名前で呼び出して実行可能です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /_search
{
  "query":{
    "filtered":{
      "filter":{
        "script":{
          "script_id":"your_custom_script",
          "lang":"groovy",
          "params":{
            "shift":3
          }
        }
      }
    }
  }
}

あまり変わっていないことに気づくでしょう。 scriptの部分が、前もって記述されたスクリプトの名前script_idに変更されただけです。

Elasticsearchにスクリプトを提供するダイナミックではない方法はインデックスに保存する代わりに、 ディスクにファイルとしてスクリプトを保存することです。 そうすることで、各スクリプトを設定として保存します。 これは、どのようなスクリプト言語に対してもダイナミックスクリプティングをオフにしたまま、 サンドボックス化されないスクリプトを使い続けることができる方法です。

最初のサンプルで、Groovyスクリプトはdoc['field1'].value == doc['field2'].value + shiftでした。 これを、.groovy拡張子を持ったファイルとして書き出すことができます。

1
doc['field1'].value == (doc['field2'].value + shift)

もし、このファイルにyour_custom_script.groovyちう名前をつけて、 Elasticsearchのすべてのデータノードのconfig/scriptsディレクトリに保存すると、 Elasticsearchは60秒(elasticsearch.ymlのwatcher.intervalで変更可能)でこのスクリプトを認識し、今後のリクエストに利用できるようにプリコンパイルするでしょう。 そのファイルはElasticsearch実行ユーザによって読み込みができる必要があります。 これをディスクに書き込んだ後、あなたの設定ディレクトリは次のようになっています。

1
2
3
4
5
config/
  elasticsearch.yml
  logging.yml
  scripts/
    your_custom_script.groovy

これは、各リクエストやインデックスされたスクリプトをスクリプトとして動的に送信しませんが、 信頼された環境にスクリプトを追加することでダイナミックスクリプトとなることを許します。

ディスクに書かれたスクリプトを使用する

スクリプトは、ロードされたスクリプトになるまでは、利用できません。 ログファイルに次のようなログが表示されるまではです。

1
[2015-02-11 11:14:47,066][INFO ][script                   ] [Sergei Kravinoff] compiling script file [/path/to/elasticsearch-1.4.3/config/scripts/your_custom_script.groovy]

すべてのElasticsearchのデータノードでスクリプトが読み込まれたら、 それを利用することができます。 利用するために、filescript_idではありません!)としてスクリプト名を指定します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /_search
{
  "query":{
    "filtered":{
      "filter":{
        "script":{
          "file":"your_custom_script",
          "lang":"groovy",
          "params":{
            "shift":3
          }
        }
      }
    }
  }
}

Note:langは必須ではありません。Groovyがデフォルトの言語のためです。 もし、違うスクリプト言語を使いたい、もしくは、デフォルトの言語を(例えば、Lucene Expressionsへ) 変更したい場合、言語が正しいスクリプトを見つけるために提供されている必要があります。 一番良い方法は、アプリケーションがlangパラメータを含んでいることを勧めます。 これは、将来、デフォルトのスクリプト言語が変更されても、問題ないからです。

質問?

もし、質問があれば、遠慮なくTwitter(@elasticsearch)で教えて下さい。 また、問題がありましたら、GitHub issues pageで報告をお願いします。

Comments