絶品ゆどうふのタレ

ふと気づいたことを綴るだけのメモ

fluent-plugin-elasticsearch(elasticsearch-ruby)で負荷分散してる場合のリトライ設定

結論的にはマニュアル以上の情報はないです。個人的メモに近い感じ。

この辺は気軽にテスト/確認しづらいレイヤだと思ってるので、具体的な実装を知らないと怖かったので調べてみました。

背景

  • fluentdからelasticsearchクラスタにログを投入したい
  • サービスから参照されるのでクラスタの一部が死んでもサービスに影響がないようにしたい

世にある記事だと、elasticsearchサーバのローカルにfluentd receiverを置いて、そこからesに投入する形式が多く見られた。

が、それだとreceiverが生きててesプロセスだけが死んだ場合にデータの投入が部分的に止まるよねってことで、送信元のfluentdからesに直接接続して投入することにした。

その場合fluent-plugin-elasticsearchが分散処理を行うので、その部分のfailure/retryの扱いやパラメータを調査した。

ポイント

先に、設定項目とかのまとめだけ。

  • fluent-plugin-elasticsearchは内部的にelasticsearch-ruby(elasticsearch-transport)を利用していて、そこが接続管理をしている。

  • 設定を検討した方がいいelatissearch-rubyの項目は以下

    • reload_connections
      • コネクション先サーバリストの更新可否と、その閾値trueでdefault 10000リクエスト毎。
      • とりあえずtrueで。クラスタの構成変更の頻度次第で増減させるといいかも。
    • reload_on_failure
      • 接続失敗した場合にサーバリストを更新するか否か。
      • これはtrueにするのが良さそうだが、サービスの監視体制等次第かも。正常にクラスタから外れないまま壊れたサーバが出るとどうなるだろう。。。
      • reload_connectionsの長さとのバランスにもよる。
    • retry_on_failure
      • サーバがunreachableだった場合のretry可否と試行回数。true指定だとdefaultの3が使われる。
      • fluent-plugin-elasticsearchでは5で固定。まぁそんもん感。
    • retry_on_status
      • サーバエラーが返ってきた時にretryするかどうか。
      • クラスタのうちの1台だけが異常になった場合とか想定すると、trueしたい。
        • ...だが、これはfluent-plugin-elasticsearchでは指定できない。
  • 環境次第だがWARNのログも閾値設けて監視の対象とした方が安全そう

    • ここを見ないとLANポート・ケーブル故障とかネットワーク異常系の場合に気づきにくそう
  • reload_connectionsをしていれば無停止でクラスタ構成変更に対応できるが、confの書き換えは忘れず行う必要があるので手順漏れがないよう注意

  • 以下項目は、関連するがdefaultのままでもたぶん支障はない

    • resurrect_after
      • default 60
    • resurrect_timeout
      • fluent-plugin-elasticsearchにはない
    • sniffer_timeout
      • fluent-plugin-elasticsearchにはない

以下詳細

まぁほぼソースコードの解説なので。。。

アクセスエラー処理の流れ

対象となるソースコードこのあたりにまとまっている。 とくにBase#perform_requestが中心。

  • 指定された選択方式(RRとか)で接続先サーバを決め、リクエストを送る
  • retry対応が走るのは以下のパターンだけで、それ以外は問答無用で死ぬ

unreachableだった場合(Timeout / ConnectionFailed)

  • 対象サーバは死亡判定を受ける
    • reload_on_failureパラメータがあれば、接続不能なサーバにあたった時点で即座にサーバリストのリロードが走る
    • retry_on_failureパラメータが設定されていると、改めてretry
      • max回数を超えるまでは失敗してもWARNしか吐かないので、WARNログをチェックしたほうが良さそう
      • [#{e.class}] Attempt #{tries} connecting to #{connection.host.inspect}

ServerErrorだった場合

  • retry_on_statusパラメータがあるなら、無条件でretry
    • エラーを返したサーバに接続ペナルティはない(dead判定もfailure判定もされない)ので、調子悪いサーバが対象から外れることはない。
    • WARNで出る [#{e.class}] Attempt #{tries} to get response from #{url}のログをちゃんと監視しておくと良いのかもしれない。
    • max失敗するとFatal。default回数は3。retry_on_failureパラメータに数字を渡すと変えられる。
  • パラメータがないならそのまま失敗する!!

なお、fluent-plugin-elasticsearchにはretry_on_statusがないので、この処理は通らない。

死亡判定と復帰判定

サーバの死亡判定はunreachable時、復帰の判定は接続先サーバの選定時に走る。

死亡判定

unreachableになると一旦死亡判定される

  • 死亡判定をされると、メモリ上に死亡フラグと失敗回数インクリメント、死亡時刻が記録される
    • reload_connectionsパラメータがセットされてると、指定リクエスト回数毎にサーバリストのリロードが走る
  • メモリがクリアされると死亡判定は消えちゃうよ
  • 死亡判定されると一旦接続対象から外されて、復帰判定を待つ

復帰判定

unreachableになったサーバにも再度接続のチャンスを与えるために、一定時間経過の後で復帰判定が行われる

  • resurrect_afterパラメータが関与
  • 最後のアクセスからresurrect_after秒後に復活チェック(default: 60sec)
  • 死亡判定を受けたConnectionが、死亡時刻からresurrect_timeout x (2 ** 失敗回数-1) 経っていたら復帰させる

復帰のあと、接続に成功した場合

復帰後にちゃんと接続に成功したら、正常に戻ったと判定されて失敗回数がクリアされる

対象サーバリストのリロード

  • 実際にクラスタにnode情報を取りに行って、対象サーバのリストを更新する
    • その際のtimeoutは別途sniffer_timeoutで決められる(default: 1)
  • この機能を使うことで、設定ファイルのhostsリストを編集・リロードすることなく、クラスタ群の追加削除に対応できる
  • ただし情報はメモリ上にロードされるだけなので、設定ファイルの更新は併せて行う必要がある

だいたいこんな感じ。そんじゃーね。