
AmazonLinux1(CentOS6世代)
のlatesttd-agent3(embedded ruby2.4)
において、CloudWatch連携プラグインfluent-plugin-cloudwatch-logs
のgem依存関係がruby>=2.5を必要とするようになったため、システムのrubyランタイムを使ってfluentd
gem直でCloudWatchにサーバーログを送信するためのロードマップ。
Ruby環境
AWS Amazon EC2にrbenvでRuby環境を構築する
rbenvで2.5.1を入れてglobalをスイッチ
# rbenvバイナリをgithubからクローン
git clone https://github.com/sstephenson/rbenv.git /opt/rbenv
# ログインプロフィールにrbenvの環境設定
echo 'export RBENV_ROOT="/opt/rbenv"' > /etc/profile.d/rbenv.sh
echo 'export PATH="${RBENV_ROOT}/bin:${PATH}"' >> /etc/profile.d/rbenv.sh
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh
# ログインプロフィールをローディング
source /etc/profile.d/rbenv.sh
# ruby 2.5.1をインストールする
rbenv install -v 2.5.1
rbenv rehash
# インストールバイナリ確認
rbenv versions
* system
2.5.1 (set by /opt/rbenv/version)
# システムバージョンを2.5.1に変更
rbenv global 2.5.1
# システムバージョンを確認
rbenv versions
system
* 2.5.1 (set by /opt/rbenv/version)
fluentdパッケージをインストール
rbenvで環境を縛る
rbenv exec gem install fluentd
# yajl-rubyがrequire ruby >= 2.6.0
ERROR: Error installing fluentd:
The last version of yajl-ruby (~> 1.0) to support your Ruby & RubyGems was 1.4.1. Try installing it with `gem install yajl-ruby -v 1.4.1` and then running the current command again
yajl-ruby requires Ruby version >= 2.6.0. The current ruby version is 2.5.0.
# yajl-rubyがrequire ruby >= 2.Xなv1.4.1に落としてインストール
rbenv exec gem install yajl-ruby -v 1.4.1
rbenv exec gem install fluentd
# bundlerがrequire ruby >= 3.0.0
ERROR: Error installing fluentd:
The last version of bundler (>= 0) to support your Ruby & RubyGems was 2.3.27. Try installing it with `gem install bundler -v 2.3.27` and then running the current command again
bundler requires Ruby version >= 3.0.0. The current ruby version is 2.5.0.
# bundlerをrequire ruby >= 2.Xなv2.3.27に落としてインストール
rbenv exec gem install bundler -v 2.3.27
# 成功
rbenv exec gem install fluentd
fluentdプラグインをインストール
fluent-plugin-cloudwatch-logsプラグイン
CloudWatchに送信するためにfluent-plugin-cloudwatch-logs
プラグインをインストールする
rbenv exec gem install fluent-plugin-cloudwatch-logs
fluent-plugin-record-reformerプラグイン
ログにホストのメタ情報を添加するためにfluent-plugin-record-reformer
プラグインをインストールする
rbenv exec gem install fluent-plugin-record-reformer
fluentdのセットアップ
fluent.conf
setupコマンドでconfを作成する
fluentd --setup /etc/fluentd
> Installed /etc/fluentd/fluent.conf.
fluentdの制御
ネイティブ制御
フロント起動させて動作確認するときに
fluentdのフロント実行コマンド
fluentd -c /path/to/fluent.conf -vv &
終了は直接プロセスを落とす
pkill fluentd
init.d制御
/etc/init.d/fluentd
に下記のinit.dスクリプトを作成する
- コンフィグファイル:
/etc/fluentd/fluent.conf
- ログファイル:
/var/log/fluentd/fluentd.log
#!/bin/sh
# chkconfig: 2345 99 01
# description: Fluentd
### BEGIN INIT INFO
# Provides: fluentd
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
# USER=fluent
USER=root
# DAEMON=/usr/local/bin/fluentd
DAEMON=/opt/rbenv/shims/fluentd
DAEMON_ARGS="-c /etc/fluentd/fluent.conf -o /var/log/fluentd/fluentd.log --daemon /var/run/fluentd.pid"
PID=/var/run/fluentd.pid
case "$1" in
start)
echo -n "Starting Fluentd: "
su - $USER -c "$DAEMON $DAEMON_ARGS"
echo "done"
;;
stop)
echo -n "Stopping Fluentd: "
kill `cat $PID`
echo "done"
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
制御方法
# init.dスクリプトに実行権限あてる
chmod +x /etc/init.d/fluentd
# 起動
/etc/init.d/fluentd start
# 終了
/etc/init.d/fluentd stop
# 再起動
/etc/init.d/fluentd restart
自動起動の設定
chkconfig --add fluentd
chkconfig fluentd on
chkconfig | grep fluentd
> fluentd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
設定
/etc/fluentd/fluent.conf
apacheのカスタムアクセスログの構成例 )
- 調整にRuby実装が可能
- formatディレクティブの正規表現の検証はフォーマットデバッガでテストできる。
# sourceディレクティブ
# pathに定義したログをトラッキングしてformatに合致したとき、tagとともに、<...>のキャプチャ内容をmatchフェーズに送る
<source>
@type tail
format /^(?<xforwarded>[^ ]*) (?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")? "(?<sess>[^\"]*)"$/
time_format %d/%b/%Y:%H:%M:%S %z
path /etc/httpd/logs/access.log
pos_file /var/log/fluentd/log.httpd.access.idt.pos
# メタ情報を添加するために.reformタグをつける
tag this.tagname.reform
</source>
# matchディレクティブ
# sourceから送られてきたtag識別子をもとにmatchディレクティブのタグとスクリーニングし、それをどうするかを定義
# 1. はじめに.reformタグをスクリーニングして、ホストのメタ情報を添加して、matchフェーズに再入させる
<match **.reform>
# record-reformerプラグイン
@type record_reformer
renew_record false
enable_ruby true
# 添加後、スクリーニング再入で本来のmatchアクションディレクティブに補足させるためにreformタグをクレンジングして、tagディレクティブでmatchフェーズに再入する
# クレンジングしないと永遠に<match **.reform>にマッチして、無限ループになるので要注意
tag ${(tag_parts - ["reform"]).join(".")}
# ホスト名とタイムスタンプを添加する
<record>
hostname ${hostname}
servertime ${time.iso8601(3)}
</record>
</match>
# 2. <match **.reform>による再入後、データをアクションする
<match this.tagname.**>
# cloudwatch-logsプラグイン
@type cloudwatch_logs
region ap-northeast-1
log_group_name path.to.loggroup
# ロググループ内に{タグ名}で自動的にログストリームを作成する
auto_create_stream true
use_tag_as_stream true
</match>
sourceディレクティブに入力されるapacheアクセスログ
XXX.XXX.XXX.XXX YYY.YYY.YYY.YYY - - [23/Mar/2024:05:45:00 +0900] "GET /path/to/page HTTP/1.1" 200 138792 "https://host.example.com/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36" "abcdefg"
フォーマット後のjson
{
"xforwarded": "XXX.XXX.XXX.XXX",
"host": "YYY.YYY.YYY.YYY",
"user": "-",
"method": "GET",
"path": "/path/to/page",
"code": "200",
"size": "687368",
"referer": "https://host.example.com/",
"agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
"sess": "abcdefg",
# record_reformerで追加したメタ情報
"hostname": "fluent-host",
"servertime": "2024-03-23T05:45:03.000+09:00"
}
AWS
EC2/IAM
EC2のロールにCloudWatchLogsFullAccessポリシーを追加する
CloudWatch
ロググループpath.to.loggroup
を作成しておく
※ログストリームはauto_create_stream
オプションで自動的に作成してくれる
httpdとアプリログのスニペット
############################################
# sourceディレクティブ
############################################
# アプリケーションログ
<source>
@type tail
format /^\[(?<timestamp>.*)\]\[(?<level>[^ ]*)\] \[(?<ip>[^ ]*) - (?<session_id>[^ ]*).*\] (?<message>.*)$/
path /var/www/project/log/*.log
pos_file /var/log/fluentd/log.app.pos
tag log.app.reform
read_from_head true
</source>
# httpdアクセスログ
<source>
@type tail
format /^(?<xforwarded>[^ ]*) (?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")? "(?<sess>[^\"]*)"$/
time_format %d/%b/%Y:%H:%M:%S %z
path /etc/httpd/logs/access.log
pos_file /var/log/fluentd/log.httpd.access.pos
tag log.httpd.access.reform
</source>
# httpdエラーログ
<source>
@type tail
format /^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])? \[client (?<client>[^\]]*)\] (?<message>.*)$/
path /etc/httpd/logs/error.log
pos_file /var/log/fluentd/log.httpd.error.pos
tag log.httpd.error.reform
</source>
# auditlog
<source>
@type tail
format none
path /var/log/audit/audit.log
pos_file /var/log/fluentd/log.audit.pos
tag log.audit.reform
</source>
# securelog
<source>
@type tail
format syslog
path /var/log/secure
pos_file /var/log/fluentd/log.secure.pos
tag log.secure.reform
</source>
# ホストのメタ情報を追加してmatchフェーズに再入させる
<match **.reform>
@type record_reformer
renew_record false
enable_ruby true
tag ${(tag_parts - ["reform"]).join(".")}
<record>
hostname ${hostname}
servertime ${time.iso8601(3)}
</record>
</match>
# CloudWatchLogsのlog.appロググループに送信
<match log.app.**>
@type cloudwatch_logs
region ap-northeast-1
log_group_name log.app
auto_create_stream true
use_tag_as_stream true
</match>
# CloudWatchLogsのlog.httpd.accessロググループに送信
<match log.httpd.access.**>
@type cloudwatch_logs
region ap-northeast-1
log_group_name log.httpd.access
auto_create_stream true
use_tag_as_stream true
</match>
# CloudWatchLogsのlog.httpd.errorロググループに送信
<match log.httpd.error.**>
@type cloudwatch_logs
region ap-northeast-1
log_group_name log.httpd.error
auto_create_stream true
use_tag_as_stream true
</match>
# CloudWatchLogsのlog.auditロググループに送信
<match log.audit.**>
@type cloudwatch_logs
region ap-northeast-1
log_group_name log.audit
auto_create_stream true
use_tag_as_stream true
</match>
# CloudWatchLogsのlog.secureロググループに送信
<match log.secure.**>
@type cloudwatch_logs
region ap-northeast-1
log_group_name log.secure
auto_create_stream true
use_tag_as_stream true
</match>