こんにちは、やまなかです。
Fluentd は、さまざまなシステムのログを集約し、分析や転送を行うことができるツールです。
Fluentd (td-agent) とは?
Fluentd (td-agent) とは、さまざまなシステムに散らばっているログデータを収集、集約して、分析を行ったり、DBなどにログデータを転送することができるツールです。Elasticsearch(検索エンジン)や kibana(データ可視化ツール)と組み合わせることでログを可視化することもできます。すべての機能がプラグインで実装されていることが特徴的で、柔軟な運用が可能です。
Fluentd は、Ruby gem で提供されています。Ruby やいくつかのプラグインが同梱されている安定版の「td-agent」としても提供されています。両者の挙動は同じため、ひとまずは違いを気にする必要はありませんが、気になる方は公式Q&Aがあるのでそちらをご覧ください。
Image Source: https://docs.fluentd.org/quickstart
ログの重要性
そもそも、ログを取ること、集約させることの重要性を理解する必要があります。エンジニアの方なら痛感しているかもしれませんが、ログはトラブルシューティングの際に大きく役立ちます。
ログの重要性はそれだけではありません。外部からの不正アクセスやウイルス感染により、組織内部からの情報漏洩などの事故が発生してしまった場合、そのことにいち早く気づき、被害状況や影響範囲の調査などの事後対応を効果的に行う必要があります。後から追跡調査を行う際にログの解析が役立ち、事故の原因究明や、事後の抜本的な対策を導き出すことにつながります。
総務省によると、 ログは別途ログ管理システムなどを設計し、そこで保管を行うことが推奨されています。そうすることで、ログの改ざんなどの不正行為からの保護だけでなく、可視化処理を行ったり、保存期間の制御なども行いやすくなります。一定期間を経過したログの保管方法として、コストや保存期間を考慮し、外部記憶媒体等に保管する運用も検討する必要があります。通常のデータのバックアップと同様に、ログのバックアップも重要になります。
Fluentdの機能
input
外部ソースからイベントログを収集します。
ログの収集や、転送データの取り込みがこれにあたります。
output
データを出力します。ファイル出力やデータ転送、DBに書き込んだりすることが出来ます。プラットフォームとしての拡張性も高く、柔軟な運用が可能です。
buffer
buffer は output で使用します。送信する前に、受信したデータを一時的に蓄積します。output では送信中にエラーが発生する可能性があります。その時、buffer は再送を行うことで欠損を防ぐ役割を持っています。
filter
イベントストリームを変更することができます。正規表現に一致する行を検索してフィルタリング、新しいフィールドを追加、プライバシーとコンプライアンスのために特定のフィールドを削除または隠すことができます。
parser
filter の正規表現では解析できない場合があります。その時にはユーザー独自の形式で解析することができます。
formatter
output の出力形式がニーズを満たさない場合があります。その時にはユーザー独自の形式で出力させることができます。
デモ
ここでは簡単に Fluentd の Docker image を触ってみたいと思います。
さらに、Fluentd では、Elasticsearch、Kibana と組み合わせることで Splunk というログを検索するツールを OSS で代替できます。Splunk はコストが高いため、使用できないプロジェクトもあります。そこで、EFK (Elasticsearch + Fluentd + Kibana) もデモを行ってみたいと思います。
動作環境
OS : WSL2 – Ubuntu 22.04.2 LTS
Docker : Docker version 24.0.2, build cb74dfc
Docker Compose : Docker Compose version v2.18.1
Docker image
Fluentd の最新バージョンの image を pull します。2023年10月4日時点ではv1.16.2-debian-1.0のDocker image を使用しています。
$ docker pull fluent/fluentd:edge-debian
conf フォルダを作成してその中に構成ファイルを作成します。
# $(pwd)/conf/fluentd.conf
<source>
@type http
port 9880
bind 0.0.0.0
</source>
<match**>
@type stdout
</match>
Fluentd を実行します。
$ docker run -p 9880:9880 -v $(pwd)/conf:/fluentd/etc fluent/fluentd:edge-debian -c /fluentd/etc/fluentd.conf
2023-10-02 02:46:08 +0000 [info]: init supervisor logger path=nil rotate_age=nil rotate_size=nil
2023-10-02 02:46:08 +0000 [info]: parsing config file is succeeded path="/fluentd/etc/fluentd.conf"
2023-10-02 02:46:09 +0000 [info]: gem 'fluentd' version '1.16.2'
2023-10-02 02:46:09 +0000 [warn]: define <match fluent.**> to capture fluentd logs in top level is deprecated. Use <label @FLUENT_LOG> instead
2023-10-02 02:46:09 +0000 [info]: using configuration file: <ROOT>
<source>
@type http
port 9880
bind "0.0.0.0"
</source>
<match **>
@type stdout
</match>
</ROOT>
2023-10-02 02:46:09 +0000 [info]: starting fluentd-1.16.2 pid=7 ruby="3.1.4"
2023-10-02 02:46:09 +0000 [info]: spawn command to main: cmdline=["/usr/local/bin/ruby", "-Eascii-8bit:ascii-8bit", "/usr/local/bundle/bin/fluentd", "-c", "/fluentd/etc/fluentd.conf", "--plugin", "/fluentd/plugins", "--under-supervisor"]
2023-10-02 02:46:09 +0000 [info]: #0 init worker0 logger path=nil rotate_age=nil rotate_size=nil
2023-10-02 02:46:09 +0000 [info]: adding match pattern="**" type="stdout"
2023-10-02 02:46:09 +0000 [info]: adding source type="http"
2023-10-02 02:46:09 +0000 [warn]: #0 define <match fluent.**> to capture fluentd logs in top level is deprecated. Use <label @FLUENT_LOG> instead
2023-10-02 02:46:09 +0000 [info]: #0 starting fluentd worker pid=16 ppid=7 worker=0
2023-10-02 02:46:09 +0000 [info]: #0 fluentd worker is now running worker=0
2023-10-02 02:46:09.396460464 +0000 fluent.info: {"pid":16,"ppid":7,"worker":0,"message":"starting fluentd worker pid=16 ppid=7 worker=0"}
2023-10-02 02:46:09.397804426 +0000 fluent.info: {"worker":0,"message":"fluentd worker is now running worker=0"}
HTTP経由でサンプルログを投稿します。
$ curl -X POST -d 'json={"json":"message"}' http://127.0.0.1:9880/sample.test
docker ps でコンテナIDを取得し、docker logs で特定のコンテナのログを確認します。
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da3ec64890df fluent/fluentd:edge-debian "tini -- /bin/entryp…" 33 seconds ago Up 32 seconds 5140/tcp, 24224/tcp, 0.0.0.0:9880->9880/tcp, :::9880->9880/tcp nervous_solomon
$ docker logs da3ec64890df | tail -n 1
2023-10-02 02:46:29.970026351 +0000 sample.test: {"json":"message"}
投稿したログを確認することができました。
これだけでは Fluentd の魅力を体感できてないと思うので、EFK のデモも行ってみます。
EFK
3 つのツール Elasticsearch + Fluentd + Kibana を組み合わせることで、柔軟で使いやすいログ収集および分析パイプラインが得られます。それぞれに以下が含まれる 4 つのコンテナを Docker Compose を使用してセットアップします
- ApacheHTTP Server
- Fluentd
- Elasticsearch
- Kibana
ディレクトリ構造は以下の通りです。
$ tree . --charset=c
.
|-- docker-compose.yml
`-- fluentd
|-- Dockerfile
`-- conf
`-- fluent.conf
それぞれのファイルの中身を書いていきます。
※2023年10月4日時点では公式チュートリアルだとバージョンの非互換が原因でエラーが発生してしまうので以下を参考にしてください。
# docker-compose.yml
version: "3"
services:
web:
container_name: web
image: httpd
ports:
- "80:80"
links:
- fluentd
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: httpd.access
fluentd:
container_name: fluentd
build: ./fluentd
volumes:
- ./fluentd/conf:/fluentd/etc
links:
- "elasticsearch"
ports:
- "24224:24224"
- "24224:24224/udp"
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2
container_name: elasticsearch
environment:
- "discovery.type=single-node"
- "xpack.security.enabled=false"
expose:
- "9200"
ports:
- "9200:9200"
kibana:
container_name: kibana
image: docker.elastic.co/kibana/kibana:8.10.2
links:
- "elasticsearch"
ports:
- "5601:5601"
Webコンテナの logging セクションでは Docker Fluentd Logging Driver をデフォルトのコンテナロギングドライバとして指定しています。Webコンテナからのログはすべて、fluentd-address で指定されたhost:port に自動的に転送されます。
Fluentd 公式 Dockerイメージを使って、以下の内容で fluentd/Dockerfile を作成し、Elasticsearch プラグインをインストールします。
# fluentd/Dockerfile
FROM fluent/fluentd:v1.12.0-debian-1.0
USER root
RUN ["gem", "install", "elasticsearch", "--no-document", "--version", "< 8"]
RUN ["gem", "install", "fluent-plugin-elasticsearch", "--no-document", "--version", "5.3.0"]
USER fluent
Fluentd の設定ファイル fluentd/conf/fluent.conf を作成します。forward input plugin は Docker logging driver からログを受け取り、elasticsearch output plugin はこれらのログを Elasticsearch に転送します。
# fluentd/conf/fluent.conf
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<label @FLUENT_LOG>
<match *.**>
@type copy
<store>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix fluentd
logstash_dateformat %Y%m%d
include_tag_key true
type_name access_log
tag_key @log_name
flush_interval 1s
</store>
<store>
@type stdout
</store>
</match>
</label>
これで準備は完了したのでコンテナを起動します。そこそこ時間がかかります。すべて起動したら4つのコンテナが稼働していることを確認します。
$ docker-compose up -d
[+] Building 0.0s (0/0)
[+] Running 5/5
✔ Network fluentd_default Created
✔ Container elasticsearch Started
✔ Container fluentd Started
✔ Container kibana Started
✔ Container web Started
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1cb41473dc7e httpd "httpd-foreground" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp web
d555d19f200e docker.elastic.co/kibana/kibana:8.10.2 "/bin/tini -- /usr/l…" About a minute ago Up About a minute 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp kibana
36e82b7e25c6 fluentd-fluentd "tini -- /bin/entryp…" About a minute ago Up About a minute 5140/tcp, 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp, :::24224->24224/tcp, :::24224->24224/udp fluentd
2102d88c4466 docker.elastic.co/elasticsearch/elasticsearch:8.10.2 "/bin/tini -- /usr/l…" About a minute ago Up About a minute 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp elasticsearch
次のコマンドを使用してアクセスログを生成します。
$ curl http://localhost:80
<html><body><h1>It works!</h1></body></html>
ここにアクセスしてログを確認してみます。
http://localhost:5601/app/management/kibana/dataViews
Kibana 用のインデックスパターン名を設定します。fluentd-* を Index pattern に指定し、Create を押します。
作成されました。
次に、左上の ≡ から Discover タブに行き、ログを確認します。
ログは Fluentd 経由でElasticsearch + Kibana に収集されていることが確認できます。
まとめ
Fluentd はマイクロサービス上でさまざまなシステムに散らばっているログデータを収集、集約して、分析を行ったり、DB などにログデータを転送することができるツールでした。デモでは実際にログの分析を行ってみました。ログを管理しやすいようにまとめ、保管しておくことはどんなプロジェクトにも必要なことだと思います。是非、Fluentd を体験してみてください。
参考文献
https://www.mdis.co.jp/service/fluentd/?via=mmenu
https://zenn.dev/ryoatsuta/articles/a0dea1dc377000
https://qiita.com/miyuki_samitani/items/87c64ca23cd7f3a709e2