every Tech Blog

株式会社エブリーのTech Blogです。

fluentd/fluent-bitでTreasure Dataにログを送信するときにハマった話

はじめに

DelishKitchenヘルシカでインフラをやったりバックエンドをやったりしているyoshikenです。

今回は、Treasure Dataにログを送信しようとfluentdとfluent-bitを使っていたときにハマった話を書きます。

fluentdからfluent-bitへ

もともと弊社では歴史的背景でfluentdを使っていました。が、

  1. 大々的なlogの加工が必要なものはTreasure Dataなど別サービスで行う
  2. リソースの消費がやや気になる
  3. FireLensはじめ、Fargateでfluentbitのほうが相性が良い

などの理由より、新規サービスはすべてfluent-bitを使用することになりました。

移行から数ヶ月~数年経っていますが、flunetdに比べ軽量であるため期待した通りのパフォーマンスを発揮してくれています。

fluent-bitでTreasure Dataにログを送信できない現象

ヘルシカではアクセスログをTreasure Dataに送信する要件がありましたので、fluent-bitでTreasure Dataにログを送信する設定を行いました。

confを公式ドキュメント通りに記述しましたが、ログが送信されず、logを漁ってみると以下のようなエラーが出ていました。

[202x/xx/xx xx:xx:xx] [ warn] [output:td:td.0] HTTP status 404
{"status_code":404,"message":"Resource not found","severity":"error","error":"Resource not found","text":"Resource not found"}

同じような設定でflunetdを動かしてみると問題なく送信/挿入できたので、fluentbit固有の問題と考えdebugしていきます。

fluent-bitとfluentdではTreasure Dataプラグインの挙動が違う件

結論からいうと、fluent-bitのTreasure Dataプラグインはfluentdの同名のプラグインと挙動が微妙に異なります。

fluentdではテーブルが存在しない場合、正確に記すと「upload時に 404 not found httpステータスコードが帰ってきた場合」はテーブルを作成する処理を行います

https://github.com/treasure-data/fluent-plugin-td/blob/master/lib/fluent/plugin/out_tdlog.rb#L209-L224

      begin
        begin
          @client.import(database, table, UPLOAD_EXT, io, size, unique_str)
        rescue TreasureData::NotFoundError
          unless @auto_create_table
            raise
          end
          ensure_database_and_table(database, table)
          io.pos = 0
          retry
        end

対してfluent-bitでは、テーブルが存在しない場合でも特に追加処理などせずにそのままエラーを返却する形になっています

https://github.com/fluent/fluent-bit/blob/master/plugins/out_td/td.c#L188-L207

/* Validate HTTP status */
    if (ret == 0) {
        /* We expect a HTTP 200 OK */
        if (c->resp.status != 200) {
            if (c->resp.payload_size > 0) {
                flb_plg_warn(ctx->ins, "HTTP status %i\n%s",
                             c->resp.status, c->resp.payload);
            }
            else {
                flb_plg_warn(ctx->ins, "HTTP status %i", c->resp.status);
            }
            goto retry;
        }
        else {
            flb_plg_info(ctx->ins, "HTTP status 200 OK");
        }
    }
    else {
        flb_plg_error(ctx->ins, "http_do=%i", ret);
        goto retry;
    }

理由ついてはissueなどを漁ってみましたが、特に言及はなかったです。 一応歴史的にはfluentdも昔はflunet-bit同様にエラーをそのままエラーで返していたましたが、途中でリトライ処理が追加された形になります。

Prevent retrying unretriable errors by cyberdelia · Pull Request #35 · treasure-data/fluent-plugin-td

まとめ

まとめると以下の表になります。

fluentd fluent-bit
テーブルが存在する 送信可能 送信可能
テーブルが存在しない 自動生成 404 not found

弊チームではデータチームと話し合い、"エラーが出続けるのは健全ではない"・"fluentdと同じ仕様と勘違いし、Treasure Data側のテーブル作成を忘れてしまう"などの懸念が生じ、即座のリアルタイム性が必要なログではないため、"一度S3にoutput。その後、Treasure Dataのbatch importで挿入する。"という形で対応することとなりました。

同じようなプラグインでも挙動が異なるというレアケースを引いてしまったため、後世に同じような人がハマらないように記事に残しておきます。