(O+P)ut

アウトプット



(O+P)ut

エンジニアのアウトプット

【AWS/ECS】Firelensで指定したディレクトリのファイルをCloudWatchLogsに連携する

スポンサーリンク

はじめに

ECSで稼働するコンテナは、awslogsというログドライバーを有効化することで標準出力がCloud Watch Logsに連携されます。
簡易的な使い方であればこれでいいものの、より細かい用件でログルーティングを行う場合は不十分であり、代わりにFirelensコンテナを利用した構成を取る必要があります。

本記事ではコンテナ内のディレクトリを指定し、そのディレクトリ配下のログをファイル毎にログストリームを分けて出力する方法について解説します。

環境情報
  • ECS Fargate 1.4
  • aws-cli/2.15.5
  • Fluent Bit v1.9.10

実装の流れ

FirelensとはOSSのFluentdをベースにした製品であり、ログを収集したいコンテナと同一タスクで動かす必要があります。

そのため、タスク定義(Task Definition)にてFirelensの情報を記載する必要があります。また、記載する際のポイントとしてログボリュームの共有が必要です。

要はFirelensコンテナがログを出力するコンテナのログにアクセスできるようにします。今回はApacheのログを想定して以下のディレクトリを指定しました。

"mountPoints": [
                {
                    "sourceVolume": "log_directory",
                    "containerPath": "/var/log/httpd"
                }
            ]

共有ボリュームからFirelensがCloudWatchに連携する

Firelensのコンテナイメージの作成

設定用ファイルを用意し、それをコンテナイメージの中に配置します。INPUTにてPathの場所を正規表現のアスタリスクで抽出し、そのパス名を変数にしてOUTPUTで渡しています。

[INPUT]
    Name tail
    Tag tail.app_logs.*
    Path /var/log/httpd/*
    Path_Key application_log_file_path
    Read_from_Head true
    
[OUTPUT]
    Name cloudwatch_logs
    Match tail.app_logs.*
    Region ap-northeast-1
    log_group_name xx
    log_stream_prefix xx
    auto_create_group true
    log_group_template /xx/xx
    log_stream_template $application_log_file_path

タスク定義

今回の設定に関連する箇所を抜粋しながら解説します。

{
    "taskDefinitionArn": "xx",
    "containerDefinitions": [
        {
            "name": "apache",
            "image": "xx.ecr.ap-northeast-1.amazonaws.com/xx",...
            "mountPoints": [
                {
                    "sourceVolume": "log_directory",
                    "containerPath": "/var/log/httpd"
                }
            ],
            "volumesFrom": [],
            "logConfiguration": {
                "logDriver": "awsfirelens",
                "options": {
                    "region": "ap-northeast-1",
                    "auto_create_group": "true",
                    "Name": "cloudwatch"
                }
            },

mountPointsでFirelens側に見せたいパスを指定し、logConfigurationのlogDriverにて"awsfirelens"を設定します。

続いてFirelensのコンテナ側。

        {
            "name": "log_router",
            "image": "xx.ecr.ap-northeast-1.amazonaws.com/xx",
...
            "mountPoints": [
                {
                    "sourceVolume": "log_directory",
                    "containerPath": "/var/log/httpd"
                }
            ],...
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "log_router_group",
                    "awslogs-create-group": "true",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-stream-prefix": "test"
                }
            },
..
            "firelensConfiguration": {
                "type": "fluentbit",
                "options": {
                    "config-file-type": "file",
                    "config-file-value": "/fluent-bit/etc/extra.conf"
                }
...
    "volumes": [
        {
            "name": "log_directory",
            "host": {}
        }
    ],
...

mountPointsは同じパスを指定し、firelensConfigurationにてコンテナイメージ内に配置した追加設定ファイルのパスを設定します。
また、全体の設定箇所にてvolumesに関する記述を行います。

動作確認

この状態でタスクを起動すると、Firelens側のログにとしてログストリームが作成されるメッセージが出力されます。

[ info] [output:cloudwatch_logs:cloudwatch_logs.0] Creating log stream /var/log/httpd/access_log in log group xx log_router

ここでCloudWatchLogsのLog streamsを確認すると以下のようなログストリームが確認でき

/var/log/httpd/access_log
/var/log/httpd/error_log

中を見るとJSON形式でログが連携されていました。

{
    "application_log_file_path": "/var/log/httpd/access_log",
    "log": "xx - - [... +0000] \"GET /test/ HTTP/1.1\" 200 14 \"-\" \"ELB-HealthChecker/2.0\"",
    "ecs_cluster": "xx",
    "ecs_task_arn": "arn:aws:ecs:ap-northeast-1:xx:task/xx/xx",
    "ecs_task_definition": "xx:xx"
}

追加検証

Apacheコンテナの中に入って設定しているディレクトリ配下を確認すると以下のようになっています。

# ls
access_log  error_log
sh-4.2# ls -ltr
total 32
-rw-r--r-- 1 root root   700 xx error_log
-rw-r--r-- 1 root root 24360 xx access_log

ここで手動でファイルを作成し、書き込みを行いました。

sh-4.2# touch test_log
sh-4.2# echo HelloWorld >> test_log
sh-4.2# ls -ltr
total 40
-rw-r--r-- 1 root root   700 xx error_log
-rw-r--r-- 1 root root    11 xx test_log
-rw-r--r-- 1 root root 27720 xx access_log

するとFirelens側のログに"inotify_fs_add()"と出力され

[ info] [input:tail:tail.3] inotify_fs_add(): inode=561837 watch_fd=3 name=/var/log/httpd/test_log
[ info] [output:cloudwatch_logs:cloudwatch_logs.0] Creating log stream /var/log/httpd/test_log in log group ...
[ info] [output:cloudwatch_logs:cloudwatch_logs.0] Created log stream /var/log/httpd/test_log

CloudWatchLogs側に出力されました。

{
    "application_log_file_path": "/var/log/httpd/test_log",
    "log": "HelloWorld",
    "ecs_cluster": "xx",
    "ecs_task_arn": "xx",
    "ecs_task_definition": "xx"
}

終わりに

ディレクトリ配下のログを出力できるため、ログ出力のために標準出力に変更する必要がない点は本方式のメリットとなります。ただし、ログ自体はコンテナの中で肥大化する可能性があるので、定期的なメンテナンスが必要な点はご注意ください。

以上、ご参考になれば幸いです。