log
docker
CoreOS
systemd

logspout で CoreOS 上の Docker コンテナのログを集約・ルーティングする

More than 3 years have passed since last update.


概要

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 を使って、ログストリームを出力すると色分けされて出力してくれる:)

1__core_core-01____ssh_.png


コンテナ名で絞り込み

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 を作成する

新しくログを追加

hosts_CoreOS___Logentries.png

Syslog を選択

hosts_CoreOS___Logentries.png

syslogd を選択

hosts_CoreOS___Logentries.png

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 に送信され、

hosts_CoreOS_Docker_Containers___Logentries.png

Logentries 上でリアルタイムにフィルタリングできる

hosts_CoreOS_Docker_Containers___Logentries.png


特定の名前がついたコンテナのログだけルーティングする

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