datadog-agent・k8sのdocker containerのログの取得方法を調べる
About
log周りが気になったので雑多にいろいろ調べます。間違ってたらこっそり教えてください。
docker API検証環境
- ecs optimized instance amzn-ami-2018.03.i-amazon-ecs-optimized
$ docker version ... Server: Engine: Version: 18.06.1-ce API version: 1.38 (minimum version 1.12) Go version: go1.10.3 Git commit: e68fc7a/18.06.1-ce Built: Fri Oct 26 23:39:46 2018 OS/Arch: linux/amd64 Experimental: false
dockerのloggingについて
logについてはlogging driverの設定で様々な方法でログを吐くことができます。docker-ce.18.09だと下記をサポートしています。
- logentries
- json-file
- gelf
- syslog
- awslogs(CWLogs)
- ETW(Windows向け?)
- fluentd
- gcplogs(Google Cloud Logging driver)
- journald
- splunk
View logs for a container or service | Docker Documentation
ドキュメントにはまだ無いようですが、18.09よりlocal
というタイプもサポートされています。json-fileの置き換えみたいですかね。
[Proposal] Enhance local logging / remote logging · Issue #33475 · moby/moby
各driverごとに--log-opt
で制御できる項目があるので、メリデメを比較して選択することになりそうです。
datadog-agentのログの取得方法
ドキュメントを見るとlogging driverを使わずにログを取得できるようです。
The Datadog Agent can collect logs directly from container stdout/stderr without using a logging driver. When the Agent’s Docker check is enabled, container and orchestrator metadata are automatically added as tags to your logs.
挙動が気になったので調べてみます。
datadog-agentのログ集約の仕組み
結論から言うとAPI経由でログを取得しています。(たぶん)
APIの叩き方
root権限があればcurlを使ってunix-domain socket経由でAPIを叩くことが可能です。実際に試してみます。
# curl --unix-socket /var/run/docker.sock http://docker/version {"Platform":{"Name":""},"Components":[{"Name":"Engine","Version":"18.06.1-ce","Details":{"ApiVersion":"1.38","Arch":"amd64","BuildTime":"2018-10-26T23:39:46.000000000+00:00","Experimental":"false","GitCommit":"e68fc7a/18.06.1-ce","GoVersion":"go1.10.3","KernelVersion":"4.14.77-69.57.amzn1.x86_64","MinAPIVersion":"1.12","Os":"linux"}}],"Version":"18.06.1-ce","ApiVersion":"1.38","MinAPIVersion":"1.12","GitCommit":"e68fc7a/18.06.1-ce","GoVersion":"go1.10.3","Os":"linux","Arch":"amd64","KernelVersion":"4.14.77-69.57.amzn1.x86_64","BuildTime":"2018-10-26T23:39:46.000000000+00:00"}
API経由でログを取得する
次にAPIを直接叩いてログを取得してみます。取得するためにはそのコンテナのlogging driverがjson-fileかjournaldである必要があります。
Docker Engine API v1.39 Reference #operation/ContainerLogs
対応してないlogging driverを使っている場合は以下のようになりました。
# curl --unix-socket /var/run/docker.sock http://docker/containers/354762cc39ed/logs?stdout=1 {"message":"configured logging driver does not support reading"}
取得したいコンテナのIDをdocker ps等で取得し、logを取ってみます。
# curl --unix-socket /var/run/docker.sock http://docker/containers/94c3df583f6f/logs?stdout=1\&stderr=1\×tamp=true\&since=1546337593 2019-01-01T10:13:14Z [INFO] Task [arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3]: recording execution stopped time. Essential container [serin] stopped at: 2019-01-01 10:13:14.430156102 +0000 UTC m=+20914.955258823 H2019-01-01T10:13:14Z [INFO] Managed task [arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3]: sending container change event [serin]: arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3 serin -> STOPPED, Exit 1, , Ports [{8080 32931 0.0.0.0 0}], Known Sent: RUNNING E2019-01-01T10:13:14Z [INFO] Managed task [arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3]: sent container change event [serin]: arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3 serin -> STOPPED, Exit 1, , Ports [{8080 32931 0.0.0.0 0}], Known Sent: RUNNING 019-01-01T10:13:14Z [INFO] Managed task [arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3]: sending task change event [arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3 -> STOPPED, Known Sent: RUNNING, PullStartedAt: 2019-01-01 10:12:42.267164134 +0000 UTC m=+20882.792266780, PullStoppedAt: 2019-01-01 10:12:42.421250784 +0000 UTC m=+20882.946353392, ExecutionStoppedAt: 2019-01-01 10:13:14.430156102 +0000 UTC m=+20914.955258823] 019-01-01T10:13:14Z [INFO] TaskHandler: batching container event: arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3 serin -> STOPPED, Exit 1, , Ports [{8080 32931 0.0.0.0 0}], Known Sent: RUNNING L2019-01-01T10:13:14Z [INFO] TaskHandler: Adding event: TaskChange: [arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3 -> STOPPED, Known Sent: RUNNING, PullStartedAt: 2019-01-01 10:12:42.267164134 +0000 UTC m=+20882.792266780, PullStoppedAt: 2019-01-01 10:12:42.421250784 +0000 UTC m=+20882.946353392, ExecutionStoppedAt: 2019-01-01 10:13:14.430156102 +0000 UTC m=+20914.955258823, arn:aws:ecs:ap-northeast-1:078190609136:task/003c71ad-7b8f-4e4d-b1c1-cf18ef9f3ae3 serin -> STOPPED, Exit 1, , Ports [{8080 32931 0.0.0.0 0}], Known Sent: RUNNING] sent: false
いくつかqueryパラメータを利用することで、期間を指定してログを取得できます。
k8sのログの取得方法?
k8sのlogging のアーキテクチャはドキュメントに記載されています。 Logging Architecture - Kubernetes
ただもうちょっと細かい挙動が気になったので確認します。
dockerをruntimeとして使っている場合、下記の様な流れになりそうです。(たぶん)
- kubeletがコンテナ起動時にログ・ファイルの場所をdockerのAPIを叩き取得している
- 取得したLogPathに対してsymbolic linkを貼っている https://github.com/kubernetes/kubernetes/blob/d8eba8817b167a2de100e7c8e0caa721852c5076/pkg/kubelet/dockershim/docker_container.go#L246
- /var/log/containers -> /var/log/pods -> /var/lib/docker/containertsのように2段構えでリンクを張ってそう
- ホストマシンからは/var/log/containersを見ていれば良い。
- fluentdをdaemonsetで起動する際には
/var/lib/docker
をマウントすることでログを読むことができる https://github.com/fluent/fluentd-kubernetes-daemonset/blob/master/templates/conf/kubernetes.conf.erb#L11-L27
なぜsymlinkを張るのか?
どうもCRIというinterfaceに準拠した形のために行っているようです。 CRI: Symlink docker logs to CRI defined log path. by Random-Liu · Pull Request #34858 · kubernetes/kubernetes
感想
コンテナのログはパフォーマンスに問題無い限り一旦ファイルに吐かせた方がいい気がしています。CWLogsやfluentdに直接投げる場合、そのバックエンドに何らかの問題があった場合ログの救出が困難かつ、問題の広がり方が部分的にとどまりません。一度ファイルに吐くことのほうが安定する気がしていますし、そこからfluentdでtailするなどであればログ出力とログの収集を分離できます。fluentdにログを直接投げる場合は、特にイメージの入れ替え時のログの扱いがめんどくさそうかなと思っています。(dockerdがretryしてくれますが、bufferから溢れてしまったら消え去るはず)
ECSで動かす場合、k8sのようにログにsymlinkを貼ってくれるわけではないので、直接/var/lib/dockerを見に行くのがいいのかちょっと考えたいところです。