容器由于其特殊性,在日志采集上有着不同的解决方案,目前主要还是以探针采集为主。

日志采集演进

容器日志采集方案一直不断的演进,纵览当前容器日志收集的场景,无非就是两种方式:一是直接采集Docker标准输出,容器内的服务将日志信息写到标准输出,这样通过Docker的log driver可以发送到相应的收集程序中;二是延续传统的日志写入方式,容器内的服务将日志直接写到普通文件中,通过Docker volume将日志文件映射到Host上,日志采集程序就可以收集它。

docker log driver

docker logs

docker logs edc-k8s-demo

默认情况下,Docker的日志会发送到容器的标准输出设备(STDOUT)和标准错误设备(STDERR),其中STDOUT和STDERR实际上就是容器的控制台终端。如果想要持续看到新打印出的日志信息,那么可以加上 -f 参数

docker logs -f edc-k8s-demo

Docker logging driver

Docker还提供了其他的一些机制允许我们从运行的容器中提取日志,这些机制统称为 logging driver。

对Docker而言,其默认的logging driver是json-file,如果在启动时没有特别指定,都会使用这个默认的logging driver。json-file会将我们在控制台通过docker logs命名看到的日志都保存在一个json文件中,我们可以在服务器Host上的容器目录中找到这个json文件。

容器日志路径:/var/lib/docker/containers/<container-id>/<container-id>-json.log

除了json-file,Docker还支持以下多种logging dirver

  • none No logs are available for the container and docker logs does not return any output.
  • local Logs are stored in a custom format designed for minimal overhead.
  • json-file The logs are formatted as JSON. The default logging driver for Docker.
  • syslog Writes logging messages to the syslog facility. The syslog daemon must be running on the host machine.
  • journald Writes log messages to journald. The journald daemon must be running on the host machine.
  • gelf Writes log messages to a Graylog Extended Log Format (GELF) endpoint such as Graylog or Logstash.
  • fluentd Writes log messages to fluentd (forward input). The fluentd daemon must be running on the host machine.
  • awslogs Writes log messages to Amazon CloudWatch Logs.
  • splunk Writes log messages to splunk using the HTTP Event Collector.
  • etwlogs Writes log messages as Event Tracing for Windows (ETW) events. Only available on Windows platforms.
  • gcplogs Writes log messages to Google Cloud Platform (GCP) Logging.
  • logentries Writes log messages to Rapid7 Logentries.

我们可以在容器启动时通过加上 –log-driver 来指定使用哪个具体的 logging driver,例如:

docker run -d --log-driver=syslog ......

如果想要设置默认的logging driver,那么则需要修改Docker daemon的启动脚本,例如:

{
  "log-driver": "json-file",
  "log-opts": {
    "labels": "production_status",
    "env": "os,customer"
  }
}

每个logging driver都有一些自己特定的log-opt,使用时可以参考具体官方文档。

可见,第一种方式足够简单,直接配置相关的Log Driver就可以,但是这种方式也有些劣势:

  • 当主机的容器密度比较高的时候,对Docker Engine的压力比较大,毕竟容器标准输出都要通过Docker Engine来处理。
  • 尽管原则上,我们希望遵循一容器部署一个服务的原则,但是有时候特殊情况不可避免容器内有多个业务服务,这时候很难做到所有服务都向标准输出写日志,这就需要用到前面所说的第二种场景模式。
  • 虽然我们可以先选择很多种Log Driver,但是有些Log Driver会破坏Docker原生的体验,比如docker logs无法直接看到容器日志。

docker volume

通过对第一种方案的摸索,存在着很多的问题与不方便,所以目前我们大多数采集还是使用第二种方案,文件采集的方式。

第三方采集方案

上面都是将日志文件落到STDOUT和STDERR,我们采集都是基于这个,其实在我们应用编程的时候,完全可以将日志文件落到容器的对应的目录下,落盘然后使用第三方采集组件比如filebeat、fluentd等采集,统一管理。

容器日志采集方案

根据上面的基本描述,容器日志采集有很多种方式,每种方式都用不同实现方案,适用于不同的场景。

LogDriver

DockerEngine 本身具有 LogDriver 功能,可通过配置不同的 LogDriver 将容器的 stdout 通过 DockerEngine 写入到远端存储,以此达到日志采集的目的。这种方式的可定制化、灵活性、资源隔离性都很低,一般不建议在生产环境中使用,上面我们已经说明不使用的原因。

http

业务直写是在应用中集成日志采集的 SDK,通过 SDK 直接将日志发送到服务端。这种方式省去了落盘采集的逻辑,也不需要额外部署 Agent,对于系统的资源消耗最低,但由于业务和日志 SDK 强绑定,整体灵活性很低,一般只有日志量极大的场景中使用,这是一种特殊的场景,我们会在特殊情况下使用。

deamonset模式

DaemonSet 方式在每个 node 节点上只运行一个日志 agent(filebeat,fluentd,flume,fluentbit),采集这个节点上所有的日志。DaemonSet 相对资源占用要小很多,但扩展性、租户隔离性受限,比较适用于功能单一或业务不是很多的集群;

正常规模的采集可以适应,日志分类明确、功能较单一的集群,大规模的集群采集速度就跟不上了,而且没有办法做到垂直扩展无上限。

当然完整的方案还是有很多需要做的工作,比如如何做发现,如何动态变更,这就是一个完成的平台建设了,这些每个公司都有自己的建设,就不太好说了。

sidecar模式

Sidecar 方式为每个 POD 单独部署日志 agent,这个 agent 只负责一个业务应用的日志采集。Sidecar 相对资源占用较多,但灵活性以及多租户隔离性较强,建议大型的 K8s 集群或作为 PaaS 平台为多个业务方服务的集群使用该方式。

适用于大型、混合型、PAAS型集群的日志采集,是一种水平扩展消耗更多资源来增加采集速度的方案,但是方案就比较复杂。

当然完整的方案还是有很多需要做的工作,比如如何做发现,如何动态变更,这就是一个完成的平台建设了,这些每个公司都有自己的建设,也就不太好说了。

网络采集性能数据

有赞

重flume发展到自研rsyslog-hub和http服务

17年平均每秒产生日志1.1万条,峰值1.5万条,每天的日志量约9亿条,占用空间2.4T左右

19年每天都会产生百亿级别的日志量(据统计,平均每秒产生 50 万条日志,峰值每秒可达 80 万条)

七牛云

自研logkit

17年现在日均数据流入量超 250 TB,3650 亿条,其中最大的客户日均数据流入量超过 45 TB。

b站

17年目前集群规模20台机器,接入业务200+,单日日志量10T+。

阿里云

自研logtail(重内核都得到的优化和充分利用)

速度达到160M/s