EC2上のコンテナから直接CloudWatchLogsにログを出力する方法
前回の記事で構築した環境で、ディスク容量が発生した際の回避策としてログを直接CloudWatchLogsに出力するようにしたのでその備忘録です。
まず、EC2上のコンテナからCloudWatchLogsにログを転送する場合、以下の手段があります。
- Docker の logging driverを使う
- Fluent Bitを使う
- EC2 に出力してCloudWatch Agentで送る
今回のケースの場合、3はディスク容量を逼迫してしまうので選択肢から除外しました。
また、1についてはAmazonLinux2023のDockerではawslogs ドライバが削除されているため除外となり、必然的に手段2で対応しました。
1.アプリ側の標準出力化
まず、webサーバやLaravelのログをファイルに吐かずに標準出力に変更します。
1-1 Apacheの設定
#httpd.conf(VirtualHostを設定している場合はそのconf)に以下を記述
ErrorLog /dev/stderr
CustomLog /dev/stdout combined
1-2 Laravelの設定
#.envに以下を記述
LOG_CHANNEL=stderr
2.Docker のログローテーション
Docker の既定ログドライバ json-file はローテーション無効=無限増加です。AMI 作成時点で制限を入れておきましょう。
#/etc/docker/daemon.jsonを作成して以下を記述
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
#保存したら、以下のコマンドで反映
sudo systemctl restart docker
3.Fluent Bit の導入
次に、fluent-bitの設定を追加します。
3.1 ディレクトリ構成(例)
/home/ec2-user/myrepo/
├─ docker-compose.yml
└─ fluent-bit/
├─ fluent-bit.conf
└─ parsers.conf
3.2 parsers.confの作成
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
Time_Keep On
3.3 fluent-bit.confの作成
以下はfluent-bit.confの構成例です。
各環境に合わせてpathなどを調整してください。
[SERVICE]
Flush 1
Daemon Off
Log_Level info
Parsers_File /fluent-bit/etc/parsers.conf
# ================================================================
# 入力:Docker の json-file
# ================================================================
[INPUT]
Name tail
Path /var/lib/docker/containers/*/*-json.log
Tag raw.docker
Parser docker
Docker_Mode On
Docker_Mode_Parser docker
Mem_Buf_Limit 200MB
Skip_Long_Lines On
Refresh_Interval 1
Read_from_Head Off
# ================================================================
# 0) $log を JSON として解釈
# ================================================================
[FILTER]
Name parser
Match raw.docker
Key_Name log
Parser json
Reserve_Data On
Preserve_Key On
# ================================================================
# 1) Laravel 判定とタグ付与(どちらかがヒットすれば OK)
# ================================================================
# channel が文字列なら Laravel
[FILTER]
Name rewrite_tag
Match raw.docker
Rule $channel ^[A-Za-z0-9._-]+$ laravel.app true
# level_name が Monolog レベル名なら Laravel
[FILTER]
Name rewrite_tag
Match raw.docker
Rule $level_name ^(DEBUG|INFO|NOTICE|WARNING|ERROR|CRITICAL|ALERT|EMERGENCY)$ laravel.app true
# ================================================================
# 2) 残りの raw.docker は stdout/stderr で Apache に振り分け
# ================================================================
[FILTER]
Name rewrite_tag
Match raw.docker
Rule $stream ^stdout$ apache.access true
[FILTER]
Name rewrite_tag
Match raw.docker
Rule $stream ^stderr$ apache.stderr true
# ================================================================
# 3) stderr から Laravel を除外して Apache エラーへ
# ================================================================
[FILTER]
Name grep
Match apache.stderr
Exclude channel ^[A-Za-z0-9._-]+$
[FILTER]
Name rewrite_tag
Match apache.stderr
Rule $log .* apache.error true
# ================================================================
# 4) 出力:CloudWatch Logs
# - Laravel は JSON で出力
# - Apache はテキストのまま
# ================================================================
[OUTPUT]
Name cloudwatch_logs
Match apache.access
region ap-northeast-1
log_group_name /myrepo/apache
log_stream_prefix web-
auto_create_group true
[OUTPUT]
Name cloudwatch_logs
Match apache.error
region ap-northeast-1
log_group_name /myrepo/apache
log_stream_prefix web-
auto_create_group true
[OUTPUT]
Name cloudwatch_logs
Match laravel.app
region ap-northeast-1
log_group_name /myrepo/laravel
log_stream_prefix app-
auto_create_group true
log_format json
3.4 docker-compose.ymlにコンテナを追加
#既存のymlに以下を追加
fluent-bit:
image: public.ecr.aws/aws-observability/aws-for-fluent-bit:2.31.9
container_name: px-crm-fluent-bit
restart: always
user: root
volumes:
- /var/lib/containers:/var/lib/containers:ro,Z
- ./fluent-bit/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro
- ./fluent-bit/parsers.conf:/fluent-bit/etc/parsers.conf:ro
environment:
- AWS_REGION=ap-northeast-1
command: ["/fluent-bit/bin/fluent-bit", "-c", "/fluent-bit/etc/fluent-bit.conf"]
ここまでの設定を行ってコンテナを実行すれば、pathなどに問題が無い限りはCloudWatchLogsに出力されていると思います。
もし出力されない場合は、EC2のロールを確認するのと、fluent-bit.confのFILTERの内容と実際のログを比較して確認して見てください。