ログ収集エージェントをDaemonSetでKubernetesの全nodeに配置してログを収集する、というのはクラスタレベルロギングと呼ばれていてよく目にする。アプリケーションが標準出力、標準エラーにログを吐くとコンテナがよしなにnodeのファイルシステムに書き込んでくれる。
Cluster-level logging architectures

FluentdとElasticsearch
GCP以外の場合はFluentdとElasticsearch(多分)一択で細かい設定は fluent/fluentd-kubernetes-daemonset が参考になる。ポイントは
fluent-plugin-elasticsearch
とfluent-plugin-kubernetes_metadata_filter
を追加 (参考)- sourceのpathに
/var/log/containers/*.log
を指定してkubernetes.*
タグをつける (参考) - filterで
kubernetes_metadata
を指定する(参考)
あたりかなと。kubernetes_metadata_filter
でkubernetesのメタ情報を追加してElasticsearchにログを送れる。kibanaでみるとこんな感じ。

elasticsearch_dynamicで動的にindexを変える
ただこの設定だと /var/log/containers/*.log
にすべてのコンテナのログを一つのindex(正確には日付で別れるけど)ぶち込むことになる。
色々調べてみると elasticsearch_dynamic
というのがあったので使ってみた。
<match kubernetes.**>
type copy
<store>
@type elasticsearch_dynamic
host "#{ENV['ELASTICSEARCH_HOST']}"
port "#{ENV['ELASTICSEARCH_PORT']}"
flush_interval "#{ENV['FLUSH_INTERVAL_ES']}"
logstash_format true
logstash_prefix ${record['kubernetes']['namespace_name']}-${record['kubernetes']['container_name']}
</store>
</match>
$record
という変数でkubernetesのメタ情報を参照できたので {NAMESPACE}-{CONTAINER NAME}
というようにindexを分けることができた。
でも本番でelasticsearch_dynamicは使えなさそう
ただしDynamic configurationに書かれているが Rubyのevalを使っているためパフォーマンスとセキュリティリスク が高い。
今回はnamespaceとcontainer名しか使っていないので悪意のあるコードが混入されるとは考えにくいがアクセスログなど滝のように流れるログに使うのは難しいかもしれない(ベンチ取ってないけど)。
アクセスログは別で収集するとか、開発環境だけで使うとかであればよさそう。
雑感
Elasticsearchにログが入ればPrometheusと組み合わせてログ監視とか面白そうだなーと思いを馳せつつ。ネストしたJSONでもkeyとして認識してくれるのでマイクロサービス間でログレベルと時刻のkey名を統一してそれを監視するExporter作るとかできそう(もしかしたらもうあるかもしれんけど)。
アクセスログは最近流行っている Service Mesh でも取れるし、モニタリングもMeshとかぶるところ多いからうまい具合に使い分けしたい。