8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

株式会社うるる(ULURU)Advent Calendar 2023

Day 15

Fargate のログをFireLens(fluentbit)経由で BigQuery に連携する話

Posted at

この記事は、株式会社うるる(ULURU) Advent Calendar 2023 の15日目の記事です。

サービスは AWS で動いているものの、各種ログや、それらを基にしたダッシュボードは Google の BigQuery で参照・集計したい、という需要はそれなりにあるように思います。

今年、うるるの入札情報サービス NJSS のバックエンドでも、上記の仕組みを導入しましたので、構成や設定について簡単に書き残してみます。

構成図

firelens.png

FireLens について

最初は、「FireLens」というのが何を指す言葉なのかよくわかっていませんでした。公式ブログにある「シンタックスシュガー」という表現が一番しっくりくるように思います。

awsfirelens ログドライバーは、タスク定義用のシンタックスシュガーであり、Fluentd または FluentBit のアウトプットプラグインの設定を指定できます。

内部では、以下のようなことをやっているのだと思います。

  • fluentbit(or fluend)のコンテナを起動して24224ポートで待ち受け
  • 出力元コンテナは(おそらく)log driverにfluentdを指定したような動作になる

一瞬、単に「fargateでもlogdriverにfluentdが選べるようになりました」じゃダメなのかな?とも思ったのですが、driverにfluentdを選んだらそれを受け取るfluentbit(かfluentd)は絶対に必要になるし、その場合はINPUTの定義は固定でよくて、FILTERとOUPUTが制御できればOKなので、その辺をひとかたまりにしてAWS側が用意してくれた仕組みがFireLens、という理解です。

タスク定義

こちら を参考に作成しました。

image

FireLensの基本的な使い方としてよく紹介されているのは、以下のようなパターンです。

  • AWSの提供するイメージをそのまま使う
  • ユーザ側で個別に設定したいものがある場合は、S3に配置してタスク定義で config-file-value で指定する

ただ、この方法だと、以下2点の対応ができなかったため、今回は公式イメージを基に自前でdocker imageを buildしてECRにpushしたものを使いました。

  • Mem_Buf_Limit の設定
  • GCPの認証を通すための環境変数の引き渡し

それぞれ、詳細は後述します。

name (コンテナ名)

これは log_router という名前にしないとうまく動きませんでした。
(最初、適当な名前をつけてデプロイしたらエラーになってしまいました)

fluentbit 設定

INPUT

過去にfluendを運用していたことたあるのですが、その際「変なログを投げつけられてfluendで処理が詰まってしまい、fluendがメモリを食い潰してしまう」といったトラブルに悩まされたことがあり、「fluentbitでもMem_Buf_Limit参考)の設定を入れたい!」という思いがありました。

ただ、この設定はINPUTセクションにしか記述できません。そして、AWSの公開イメージをそのまま使ってユーザ定義をconfig-file-valueで指定する場合は、INPUTセクションは編集できない(ユーザ定義でINPUTセクションを記述しても無視される)ため、INPUT含めて自分で記述したconfigを使っています。

これが、fluentbitコンテナは自前でbuildしたものを使っている理由の1つです。

なお、ユーザ定義ファイルを指定した場合に、firelensとして起動したfluentbitコンテナ(log_routerコンテナ)の中で設定ファイルがどうなっているかは、以下の記事が参考になります。

ローカルとAWS上でコンテナ内部から見た /fluent-bit/etc/fluent-bit.conf の中身が変わっている理由が最初わからなくて戸惑ったのですが、実際動かしてdfで見たら/fluent-bit/etc/fluent-bit.confに外部ボリュームか何かがマウントされていました。

ログの分類

今回は、fluentbitにログを渡す経路は以下2種類になります。

  • アプリケーションからfluentドライバを使って直接24224ポートに出力
  • 標準出力/標準エラー出力が logdriver 経由で出力

前者のパターンでは、出力時に任意のタグをセットすることができます。
一方、標準出力/標準エラー出力の場合は、内容に応じてタグを付与するといったことは(自分の知る限り)できません。
ただ、例えばnginxのログであれば、普通のリクエストログと起動時のログは分けて扱いたく、ただしそれらは全部標準出力として出力され、同じコンテナから出力されたものは全部同じタグがついてfluentbitに到達するので、そこをどうやって区分けしようかな、ということで、今回は以下のように対応しました。

nginx設定(抜粋)
# nginx設定
log_format main 'NGINX_ACCESS_LOG $msec $remote_addr $request_method $request_uri $status $body_bytes_sent $http_referer $http_user_agent $request_time $http_x_forwarded_for';
fluenbit設定(抜粋)
[FILTER]
    Name rewrite_tag
    Match *-nginx
    Rule $log (NGINX_ACCESS_LOG) nginx-access-log false
  • log_formatで固定の文字列を埋め込み
  • fluentbitのrewrite_tagで、$log本文と上で埋め込んだ文字列でマッチング
  • hitしたらnginx-access-logのタグを付与
  • 以降はこのタグとのMatchでハンドリング

このあたりは、もしかしたらもう少しうまいやり方があるのかもしれません。

Parse

時刻はTIMESTAMP型、request_timeはNUMERIC型でテーブルを作りたいですが、テーブルと一致する型でないとレコードが登録されません。
Type ConverterLuaのFilter Pluginで変換するといった方法もありそうですが、今回はParserでTypesを指定して対応しました。

上で nginx-access-logタグを付与したログにParser Filterを適用。

[FILTER]
    Name parser
    Match nginx-access-log
    Key_Name log
    Parser nginx_access_log

対応するparserの定義は以下の通り。

[PARSER]
    Name nginx_access_log
    Format regex
    Regex ^[^ ]* (?<timestamp>[^ ]*) (?<remote_addr>[^ ]*) (?<method>[^ ]*) (?<path>[^ ]*) (?<status>[^ ]*) (?<body_bytes>[^ ]*) (?<referer>[^ ]*) (?<ua>[^\"]*) (?<request_time>[^ ]*) (?<x_forwarded_for>[^\"]*)
    Time_Key timestamp
    Types status:integer body_bytes:integer request_time:float

Output

Bigquery output pluginを使用します。

ここで一つ困ったのが、GoogleのCredentialの設定でした。
AWSのパラメータストアに格納して読み込ませようとしたのですが、構築時点(この記事執筆時点でも)では、$GOOGLE_SERVICE_CREDENTIALSには「credntialの中身」ではなく「credentialファイルのパス」を指定する必要があります。
イメージの中にcrednetialファイルを埋め込むのは避けたいので、ちょっと悩んだ結果、entrypoint.shfluentbitプロセス起動前に以下を追加して対応しました。

  • パラメータストアの中身をファイルに出力
  • ファイルパスを環境変数にセット
echo ${GCP_CREDENTIAL_VALUE} > /fluent-bit/etc/gcp.json
export GOOGLE_SERVICE_CREDENTIALS=/fluent-bit/etc/gcp.json

GCP_CREDENTIAL_VALUEはタスク定義のsecretsでパラメータストアと紐付け)

これが、Buildし直したイメージを使う理由の2つ目です。

おわりに

もう少し楽に書けるかと思ったのですが、色々思い出したり確認し直したりで意外と時間がかかってしまいました。
誰かのお役に立てば幸いです。

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?