dockerのnginxのコンテナのログをfilebeatでElasticsearchに送りたい

前回、 Docker → Fluentd → Elasticsearch の流れでログを送ってみたけど、やっぱログはファイルとしてホストに保存しつつ、コピーを Elasticsearch にも送りたいと思うようになった。
いろんな方法があるみたいだけど、Filebeat が楽そうだった。


これ使うと簡単な設定で nginx のログを Elasticsearch に送って、pipelineでパースしてインデックスしてくれる。

- module: nginx
  access:
    enabled: true
    var.paths: ["/path/to/log/nginx/access.log*"]
  error:
    enabled: true
    var.paths: ["/path/to/log/nginx/error.log*"]

ただ、dockerのログは /var/lib/docker/container/{id}/{id}.json みたいなパスだし、中身がjsonになっているので、

var.paths: ["/var/lib/docker/container/*/*.json"]

みたいなことをしても、nginxのログとして扱われないので期待通りにパースしてくれない。
なので、以下のサイトに書かれてるようにする。

filebeat.autodiscover:
 providers:
   - type: docker
     templates:
       - condition:
         contains:
           docker.container.image: nginx
         config:
           - module: nginx
             access:
               input:
                 type: docker
                 containers.ids:
                   - "${data.docker.container.id}"
             error:
               input:
                 type: docker
                 containers.ids:
                   - "${data.docker.container.id}"

コンテナのイメージ名に "nginx" が含まれているものを nginx モジュールを使って処理する
これなら自分でヘンな作り込みしなくても簡単にログを送って、ちゃんとインデックスもつくので楽ですな。

dockerのnginxのコンテナのログをfluentdでパースして送りたい

最近docker使い始めて、ログの扱いに困り、fluentdで他のサーバに送ることを知り、Elasticsearchで保存し、Kibanaで閲覧することを覚えたばかり。

docker から fluentd でログを送ると、nginx の access.log の行が log フィールドにそのまま入ってきて、パースされていないので扱いにくいので、パースされた状態で送りたいと単純に思ったが、何しろ使い始めたばかりでいろいろ勘違いしているかもしれない。

docker run --rm --name nginx -p 80:80 --log-driver fluentd --log-opt tag="MY_APP_NAME.nginx" nginx

などとして nginx を起動して送り先を fluentd にし、fluentd側から nginx コンテナの出力だとわかるように tag を設定。
これで

curl localhost

とすると、fluentd にはこんなログが記録される。

fluentd_1 | 2020-06-26 13:55:31.000000000 +0900 MY_APP_NAME.nginx: {"container_id":"fcde1017a33b494e537923f46bf89aff68c34d2ac776c2b367d92faae69b6fcc", "container_name":"/nginx", "source":"stdout", "log":"172.17.0.1 - - [26/Jun/2020:04:55:31 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.55.1\" \"-\""}

この log フィールドをパースしたい。

curl localhsot/hoge

などと、存在しないリソースを要求すると、こう。

fluentd_1 | 2020-06-26 13:55:35.000000000 +0900 MY_APP_NAME.nginx: {"container_id":"fcde1017a33b494e537923f46bf89aff68c34d2ac776c2b367d92faae69b6fcc", "container_name":"/nginx", "source":"stderr", "log":"2020/06/26 04:55:35 [error] 28#28: *11 open() \"/usr/share/nginx/html/hoge\" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: \"GET /hoge HTTP/1.1\", host: \"localhost\""}

fluentd_1 | 2020-06-26 13:55:35.000000000 +0900 MY_APP_NAME.nginx: {"container_id":"fcde1017a33b494e537923f46bf89aff68c34d2ac776c2b367d92faae69b6fcc", "container_name":"/nginx", "source":"stdout", "log":"172.17.0.1 - - [26/Jun/2020:04:55:35 +0000] \"GET /hoge HTTP/1.1\" 404 153 \"-\" \"curl/7.55.1\" \"-\""}

nginx イメージのデフォルトでは access.log は /dev/stdout で、error.log は /dev/stderr になっていて、ログの source フィールドで判別できる。
この情報を使って fluent.conf でパースできるように設定したい。

rewrite-tag-filter が必要だったのでインストール。

gem install fluent-plugin-rewrite-tag-filter

fluent.conf で *.nginx のタグのうち source が stdout のものは *.nginx-access に、stderr のものは *.nginx-error にタグをrewriteする。
そして、そのタグを目印に filter で parse する。

<source>
    @type forward
    @label @mainstream
</source>

<label @mainstream>
    <match *.nginx>
        @type rewrite_tag_filter
        <rule>
          key source
          pattern /^stdout$/
          tag ${tag}-access
        </rule>
        <rule>
          key source
          pattern /^stderr$/
          tag ${tag}-error
        </rule>
    </match>
    
    <filter *.nginx-access>
      @type parser
      key_name log
      reserve_data true
      remove_key_name_field true
      <parse>
        @type nginx
        keep_time_key true
      </parse>
    </filter>
    
    <filter *.nginx-error>
      @type parser
      key_name log
      reserve_data true
      remove_key_name_field true
      <parse>
        @type multiline
        format_firstline /^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} \[\w+\] (?<pid>\d+).(?<tid>\d+): /
        format1 /^(?<time>\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[(?<log_level>\w+)\] (?<pid>\d+).(?<tid>\d+): (?<message>.*)/
        keep_time_key true
      </parse>
    </filter>
    
    <match>
      @type stdout
    </match>
</label>

nginx-error の parse で使っている正規表現は fluentd の古いドキュメントに載っていたものをそのまま使用。

この設定でログを処理すると、access.log の内容は nginx-access タグ、error.log の内容は nginx-error タグをつけて、パースした状態のログを転送できた。

fluentd_1 | 2020-06-26 13:36:39.000000000 +0900 MY_APP_NAME.nginx-access: {"source":"stdout", "container_id":"fcde1017a33b494e537923f46bf89aff68c34d2ac776c2b367d92faae69b6fcc", "container_name":"/nginx", "remote":"172.17.0.1", "host":"-", "user":"-", "time":"26/Jun/2020:04:36:39 +0000", "method":"GET", "path":"/", "code":"200", "size":"612", "referer":"-", "agent":"curl/7.55.1", "http_x_forwarded_for":"-"}

fluentd_1 | 2020-06-26 13:37:04.000000000 +0900 MY_APP_NAME.nginx-access: {"container_id":"fcde1017a33b494e537923f46bf89aff68c34d2ac776c2b367d92faae69b6fcc", "container_name":"/nginx", "source":"stdout", "remote":"172.17.0.1", "host":"-", "user":"-", "time":"26/Jun/2020:04:37:04 +0000", "method":"GET", "path":"/hoge", "code":"404", "size":"153", "referer":"-", "agent":"curl/7.55.1", "http_x_forwarded_for":"-"}

fluentd_1 | 2020-06-26 04:37:04.000000000 +0900 MY_APP_NAME.nginx-error: {"container_id":"fcde1017a33b494e537923f46bf89aff68c34d2ac776c2b367d92faae69b6fcc", "container_name":"/nginx", "source":"stderr", "time":"2020/06/26 04:37:04", "log_level":"error", "pid":"28", "tid":"28", "message":"*8 open() \"/usr/share/nginx/html/hoge\" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: \"GET /hoge HTTP/1.1\", host: \"localhost\""}

Windows 10 バージョン 2004 へのアップデートが失敗、初期化もできず

からの解決。
Windows 10 Version 2004 を使いたいがアップデートが失敗。
初期化しようにも初期化も失敗して元に戻り、もうメディアからの再インストールするしかないかとも思ったけど、Wi-Fi 6 カードを抜いたらアップデートできた。

マザーボードMSI MPG Z390M GAMING EDGE AC。

Wi-Fi 6 のカードはこれ。

マザーボードIntel Wireless-AC 9560 が載ってるところに、PCI ExpressIntel Wi-Fi 6 AX200 を付けている状態だったので、PCI Expressカードを抜いてみたら無事アップデート完了した。

ただし、アップデート後に、再び Wi-Fi 6 のカードを差し込んだらまたブルースクリーンでクラッシュしてしまった。
デスクトップが出る前にクラッシュしてしまうのでsどうにもならないが、2回起動失敗すると選択肢が出てくるので「トラブルシューティング」「詳細オプション」「スタートアップ設定」「再起動」と進み再起動する。
すると1~9の数字が表示された画面が出てくるので「4) セーフモードを有効にする」を選ぶ。

これでデスクトップにアクセスできるので、デバイスマネージャーで AC 9560 を無効化して再起動。
これで Wi-Fi 6 AX200 が有効にしたままデスクトップまでいけるので、デバイスマネージャで AX200 のドライバを更新。

これで一件落着。


Version 2004 になったらさっそく WSL2 をセットアップ。
これで Windows 10 「Home」でも docker が使えるようになった。

zabbix-agentでnginxのログから2xxの数を数えたい

nginxのログから 2xx や 4xx や 5xx のエラーの数を計算して取得したい。
logrt.count を使うとログファイルを監視して、正規表現にマッチした行数を取得できるらしい。
「Zabbixエージェント(アクティブ)」でキーを

logrt.count[/var/log/nginx/access.log,.*?" 2\d\d ]

にしてみる。
f:id:gae:20200529143443p:plain


これで行けるはずだけど zabbix_agentd.log を見ると

active check "log.count[/var/log/nginx/access.log,.*?" 2\d\d ]" is not supported: Unsupported item key.

"Unsupported item key" とのこと。
ログの上の方を見ると...

Zabbix Agent stopped. Zabbix 3.0.12 (revision 73586).

zabbix-agent のバージョンがむっちゃ古い。
Ubuntu 18.04 で普通にインストールした zabbix-agent はバージョンが 3.0 で logrt.count は 3.2 からの対応なので、新しいのにする必要がある。
↓ここを見てリポジトリを追加。

# wget https://repo.zabbix.com/zabbix/4.4/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.4-1+bionic_all.deb
# dpkg -i zabbix-release_4.4-1+bionic_all.deb
# apt update

zabbix-agent を upgrade する。

apt upgrade

zabbix_agentd.conf はそのままでも問題ないが、アップグレード中に警告がでていた。

dpkg: warning: unable to delete old directory '/var/log/zabbix-agent': Directory not empty

どうもログの場所が /var/log/zabbix-agent から /var/log/zabbix に変わったらしくて、古いディレクトリを消そうとしたらしい。
まぎらわしいので /var/log/zabbix-agent は削除しておく。

# rm -rf /var/log/zabbix-agent

これでアップグレードできたのでzabbix-agentを再起動してみると、またエラー出てる。

active check "logrt.count[/var/log/nginx/access.log,.*?" 2\d\d ]" is not supported: Cannot open file "/var/log/nginx/access.log.1": [13] Permission denied

zabbix がログにアクセスできない。zabbix を adm グループに追加。

# gpasswd -a zabbix adm

これでちゃんと動いた。

docker-compose のcontainerやvolumeのプレフィックスを変えたい

デフォルトだとディレクトリの名前がつくのがあんまり嬉しくない感じ。

SET COMPOSE_PROJECT_NAME=hoge

または

docker-compose -p hoge up


TokyoTyrant で特定のPREFIXキーを含むものをすべて消したい

ちょっとしたミスで TokyoTyrant のデータベースにへんなデータを入れてしまい、特定のprefixを持ったキーをすべて削除することになった。

$ tcrmgr list -port 3001 -fm PREFIX localhost | while read line 
do 
  tcrmgr out -port 3001 localhost $line
done

PC用モニタの表示遅延がテレビよりも大きかった

この前GH5のHDMI出力がどれくらい遅延しているか試した。
f:id:gae:20200516161214j:plain:w400
f:id:gae:20200516161217j:plain:w400
結果、テレビ(BRAVIA W730C)に出力したときは約6フレーム(100ms)遅れていることがわかったんだけど、PC用のモニタ(EIZO CG2420)に出力した場合は約7フレーム(116.6ms)遅延していて、テレビよりもPC用モニタの方が遅延が大きいということに気づいた。(テレビは遅延を最小限にするためにゲームモードを選択している)

テレビというものは画像処理のために遅延はつきものだけど、PC用モニタはそういうのがないので遅延は最小限しかないと思い込んでいたところ、逆の結果になってしまったのでちょっとショック。

今日は別の方法でPC用モニタの遅延を確認してみた。
まず最初の構成。

EIZO EV3237 には2つの入力を2分割して表示する「P by P」モードがある。
これで、DisplayportとHDMIをクローンモードで出力して、DisplayportとHDMIに出力される映像のタイミングか一致しているか確認してみた。
f:id:gae:20200516155125j:plain:w400
結果、一致していることを確認できた。

次。クローンモードでDPとHDMIのタイミングが一致していることはわかったので、別々のディスプレイにつないで遅延を確認。

f:id:gae:20200516160407j:plain:w400
1~2フレーム程度 EV3237 が遅延していることを確認。
EV3237は4Kモニタなのでスケーリングして表示されているが、スケーリングを切って画面中央に小さく表示する状態にしても結果は同じだった。

最近、「ワンダーボーイ ドラゴンの罠」というアクションゲームで遊んでいて、やたらと足場から落ちまくっていたけど、これはゲームの腕のせいじゃなくて「モニタが遅延しているからだ」ということがわかってちょっとすっきりした。

EV3237は買ってから5年が経過している。画面右隅は色が変色してきて、劣化を感じるようになってきた。当時はまだ4Kが普及してなかったこともあって18万円もしたがそろそろ買い替えも検討しようかな。

追記

DisplaySpecifications というサイトで入力遅延のデータが載っていた。

機種 Input Lag
EV3237 31ms
CG2420 12ms

とのこと。BRAVIA W730C はSonyのサイトで「0.1フレーム」と記載されている。

私の確認方法では W730C → CG2420 は 1フレーム、On-Lap1502l → EV3237 は 1~2フレームなので、上記サイトの Input Lag のデータとほぼ一致しますな。On-Lap1502l の遅延は1フレーム弱ということになりそう。

新しいモニタを買うときは「4K」とか「HDR」とかいうスペックも気になるけど、遅延についてもチェックしたほうがいいですな。