背景
個人管理のサーバでホームページを公開していて、各ページへのアクセス情報を見てみたくなった。ログ収集の試行もかねてアクセスログを可視化してみた。
環境構築の方針
- Docker, Docker-Composeを使って構築する。
- 個人的な経験値から以下のミドルウェアを利用する。
- nginx:Webサーバ
- fluentd:nginxのアクセスログを収集
- mysql:収集したログ情報の格納
- metabase:DBに格納したログ情報の可視化
構成
nginxが出力したログファイルを、fluentdで収集してmysqlに格納する。
構成図
前提環境
- OS : Ubunt 20.04.4 LTS
- Docker : 20.10.17, build 100c701
- Docker compose : version 1.29.1, build c34c88b2
構築
以下のステップで構築する。
1. ディレクトリ構成の作成
2. docker-composeファイルの作成
3. mysqlコンテナの構築
4. fluentdコンテナの構築
5. Metabaseコンテナの構築
1. ディレクトリの作成
以下のディレクトリ構成を作成した。
個別コンテナの設定ファイルなどについては後述する。
/home/com/homepage
├─ nginx
│ ├─ conf
│ │ └─ nginx.conf
│ └─ log
│ └─ access.log ・・・nginxのアクセスログファイル
├─ contents ・・・webページのコンテンツ
│ └─ index.html
├─ fluented
│ ├─ data
│ │ └─ nginx_access.log.pos ・・・最終読み込み位置の保持ファイル
│ ├─ Dockerfile
│ └─ fluent.conf ・・・Fluentdの設定ファイル
├─ mysql
│ ├─ scripts
│ │ └─ create_tabel.sql ・・・テーブル作成のSQLファイル
│ └─ dbvolume
├─ metabase
└─ docker-compose.yaml
2. docker-composeファイルの作成
以下の通り、docker-composeファイルを作成した。
[ 特記事項 ]
- fluentdのコンテナからnginxのログファイルを参照する。
version: '3'
services:
nginx:
image: nginx:1.23
ports:
- 80:80
volumes:
- ./contents:/usr/share/nginx/html
- ./nginx/log:/var/log/nginx
- ./nginx/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf
environment:
TZ: Asia/Tokyo
restart: always
metabase:
image: metabase/metabase:v0.46.2
restart: always
volumes:
- ./metabase:/metabase-data
ports:
- 3000:3000
environment:
MB_DB_FILE: /metabase-data/metabase.db
fluentd:
build:
context: .
dockerfile: ./fluentd/Dockerfile
ports:
- 24224:24224
volumes:
- ./nginx/log:/var/log/nginx
- ./fluentd/fluent.conf:/fluentd/etc/fluent.conf
- ./fluentd/data:/fluentd/log
db:
image: mysql:8.0
restart: always
volumes:
- ./mysql/dbvolume:/var/lib/mysql
- ./mysql/scripts:/docker-entrypoint-initdb.d
ports:
- 3306:3306
environment:
- MYSQL_DATABASE=log
- MYSQL_USER=log
- MYSQL_PASSWORD=password
- MYSQL_ROOT_PASSWORD=passwordpassword
- TZ=Asia/Tokyo
3. mysqlコンテナの構築
以下のステップを実施した。
1. テーブル定義ファイルの作成
2. コンテナ起動時のテーブル作成自動化
3-1. テーブル定義ファイルの作成
Fluendの公式ページを参照して、nginxのアクセスログを解析した際の出力要素を確認した。
それらの項目をカラムとして持つテーブルを作成するためのSQL文をファイルに出力した。
CREATE TABLE `nginx_access_log` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`time_jst` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`remote_ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`host_ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`user_id` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`method` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`access_path` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`size` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`referer` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`agent` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`http_code` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL,
`xforwarded` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
参考
Fluentd nginx parser plugin
3-2. コンテナ起動時のテーブル作成自動化
Fluentdのコンテナ起動時にテーブルが無いと、Fluentdでエラーになる。そのため、テーブルが存在しない場合には、自動的にテーブルを作成するようにした。
docker-entrypoint-initdb.dにスクリプトファイルを格納しておくと、コンテナ起動時に自動的に実行されるので、3-1で作成したスクリプトをコンテナにマウントする。
db:
image: mysql:8.0
restart: always
volumes:
- ./mysql/dbvolume:/var/lib/mysql
- ./mysql/scripts:/docker-entrypoint-initdb.d←これを追加
参考
【Docker】【MySQL】コンテナ初回起動時にスクリプトを実行させる
4. fluentdコンテナの構築
以下のステップを実施した。
1. MySQLプラグインのインストール
2. ログ収集の設定
4-1. MySQLプラグインのインストール
fluentdで読み込んだnginxのログをmysqlにbulk_insert1するために、td-agentでmysqlのプラグインを追加しておく必要がある。
fluentdのデフォルトのコンテナイメージには、mysqlのプラグインが含まれていない。コンテナ立ち上げ時にインストールしておくようにDockerfileを用意してbuildする。
[ 特記事項 ]
- インストールするプラグインは、fluent-plugin-mysql
- ファイルサイズが大きくならないように--no-documentを指定
- ユーザflunetには、インストール権限が無いため、rootに切り替える
- コンテナのタイムゾーンをJSTに変更
FROM fluent/fluentd:v1.16-1
USER root
RUN apk add --no-cache --virtual .build-deps \
build-base \
ruby-dev \
tzdata && \
cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
echo "Asia/Tokyo" > /etc/timezone && \
apk add --no-cache \
mariadb-dev && \
gem install fluent-plugin-mysql --no-document && \
apk del .build-deps
USER fluent
参考
fluent/fluentd - Docker Image
【Docker環境】FluentdとMySQLのbulk insertを使用したログ収集【前編】
4-2. ログ収集の設定
nginxのログを読み込んで、mysqlサーバに登録するために設定ファイルを作成する。
<source>
@type tail
format nginx
path /var/log/nginx/access.log
pos_file /fluentd/data/nginx_access.log.pos
tag nginx.access
</source>
<match nginx.access>
@type mysql_bulk
host 192.168.2.10
port 3307
database log
username log
password password
column_names id,time_jst,remote_ip,host_ip,user_id,method,access_path,http_code,size,referer,agent,xforwarded
key_names id,host,user,${time},method,path,version,code,size,referer,agent,xforwarded
table nginx_access_log
flush_interval 10s
</match>
参考
fluentdのnginxアクセスログ取得をdockerで試す
5. metabaseによる可視化
ダッシュボードの作成
「分析」機能-「要約」で、集約方法:カウント、集約キー:time_jst(週)を選択
ひとまず、以上で可視化までのパスを通すことができました。
ダッシュボードの拡張は今後のタスクにして、今回はここまでにします。
躓いたところ
1. ログファイルのパーミッション
fluentdのコンテナを立ち上げたとき、以下のエラーが発生して、コンテナが起動しなかった。
unexpected error error_class=Errno::EACCES error="Permission denied @ rb_file_s_stat - `/var/log/nginx/access.log"
fluentdがnginxのアクセスログ(access.log)を読み込む権限が無くてエラーになっていた。
fluentdが参照できるようにaccess.logファイルのパーミッションを変更した。
chmod 775 /home/com/homepage/web/log/access.log
fluentdのコンテナを再起動したら、上記のエラーは解消したが、次のエラーが発生した。
unexpected error error_class=Errno::EACCES error="Permission denied @ rb_sysopen - /fluentd/log/nginx_access.log.pos"
fluentdがnginxのアクセスログ(access.log)を読み込む際に、
どこまで読み込んだかという情報を保持するために、access.log.posというファイルを作成するが、そのファイルを作れなくてエラーになっていた。
ファイルを作成できるようにディレクトリのパーミッションを変更した。
chmod 777 ./fluentd/data
これで、エラー無くコンテナが起動するようになった。
参照
Permission denied @ rb_file_s_stat #2425
2. fluentdのログ確認
fluentdのログを確認するために、fluentdのコンテナに接続しようと以下のコマンドを実行したが、fluentdのコンテナにはbashがなかったので接続できなかった。
docker exec -it homepage_nginx_1 /bin/bash
bashではなく、shはインストールされていたので、下記のコマンドで接続することで、起動できた。
docker exec -it homepage_nginx_1 /bin/sh
接続後、コンソールにて、コマンドを実行してログを確認できた。
fluentd -vv -c /fluentd/etc/fluent.conf
参照
Fluentd の Docker イメージを動かした際のメモ
Fluentd Logging
3. fluentdコンテナのタイムゾーン
初期の状態ではUTCになっていた。docker-composeにて、環境変数でタイムゾーンを指定しても反映されなかった。
下記のサイトを参照して、Dockerfileを修正し、コンテナイメージを変更した。
Dockerコンテナ(Fluentd)のTimeZoneをUTCからJSTに変更する
その他のTips
1. 既存ログの読み込み
既に運用を開始しているwebサーバのアクセスログを可視化するため、ログファイルの先頭から読み込みたい。
fluentdの初期設定では、末尾の情報しか読み込まないので、設定の変更が必要になる。
read_from_headをTrueにする。
fluent.conf
<source>
@type tail
format nginx
path /var/log/nginx/access.log
pos_file /fluentd/data/nginx_access.log.pos
tag nginx.access
read_from_head true ← 追加!!
</source>
参考
fluentdのin_tailプラグインの動作について理解する
2. 監視間隔の短縮
デフォルトの設定では、ログファイルの更新を確認する監視間隔は60秒になっている。動作確認をする際には、時間間隔が短いほうが都合がいいので、設定を変更した。
fluent.conf
<source>
@type tail
format nginx
path /var/log/nginx/access.log
pos_file /fluentd/data/nginx_access.log.pos
tag nginx.access
read_from_head true
refresh_interval 10 ← 追加!!
</source>
参考
fluentdのin_tailプラグインの動作について理解する
3. テーブルのオートインクリメントの初期化
DBへの格納を試行錯誤していると、オートインクリメントによってIDの値がどんどん大きくなっていってしまう。
運用開始時点では、1から始まるようにしたいので、オートインクリメントの値を初期化した。
以下のSQLを実行することで初期化できる。
ALTER TABLE <テーブル名> AUTO_INCREMENT = 1;
今回のケースでは、以下のSQLになる。
ALTER TABLE nginx_access_log AUTO_INCREMENT = 1;
下記のようにtruncate table文で、すべてのレコードを削除した場合もインクリメントは初期化される。
TRUNCATE TABLE
参考
MySQL で AUTO_INCREMENT の値をリセットする方法
全てのデータを削除する(TRUNCATE TABLE文)