tjinjin's blog

インフラ要素多めの個人メモ

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.

Log Management

挙動が気になったので調べてみます。

datadog-agentのログ集約の仕組み

結論から言うとAPI経由でログを取得しています。(たぶん)

https://github.com/DataDog/datadog-agent/blob/5e928961e3de72105f166dd5287f51ea01bb4b96/pkg/logs/input/docker/tailer.go#L133

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\&timestamp=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として使っている場合、下記の様な流れになりそうです。(たぶん)

なぜ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を見に行くのがいいのかちょっと考えたいところです。