使用 Filebeat 和 Logstash 记录 Docker 应用程序
Posted
技术标签:
【中文标题】使用 Filebeat 和 Logstash 记录 Docker 应用程序【英文标题】:Docker apps logging with Filebeat and Logstash 【发布时间】:2016-01-30 16:50:21 【问题描述】:我有一组分散在多个服务器上的 dockerized 应用程序,并尝试使用 ELK 设置生产级集中日志记录。我对 ELK 部分本身没意见,但我对如何将日志转发到我的日志存储区有点困惑。 我正在尝试使用 Filebeat,因为它具有负载平衡功能。 我还想避免将 Filebeat(或其他任何东西)打包到我所有的 docker 中,并保持分开,不管是否 docker 化。
我该如何继续?
我一直在尝试以下方法。我的 Docker 登录标准输出,因此我使用配置为从标准输入读取的非 dockerized Filebeat:
docker 日志 -f mycontainer | ./filebeat -e -c filebeat.yml
这似乎在一开始就有效。第一个日志被转发到我的logstash。我猜是缓存的。但在某些时候它会卡住并继续发送相同的事件
这只是一个错误还是我走错了方向?你设置了什么解决方案?
【问题讨论】:
我刚刚用旧的logstash-forwarder尝试了同样的事情:docker logs -f mycontainer | ./logstash-forwarder_linux_amd64 -config forwarder.conf 它可以工作。我怀疑 Filebeat 的错误。唯一的问题仍然是与没有负载平衡的 logstash 的随机连接。 您使用的是哪个版本的 filebeat?这看起来像一个潜在的错误。随时打开问题here,以便我们更深入地研究问题。供参考:关于 docker 实现的一些额外讨论可以在这里找到:github.com/elastic/libbeat/issues/37 【参考方案1】:这是将docker logs
转发到 ELK 堆栈的一种方法(gelf 日志驱动程序需要 docker >= 1.8):
使用gelf input plugin 启动一个 Logstash 容器,以从 gelf 读取数据并输出到 Elasticsearch 主机 (ES_HOST:port):
docker run --rm -p 12201:12201/udp logstash \
logstash -e 'input gelf output elasticsearch hosts => ["ES_HOST:PORT"] '
现在启动一个 Docker 容器并使用gelf Docker logging driver。这是一个愚蠢的例子:
docker run --log-driver=gelf --log-opt gelf-address=udp://localhost:12201 busybox \
/bin/sh -c 'while true; do echo "Hello $(date)"; sleep 1; done'
加载 Kibana,本应登陆 docker logs
的内容现在可见。 gelf source code 显示为您生成了一些方便的字段(帽子提示:Christophe Labouisse):_container_id
、_container_name
、_image_id
、_image_name
、_command
、_tag
、_created
.
如果您使用 docker-compose(确保使用 docker-compose >= 1.5)并在启动 logstash 容器后在 docker-compose.yml
中添加适当的设置:
log_driver: "gelf"
log_opt:
gelf-address: "udp://localhost:12201"
【讨论】:
我认为 gelf 的问题在于它使用 udp 并且可能会默默地丢弃日志事件。 好点,@urso。syslog
日志记录驱动程序可以以类似的方式用于通过 TCP here's an example 传递日志。 Graylog Extended Format (GELF) docs 提到了使用 TCP 与 UDP 静默丢弃日志事件相比的潜在问题。
这篇文章解释了 gelf (UDP 和 TCP) 的问题:claudiokuenzler.com/blog/845/…【参考方案2】:
Docker 允许您指定使用中的logDriver。这个答案不关心 Filebeat 或负载平衡。
在一次演示中,我使用 syslog 将日志转发到侦听端口 5000 的 Logstash (ELK) 实例。 以下命令不断通过 syslog 向 Logstash 发送消息:
docker run -t -d --log-driver=syslog --log-opt syslog-address=tcp://127.0.0.1:5000 ubuntu /bin/bash -c 'while true; do echo "Hello $(date)"; sleep 1; done'
【讨论】:
我查看了 logDriver,但是高可用性呢?我需要 TCP 负载平衡器来将请求路由到我的 logstash 集群吗? 我不确定您的系统规模。使用〜200个日志生产者(我的答案中的命令)我没有发现任何问题。但是我没有考虑高可用性或负载平衡/集群。 我并不关心日志的数量,而是logstash的可用性。我需要至少 2 或 3 个来确保良好的容错性,然后需要一些机制来从一个切换到另一个。 好吧.. 我帮不了你。但我会留下我的解决方案来帮助别人。也许您想指出问题中的负载平衡。 使用 docker swarm 或 kubernetes 应该可以解决您的负载平衡问题。在 docker swarm 中,您指定哪个服务应该接收消息,然后 docker 转发到其中一个副本(可以是循环或其他可能性)【参考方案3】:使用 filebeat,您可以将docker logs
输出如您所描述的那样。您看到的行为绝对听起来像一个错误,但也可能是部分行读取配置对您造成影响(重新发送部分行,直到找到换行符)。
如果没有可用的 logstash,我看到的管道问题可能是背压。如果 filebeat 无法发送任何事件,它将在内部缓冲事件,并在某个时候停止从标准输入读取。不知道 docker 如何/是否防止标准输出变得无响应。如果您使用的是 docker-compose,管道的另一个问题可能是 filebeat + docker 的重启行为。 docker-compose 默认重用图像 + 图像状态。因此,当您重新启动时,您将再次发送所有旧日志(假设底层日志文件尚未轮换)。
您可以尝试读取 docker 写入主机系统的日志文件,而不是管道。默认的 docker 日志驱动程序是 json log driver 。您可以并且应该配置 json 日志驱动程序来执行日志轮换 + 保留一些旧文件(用于在磁盘上缓冲)。请参阅 max-size 和 max-file 选项。 json 驱动程序为要记录的每一行放置一行“json”数据。在 docker 主机系统上,日志文件被写入 /var/lib/docker/containers/container_id/container_id-json.log 。这些文件将通过 filebeat 转发到 logstash。如果 logstash 或网络变得不可用或 filebeat 重新启动,它会继续转发它离开的日志行(给定文件由于日志轮换而没有被删除)。不会丢失任何事件。在 logstash 中,您可以使用 json_lines 编解码器或过滤器来解析 json 行,并使用 grok 过滤器从日志中获取更多信息。
some discussion 关于使用 libbeat(由 filebeat 用于传送日志文件)向 docker 添加新的日志驱动程序。也许将来可以使用 docker logs api 通过dockerbeat 收集日志(不过,我不知道有任何关于使用日志 api 的计划)。
使用系统日志也是一种选择。也许你可以在你的 docker 主机负载平衡日志事件上获得一些 syslog 中继。或者让 syslog 写入日志文件并使用 filebeat 转发它们。我认为 rsyslog 至少有一些故障转移模式。您可以使用 logstash syslog 输入插件和 rsyslog 将日志转发到具有故障转移支持的 logstash,以防活动的 logstash 实例不可用。
【讨论】:
Re json-file, github.com/moby/moby/issues/17763 表示docker json-files被认为是内部数据,不打算被其他进程使用。【参考方案4】:借助 Filebeat,我使用 Docker API 创建了自己的 docker 镜像来收集机器上运行的容器的日志并将它们发送到 Logstash。无需在主机上安装或配置任何东西。
检查一下并告诉我它是否适合您的需求:https://hub.docker.com/r/bargenson/filebeat/。
代码可在此处获得:https://github.com/bargenson/docker-filebeat
【讨论】:
【参考方案5】:只是为了帮助其他需要这样做的人,您可以简单地使用 Filebeat 来发送日志。我会使用@brice-argenson 的容器,但我需要 SSL 支持,所以我使用了本地安装的 Filebeat 实例。
filebeat 的探矿者是(重复更多容器):
- input_type: log
paths:
- /var/lib/docker/containers/<guid>/*.log
document_type: docker_log
fields:
dockercontainer: container_name
您需要知道 GUID 有点糟糕,因为它们可能会在更新时发生变化。
在 logstash 服务器上,为 logstash 设置通常的 filebeat 输入源,并使用如下过滤器:
filter
if [type] == "docker_log"
json
source => "message"
add_field => [ "received_at", "%@timestamp" ]
add_field => [ "received_from", "%host" ]
mutate
rename => "log" => "message"
date
match => [ "time", "ISO8601" ]
这将从 Docker 日志中解析 JSON,并将时间戳设置为 Docker 报告的时间戳。
如果你是从 nginx Docker 镜像中读取日志,你也可以添加这个过滤器:
filter
if [fields][dockercontainer] == "nginx"
grok
match => "message" => "(?m)%IPORHOST:targethost %COMBINEDAPACHELOG"
mutate
convert => "[bytes]" => "integer"
convert => "[response]" => "integer"
mutate
rename => "bytes" => "http_streamlen"
rename => "response" => "http_statuscode"
转换/重命名是可选的,但修复了 COMBINEDAPACHELOG
表达式中的一个疏忽,它不会将这些值转换为整数,从而使它们无法在 Kibana 中进行聚合。
【讨论】:
谢谢!关于您关于 GUID 的提示,我同意,但是您可能不想手动进行这样的配置,而是使用 Ansible 之类的东西。然后只需“docker ps | grep container_name | awk 'print $1'”,然后将结果模板化到配置中并重新启动 filebeat。 根据文档,您应该可以在您的prospectors.paths 中使用这样的模式:/var/lib/docker/containers/*/*.log
【参考方案6】:
我在评论中验证了 erewok 上面写的内容:
根据文档,您应该能够使用这样的模式 在你的prospectors.paths:/var/lib/docker/containers/*/*.log – erewok 4月18日 21:03
以第一个“*”表示的 docker 容器 guid 在 filebeat 启动时被正确解析。我不知道添加容器时会发生什么。
【讨论】:
以上是关于使用 Filebeat 和 Logstash 记录 Docker 应用程序的主要内容,如果未能解决你的问题,请参考以下文章