本日も乙

ただの自己満足な備忘録。

FluentdからAmazon Elasticsearch Serviceへログ転送する時の注意点

昨年10月にAmazon Elasticsearch Service(Amazon ES)がリリースされました。
今まではEC2インスタンスにElasticsearch をインストールして運用していましたが、AWS側でマネージドしてくれるということでとても便利そうだなと思い使ってみました。

動作環境は以下の通りです。

  • Fluentd 0.10.58
  • fluent-plugin-elasticsearch 0.9.0
  • Amazon Elasticsearch Service r3.xlarge

構成としては、各WebサーバからApacheアクセスログをFleuntdサーバに集約してAmazon ESへ転送するようにしていました。
しかし、FluentdからAmazon ESへ転送する際に途中でエラーが出てしまい、転送できなくなることがありました。

2015-12-14 07:00:55 +0900 [warn]: failed to flush the buffer. error_class="Elasticsearch::Transport::Transport::Error" error="Cannot get new connection from pool." plugin_id="object:3f883d4b52e4"
2015-12-14 07:00:55 +0900 [warn]: retry count exceededs limit.
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/elasticsearch-transport-1.0.12/lib/elasticsearch/transport/transport/base.rb:182:in `perform_request'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/elasticsearch-transport-1.0.12/lib/elasticsearch/transport/transport/http/faraday.rb:20:in `perform_request'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/elasticsearch-transport-1.0.12/lib/elasticsearch/transport/client.rb:119:in `perform_request'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/elasticsearch-api-1.0.12/lib/elasticsearch/api/actions/bulk.rb:80:in `bulk'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/fluent-plugin-elasticsearch-0.9.0/lib/fluent/plugin/out_elasticsearch.rb:170:in `send'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/fluent-plugin-elasticsearch-0.9.0/lib/fluent/plugin/out_elasticsearch.rb:163:in `write'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/fluentd-0.10.58/lib/fluent/buffer.rb:300:in `write_chunk'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/fluentd-0.10.58/lib/fluent/buffer.rb:280:in `pop'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/fluentd-0.10.58/lib/fluent/output.rb:315:in `try_flush'
  2015-12-14 07:00:55 +0900 [warn]: /usr/local/rvm/gems/ruby-2.1.2/gems/fluentd-0.10.58/lib/fluent/output.rb:136:in `run'

Fluentd側の設定(一部抜粋)

    index_name accesslog
    type_name apache
    type elasticsearch
    include_tag_key true
    tag_key @log_name
    host xxxxxx.ap-northeast-1.es.amazonaws.com
    port 443
    scheme https
    logstash_prefix apache_access
    logstash_dateformat  %Y.%m.%d-%H
    logstash_format true
    flush_interval 10s

最初からエラーが出るわけではなく途中でエラーが出てしまい、最終的にはqueue size exceeds limitとなってログを破棄されてしまいました。 Amazon ES側のログが見れないので何故発生したかはこの時点ではわかりません(CloudWatch Logsで見れるようにしてほしいです)。

エラー内容でググると以下のスレッドがヒットしました。

Elasitcsearch-ruby raises “Cannot get new connection from pool” error

何かしらの原因でElasticsearchへの接続がタイムアウトした後、再びコネクションプールに接続するにはある程度の時間(@resurrect_after)まで待たなければならないようです。
@resurrect_afterのデフォルトが60秒なので接続するまでに60秒待たなければならず、頻繁にESへトラフィックを送る環境だと接続できるようになるまでにキューがどんどん詰まってしまい、queue size exceeds limitとなってしまったのが原因だということです(合っているかな・・・?)。
fluent-plugin-elasticsearch v1.2.0@resurrect_afterを設定できるようになったのでアップデートして@resurrect_afterパラメータを短くすれば解決するはずです。

もう一点の問題があって、reload_connectionsが デフォルト値(true)になっていると、Amazon ESの場合のみ問題が起こるみたいです。

The problem with reoccurring exception:
temporarily failed to flush the buffer. next_retry=2015-12-12 23:56:36 +0000 error_class="Elasticsearch::Transport::Transport::Error" error="Cannot get new connection from pool." plugin_id="output_to_elasticsearch"
can be triggered also by plugin setting "reload_connections" (default set to true) and occurs only for Amazon Elasticsearch Service

reload_connections falseを設定すれば問題を回避できるみたいです。

対応

fluent-plugin-elasticsearch を 1.2.0以上にアップデートします。
最新版が 1.2.1 なのでそのバージョンでアップデートすることにします。

root> gem install fluent-plugin-elasticsearch -v 1.2.1 --no-ri --no-rdoc
gem install fluent-plugin-elasticsearch -v 1.2.1 --no-ri --no-rdoc
Fetching: fluent-plugin-elasticsearch-1.2.1.gem (100%)
Successfully installed fluent-plugin-elasticsearch-1.2.1
1 gem installed

Fluentdの設定を以下のように変更します。

    index_name accesslog
    type_name apache
    type elasticsearch
    include_tag_key true
    tag_key @log_name
    host xxxxxx.ap-northeast-1.es.amazonaws.com
    port 443
    scheme https
    logstash_prefix apache_access
    logstash_dateformat  %Y.%m.%d-%H
    logstash_format true
    buffer_chunk_limit 100MB
    flush_interval 10s
    resurrect_after 5s
    reload_connections false
  • 追加されたresurrect_afterをデフォルト(60s)から5sに変更した
  • Amazon ESのみ起こる問題への対応としてreload_connections falseを入れた
  • buffer_chunk_limitAmazon ES(r3.xlarge)の上限(100MB)まで上げた。flush_intervalが10sだが、バッファチャンクが作られてからフラッシュするまでに100MB以上のログが溜まると、Amazon ESへ送る際にエラーになるので念のため設定した
  • 先の設定では触れなかったが、Amazon ESはVPC外にあるので、HTTPSで通信した方が安全なので、schemeportを変更することでHTTPS対応した

Fluentdを再起動して完了です。

root> service fluentd restart

最後に

Amazon ESを本格的に使っている人が少ないのか、同じようにハマっている人があまりいなかったです。