PHP から Elasticsearch を操作するために Elastica というライブラリを使っている.今まで EC2 で運用していた Elasticsearch を Amazon ES に移行する話があり,Elastica から Amazon ES を操作するときに悩むことが多かったため,まとめておこうと思う.
ちなみに,僕は使ったことはないけれど,Elastic 社から公式に提供されている elasticsearch-php というライブラリもある.ただ,Elastic 社の公式ライブラリだと,積極的に Amazon ES 対応をすることはないだろうし,Amazon ES を使う場合は,サードパーティライブラリの Elastica の方が良い気がする.
Amazon ES に接続する方法
Amazon ES に接続する方法を復習しておくと,大きく3種類ある.
- リソースベース
- IP ベース
- IAM ユーザーとロールベース
今回のように,アプリケーションから Amazon ES に接続する場合は IAM を使う.IAM Role を使うのがベスト.必要に応じて IP 設定を変更して運用することも考えられるが,Amazon ES で IP 設定を変更すると,ノードが全台作り直されてしまうため,クラスタのデータ規模によっては1時間程度待たされることもあるので,実質不可能だと思うし,そもそも,アーキテクチャ的に微妙すぎる.
また,IP ベースの場合は未署名の匿名リクエストも受け付けることができるが,IAM を使う場合,署名付きリクエスト(AWS 署名バージョン 4)を送る必要がある.よって,ライブラリが署名付きリクエストに対応している必要がある.
Elastica から Amazon ES に接続する
Issue を見ると,Elastica は既に署名付きリクエストの対応がされているが,実装例などのドキュメントが全くなくて,よくわからなかった.困ったので Gitter で聞いてみたら,すぐ教えてもらうことができて助かった.簡単過ぎるけど,今回の構成をまとめた.
ライブラリをインストールする
署名付きリクエストを送るためには,Elastica 3.1.1 以上が必要になる.ただし,このままで実行すると,以下のエラーが出る.
Class 'GuzzleHttp\Psr7\Uri' not found
Elastica の composer.json
を見ると,suggest
プロパティに以下の設定がある.suggest
とは,基本的には必要ないけど,入れておくと便利な依存パッケージのことを意味している.Ruby の Gemfile にはそういう概念がないため,今まで知らなかった.
{ (中略) "require-dev": { "aws/aws-sdk-php": "~3.0", "guzzlehttp/guzzle": "~6.0" }, "suggest": { "aws/aws-sdk-php": "Allow using IAM authentication with Amazon ElasticSearch Service", "egeloen/http-adapter": "Allow using httpadapter transport", "guzzlehttp/guzzle": "Allow using guzzle 6 as the http transport", "monolog/monolog": "Logging request" }, (中略) }
よって,Amazon ES に IAM で接続する場合は,以下のような composer.json
になる(バージョン設定はご自由に).
{ (中略) "require": { "ruflin/Elastica": "~3.2", "aws/aws-sdk-php": "~3.25", "guzzlehttp/guzzle": "~6.2" }, (中略) }
まだ aws/aws-sdk-php
v2 を使っていると動かないため,合わせて aws/aws-sdk-php
をバージョンアップする必要がある.
Elastica のクライアント実装
以下のように transport
に AwsAuthV4
を設定して,aws_region
を設定すると,Amazon ES に IAM で接続できるようになる.
$client = new Elastica\Client( array( 'host' => 'xxx.ap-northeast-1.es.amazonaws.com', 'port' => 80, 'transport' => ['type' => 'AwsAuthV4', 'postWithRequestBody' => true], 'aws_region' => 'ap-northeast-1' ) );
もし IAM Role ではなく,クレデンシャルを埋め込む場合(非推奨な実装)は,以下のようになる.
$client = new Elastica\Client( array( 'host' => 'xxx.ap-northeast-1.es.amazonaws.com', 'port' => 80, 'transport' => ['type' => 'AwsAuthV4', 'postWithRequestBody' => true], 'aws_access_key_id' => 'yyy', 'aws_secret_access_key' => 'xxx', 'aws_region' => 'ap-northeast-1' ) );
まとめ
- Elastica は Amazon ES の接続をサポートしている
- ドキュメントが詳しくないため,困ったら Gitter で聞いてみると良い
- 必ず IAM Role を使おう