Dockerコンテナからログファイル出力がアプリケーション的に難しい、けれどエンジニア以外でもログを見たいという話が出たので調査してみました。
方針としては、以下を目指しています。
- docker-compose でできる
- ログファイル出力がなくてもできる
- docker logs でログを見ることができる
- 社内のみの利用を想定してセキュリティはあまり考慮しないが、そのぶん保守コストは抑えたい
上記を解決するために Logspout, Logstash, Elasticsearch, Kibana を選択してみることにしました。
Logspout を導入する
Logspout の導入は比較的簡単でした。
version: '2' services: logspout: image: gliderlabs/logspout:v3.2.4 command: <コマンドは後述> volumes: - /var/run/docker.sock:/tmp/docker.sock
dokcer-compose.yml
Logspoutはdocker.sockをマウントする必要がある代わりに、コンテナとして起動するだけでホスト上にあるDockerコンテナすべてのログを、既存のコンテナを変更することなく集約することができます。 本番運用する際には、以下の点があり、気をつける必要があります。
- Logspout自体が停止した場合やルーティング先が停止した場合にログが欠損する
- docker.sockをマウントするため、セキュリティ面の懸念がある
- Dockerに流れるログのみしか扱えない
とはいえ、特に開発用の環境などの手軽に集約できることが重視されるような状況だと便利だと思います。
Logstash を建てて、Logspoutからルーティング設定する
次に Logstash を建てて、Logspout からログ情報を受け取るようにします。
version: '2' services: logspout: image: gliderlabs/logspout:v3.2.4 command: syslog://logstash:5000 environment: - RETRY_COUNT=100 links: - logstash volumes: - /var/run/docker.sock:/tmp/docker.sock logstash: image: docker.elastic.co/logstash/logstash:5.6.0 environment: - LOGSPOUT=ignore - XPACK_MONITORING_ENABLED=false volumes: - ./config/logstash.conf:/usr/share/logstash/pipeline/logstash.conf
dokcer-compose.yml
Logstash に関しては設定が必要になるため、logstash.conf をマウントするようにしています。また今回は、X-Packの機能を使う予定は無いため、X-Pack Moniteringを無効化しています。
LOGSPOUT=ignore
は logspout にログ収集してほしくないコンテナに設定しておくとそのコンテナのログは収集されなくなります。
Logspout には、Logstash へのルーティング設定とLogstashが起動する前にLogspoutが起動してしまった場合に備えて、
ルーティング先への接続のリトライ数を設定する RETRY_COUNT
を増やしています。
logstash.conf を設定する
今回はとりあえず Logspout 経由で取得されたログを見やすい形にすることだけを目的にしています。 詳細は省きますが、syslogによる余計なデータを省略し、コンテナのメッセージのみに削って流れるようにしています。
Logstash では色々とログのメッセージをパイプライン処理できるため、今回やっていること以上に複雑なことにも対応できます。
input { syslog { port => 5000 type => "docker" } } filter { grok { match => { "message" => "%{SYSLOG5424PRI}%{NONNEGINT:ver} +(?:%{TIMESTAMP_ISO8601:ts}|-) +(?:%{HOSTNAME:service}|-) +(?:%{NOTSPACE:containerName}|-) +(?:%{NOTSPACE:proc}|-) +(?:%{WORD:msgid}|-) +(?:%{SYSLOG5424SD:sd}|-|) +%{GREEDYDATA:msg}" } } syslog_pri { } date { match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ] } mutate { remove_field => [ "message", "priority", "ts", "severity", "facility", "facility_label", "severity_label", "syslog5424_pri", "proc", "syslog_severity_code", "syslog_facility_code", "syslog_facility", "syslog_severity", "syslog_hostname", "syslog_message", "syslog_timestamp", "ver" ] } mutate { rename => { "msg" => "message" } } } output { elasticsearch { hosts => "elasticsearch:9200" } stdout { codec => rubydebug } }
config/logstash.conf
Elasticsearch + Kibana でログを表示する
Elasticsearch と Kibana を起動する
version: '2' services: kibana: image: docker.elastic.co/kibana/kibana:5.6.0 environment: - LOGSPOUT=ignore - ELASTICSEARCH_URL=http://elasticsearch:9200 - XPACK_MONITORING_ENABLED=false - XPACK_SECURITY_ENABLED=false links: - elasticsearch ports: - 5601:5601 elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:5.6.0 environment: - LOGSPOUT=ignore - node.name=dev-log-kibana - bootstrap.memory_lock=true - xpack.security.enabled=false - xpack.monitoring.enabled=false - xpack.watcher.enabled=false - xpack.graph.enabled=false - "ES_JAVA_OPTS=-Xms1g -Xmx1g" mem_limit: 2g ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65536 hard: 65536
わかりやすい部分の細かい説明は省きます。
Elasticsearch の環境変数では、スワップの無効化、X-Pack の無効化、Javaのヒープサイズの設定を行っています。
メモリはJavaヒープサイズの2倍、ulimit でカーネルの設定を行います。このあたりの設定を行っていないと起動時にメモリ不足等で起動しなかったりします。
Kibana でログを確認する
Kibana は起動時に最適化処理が走るため、少々時間がかかります。
なので、 数分待ってから localhost:5601
にアクセスしてみます。
最初にデフォルトのインデックスパターンを設定します。 上記の画面で Create を押せばOKです。
そのあと Discover を表示するとログを見ることができます。
まとめ
Logstash や Elasticsearch の設定周りはやや難しいですが、一度動かせるようになれば既存のコンテナにはほぼ手を加えずにログを集約、可視化できます。
今回は使用したい状況に対して、ちょっとメモリ使用量が多くなりすぎてしまったので本来想定していた用途としては厳しいかもというところですが、実際コレだけで動くのはかなり使いやすいと思います。おそらくElasticsearch, Kibana を使わずに、 Logstash の File output plugin を利用してみるか、Elasticsearch + Kibana だけを別に建ててみることになりそうです。
今回書いたもののサンプルをGitHubに公開したので興味ある方はそちらを参考にしてみてください。