概要
logspout というログ収集専用コンテナを使って CoreOS 上で起動する Docker コンテナのログを集約し、リモートログサーバへルーティングするという話。
systemd で CoreOS 上の Docker コンテナのログを集約・ルーティングする の "sidekick" の問題点をふまえ、 起動したいコンテナがログ収集プロセスについて気にしなくてよいアプローチを試す
Docker コンテナのログ収集の選択肢
Docker コンテナのログ収集では選択肢が 3 種類ある
- コンテナ内部で収集する: コンテナ内でログ収集のプロセスをメインプロセスと同時に走らせログを収集・ルーティングする
-
コンテナ外部で収集する:
-v
を使ってログ収集対象コンテナのログディレクトリをホストに共有し、ホスト側のログ収集のエージェントを使ってログを収集・ルーティングする -
収集用コンテナを立てる:
--volumes-from
を使ってログ収集専用コンテナとログ収集対象コンテナでログディレクトリを共有し、ログ収集専用コンテナがログを収集・ルーティングする
参考: logspoutでDockerコンテナのログの集約・ルーティング - SOTA
systemd で CoreOS 上の Docker コンテナのログを集約・ルーティングする は、 コンテナ外部で収集する にあたる。 コンテナ外部で収集すること自体が悪かった訳ではなく、 systemd の unit のバインドを使った "coprosess" "sidekick" と呼ばれる方法だと、コンテナを起動する際にログ収集プロセスについても一緒に考える必要が出て来てこれは正直面倒だよね、という話。そこでコンテナを起動する際ログ収集プロセスについて考えなくてよい方法として progrium/logspout というものがある。 logspout を使えば、上記の問題が解決するだけでなく Datadog で CoreOS をモニタリングする と同じく、出来るだけ全てコンテナだけでやる "docker way" が実現できる。(これは 収集用コンテナを立てる にあたる)
logspout を使ったログの集約
logspout は Docker コンテナのログ収集専用のサービス。 Docker イメージが用意されており、ホストの /var/run/docker.sock
をマウントすることで、そのホストで起動する全ての Docker コンテナのログを収集・ルーティングしてくれる。ログ収集対象コンテナに何の設定がいらないことが一番のメリット。
例えば以下のような hello world を出力するコンテナを 3 つ起動しておく
core@core-01 ~ $ docker run --name hello.1 busybox /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
core@core-01 ~ $ docker run -d --name hello.1 busybox /bin/sh -c "while true; do echo hello world; sleep 1; done"
1d6d6b3a37d663730761726a9e8ca4245ca6beefac9c3b800680db8fcb076af3
core@core-01 ~ $ docker run -d --name hello.2 busybox /bin/sh -c "while true; do echo hello world; sleep 1; done"
8a031e9eacacfa9369fafdb001ea05880cdc742a153fc8191a168def1cd1f1ef
core@core-01 ~ $ docker run -d --name hello.3 busybox /bin/sh -c "while true; do echo hello world; sleep 1; done"
6c47bf268ed8999fb4a6c9108fe2a2f203266cea7f3704d8c12ed55c706ab713
logsput を起動
core@core-01 /etc/systemd/system $ docker run -d --name logspout -p 8000:8000 -v=/var/run/docker.sock:/tmp/docker.sock progrium/logspout
d4e86b5f001657124a725e4b7ee5bacc61809f082fbeb5c47c437171403c63a3
logspout の HTTP API を使って、ログストリームを出力すると色分けされて出力してくれる:)
コンテナ名で絞り込み
core@core-01 ~ $ curl localhost:8000/logsfilter:hello
hello.2|hello world
hello.1|hello world
hello.3|hello world
hello.2|hello world
コンテナ名指定
core@core-01 ~ $ curl localhost:8000/logs/name:hello.1
hello world
hello world
hello world
コンテナ ID 指定
core@core-01 ~ $ curl localhost:8000/logs/id:6c47bf268ed8
hello world
hello world
logspout へ集約したログのルーティング
現状は UDP syslog 経由でのルーティングしか対応していない
Logentries へルーティングする例
Logentries で token を作成する
新しくログを追加
syslogd を選択
token が表示されるのでこの token を使って logenteries へ送る。
logspout を起動する
logspout を起動する。
core@core-01 ~ $ docker run -d --name logspout -h $HOSTNAME -p 8000:8000 -v=/var/run/docker.sock:/tmp/docker.sock progrium/logspout
Syslog のホスト名部分は logspout のコンテナのホスト名が利用されるので、 -h $HOSTNAME
を付けておくとよい。
# -h $HOSTNAME 無しのログの例
<14>2014-09-11T15:34:30Z 8ce4ab44556 hello.2[1]: hello world
# -h $HOSTNAME 付きのログの例
<14>2014-09-11T15:34:30Z core-01 hello.2[1]: hello world
ルーティングを作成する
logentries へ全てのログを送るルーティングを作成する
core@core-01 ~ $ curl localhost:8000/routes -X POST \
-d '{"target": {"type": "rfc5424", "addr": "api.logentries.com:10000", "structured_data": "e15dca62-3629-44f3-9057-ca586dcad7a3"}}'
core@core-01 ~ $ curl localhost:8000/routes
[
{
"id": "2b38d0c5c069",
"target": {
"type": "rfc5424",
"addr": "api.logentries.com:10000",
"structured_data": "e15dca62-3629-44f3-9057-ca586dcad7a3"
}
}
]
これで全てのコンテナのログが Logentries に送信され、
Logentries 上でリアルタイムにフィルタリングできる
特定の名前がついたコンテナのログだけルーティングする
core@core-01 ~ $ curl localhost:8000/routes -X POST -d '{"source": {"filter": "hello"}, "target": {"type": "rfc5424", "addr": "api.logentries.com:10000", "structured_data": "bec96897-1804-44d4-a8ac-89bd380a038b"}}'
core@core-01 ~ $ curl localhost:8000/routes
[
{
"id": "8e549a91270f",
"source": {
"filter": "hello"
},
"target": {
"type": "rfc5424",
"addr": "api.logentries.com:10000",
"structured_data": "bec96897-1804-44d4-a8ac-89bd380a038b"
}
}
]
Cloud-Config から logspout を起動する
#cloud-config
# 一部抜粋
coreos:
units:
- name: logspout.service
command: start
enable: true
content: |
[Unit]
Description=Logspout
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill logspout
ExecStartPre=-/usr/bin/docker rm logspout
ExecStartPre=/usr/bin/docker pull progrium/logspout:latest
ExecStart=/usr/bin/docker run --name logspout -h %H -p 8000:8000 -v=/var/run/docker.sock:/tmp/docker.sock progrium/logspout:latest rfc5424://api.logentries.com:10000?structuredData=xxxxxxxxxxxxxxxxxxxxxxxxxx
ExecStop=/usr/bin/docker stop progrium/logspout:latest
[Install]
WantedBy=multi-user.target
メモと感想
logspout 便利...
コンテナ個別にルーティングしない理由
logsput はフィルタリングしたログをどこどこへルーティングといったことをいくつも登録することができるようになっている。が、それをし始めると「あるコンテナを立てた際に、 logspout に routes を追加しないと」というように "coprocess" "sidekick" っぽくなる気がしてるので、コンテナのログは全て logspout に集約し、コンテナ名のタグ付きで全てリモートサーバに送り、リモートサーバ側でフィルタリングして見る、というのがシンプルな気がしてる。
token ベースで logentries へ送る必要がある
logentries は固有ポートへの Plain TCP, UDP 経由で送ると、送信元 IP で判別するので、CoreOS マシンを捨てられなくなる。token ベースの方を追記する
https://github.com/progrium/logspout/issues/12
https://github.com/progrium/logspout/issues/13
I didn't catch that part. Yes, that will require some modification to
support custom structured data. Maybe make a new issue for that feature
request.
まだ syslog header に token 入れるのは出来ないもよう。 CoreOS マシンを disposable にしておくために、journalctl の json output を jq で整形したのちルーティングする方法 も試している
追記: https://github.com/progrium/logspout/pull/20 にて token 追加できるようになる!
2015/01/13 追記: マージされた!
どちらにせよ journalctl の json log はリモートサーバへ送っておく
Docker コンテナ以外のログもあるし、後からハンドリングしやすいということで全てのコンテナは systemd 経由で起動しておき、journalctl の json ログを全て送っておく
REF
- logspoutでDockerコンテナのログの集約・ルーティング - Docker のログ管理方法について非常に勉強になりました
- progrium/logspout