Docker 应用使用 Filebeat 和 Logstash 进行日志记录
Docker apps logging with Filebeat and Logstash
我有一组分散在多个服务器上的 dockerized 应用程序,并尝试使用 ELK 设置生产级集中日志记录。我对 ELK 部分本身没意见,但我对如何将日志转发到我的 logstashes 有点困惑。
我正在尝试使用 Filebeat,因为它具有负载平衡功能。
我还想避免将 Filebeat(或其他任何东西)打包到我所有的 docker 中,并将其分开,是否已 dockerized。
我该如何继续?
我一直在尝试以下方法。我的 Dockers 登录 stdout,因此使用非 dockerized Filebeat 配置为从 stdin 读取我这样做:
docker logs -f mycontainer | ./filebeat -e -c filebeat.yml
这似乎在开始时有效。第一个日志被转发到我的 logstash。我猜是缓存的。但在某些时候它卡住了并继续发送相同的事件
这只是一个错误还是我走错了方向?您设置了什么解决方案?
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'
这是将 docker logs
转发到 ELK 堆栈的一种方法(需要 docker >= 1.8 用于 gelf 日志驱动程序):
使用 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 shows that some handy fields are generated for you (hat-tip: 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"
使用 filebeat,您可以按照您的描述通过管道传输 docker logs
输出。您看到的行为听起来确实像是一个错误,但也可能是部分行读取配置击中了您(重新发送部分行直到找到换行符)。
我看到的一个管道问题是在没有 logstash 可用的情况下可能存在背压。如果 filebeat 无法发送任何事件,它将在内部缓冲事件并在某个时候停止从标准输入读取。不知道 how/if docker 可以防止 stdout 变得无响应。如果您使用 docker-compose,管道的另一个问题可能是 filebeat + docker 的重启行为。 docker-compose 默认重用图像 + 图像状态。因此,当您重新启动时,您将再次传送所有旧日志(假设底层日志文件尚未轮换)。
您可以尝试将 docker 写入的日志文件读取到主机系统,而不是管道。默认 docker 日志驱动程序是 json log driver 。您可以而且应该配置 json 日志驱动程序来执行日志轮换 + 保留一些旧文件(用于在磁盘上缓冲)。请参阅最大大小和最大文件选项。 json 驱动程序为要记录的每一行放置一行 'json' 数据。在 docker 主机系统上,日志文件被写入 /var/lib/docker/containers/container_id/container_id-json.log 。这些文件将由 filebeat 转发到 logstash。如果 logstash 或网络变得不可用或 filebeat 重新启动,它会继续转发它离开的日志行(给定的文件由于日志轮换而未被删除)。不会丢失任何事件。在 logstash 中,您可以使用 json_lines 编解码器或过滤器来解析 json 行,并使用 grok 过滤器从您的日志中获取更多信息。
将来有 some discussion about using libbeat (used by filebeat for shipping log files) to add a new log driver to docker. Maybe it is possible to collect logs via dockerbeat 通过使用 docker 日志 api(我不知道关于使用日志 api 的任何计划,尽管).
使用系统日志也是一种选择。也许您可以在 docker 主机负载平衡日志事件上获得一些系统日志中继。或者让syslog写日志文件,用filebeat转发。我认为 rsyslog 至少有一些故障转移模式。您可以使用 logstash 系统日志输入插件和 rsyslog 将日志转发到具有故障转移支持的 logstash,以防活动的 logstash 实例不可用。
我使用 Docker API 创建了自己的 docker 映像,以收集机器上容器 运行 的日志,并借助 Filebeat 将它们发送到 Logstash .无需在主机上安装或配置任何东西。
查看并告诉我它是否符合您的需求:https://hub.docker.com/r/bargenson/filebeat/。
只是为了帮助其他需要这样做的人,您可以简单地使用 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" }
}
}
}
convert/renames 是可选的,但修复了 COMBINEDAPACHELOG
表达式中的一个疏忽,即它不会将这些值转换为整数,从而使它们无法在 Kibana 中聚合。
我在评论中验证了 erewok 上面写的内容:
According to the docs, you should be able to use a pattern like this
in your prospectors.paths: /var/lib/docker/containers/*/*.log – erewok
Apr 18 at 21:03
docker 容器 guid,表示为第一个 '*',在 filebeat 启动时被正确解析。我不知道添加容器时会发生什么。
我有一组分散在多个服务器上的 dockerized 应用程序,并尝试使用 ELK 设置生产级集中日志记录。我对 ELK 部分本身没意见,但我对如何将日志转发到我的 logstashes 有点困惑。 我正在尝试使用 Filebeat,因为它具有负载平衡功能。 我还想避免将 Filebeat(或其他任何东西)打包到我所有的 docker 中,并将其分开,是否已 dockerized。
我该如何继续?
我一直在尝试以下方法。我的 Dockers 登录 stdout,因此使用非 dockerized Filebeat 配置为从 stdin 读取我这样做:
docker logs -f mycontainer | ./filebeat -e -c filebeat.yml
这似乎在开始时有效。第一个日志被转发到我的 logstash。我猜是缓存的。但在某些时候它卡住了并继续发送相同的事件
这只是一个错误还是我走错了方向?您设置了什么解决方案?
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'
这是将 docker logs
转发到 ELK 堆栈的一种方法(需要 docker >= 1.8 用于 gelf 日志驱动程序):
使用 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 shows that some handy fields are generated for you (hat-tip: 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"
使用 filebeat,您可以按照您的描述通过管道传输 docker logs
输出。您看到的行为听起来确实像是一个错误,但也可能是部分行读取配置击中了您(重新发送部分行直到找到换行符)。
我看到的一个管道问题是在没有 logstash 可用的情况下可能存在背压。如果 filebeat 无法发送任何事件,它将在内部缓冲事件并在某个时候停止从标准输入读取。不知道 how/if docker 可以防止 stdout 变得无响应。如果您使用 docker-compose,管道的另一个问题可能是 filebeat + docker 的重启行为。 docker-compose 默认重用图像 + 图像状态。因此,当您重新启动时,您将再次传送所有旧日志(假设底层日志文件尚未轮换)。
您可以尝试将 docker 写入的日志文件读取到主机系统,而不是管道。默认 docker 日志驱动程序是 json log driver 。您可以而且应该配置 json 日志驱动程序来执行日志轮换 + 保留一些旧文件(用于在磁盘上缓冲)。请参阅最大大小和最大文件选项。 json 驱动程序为要记录的每一行放置一行 'json' 数据。在 docker 主机系统上,日志文件被写入 /var/lib/docker/containers/container_id/container_id-json.log 。这些文件将由 filebeat 转发到 logstash。如果 logstash 或网络变得不可用或 filebeat 重新启动,它会继续转发它离开的日志行(给定的文件由于日志轮换而未被删除)。不会丢失任何事件。在 logstash 中,您可以使用 json_lines 编解码器或过滤器来解析 json 行,并使用 grok 过滤器从您的日志中获取更多信息。
将来有 some discussion about using libbeat (used by filebeat for shipping log files) to add a new log driver to docker. Maybe it is possible to collect logs via dockerbeat 通过使用 docker 日志 api(我不知道关于使用日志 api 的任何计划,尽管).
使用系统日志也是一种选择。也许您可以在 docker 主机负载平衡日志事件上获得一些系统日志中继。或者让syslog写日志文件,用filebeat转发。我认为 rsyslog 至少有一些故障转移模式。您可以使用 logstash 系统日志输入插件和 rsyslog 将日志转发到具有故障转移支持的 logstash,以防活动的 logstash 实例不可用。
我使用 Docker API 创建了自己的 docker 映像,以收集机器上容器 运行 的日志,并借助 Filebeat 将它们发送到 Logstash .无需在主机上安装或配置任何东西。
查看并告诉我它是否符合您的需求:https://hub.docker.com/r/bargenson/filebeat/。
只是为了帮助其他需要这样做的人,您可以简单地使用 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" }
}
}
}
convert/renames 是可选的,但修复了 COMBINEDAPACHELOG
表达式中的一个疏忽,即它不会将这些值转换为整数,从而使它们无法在 Kibana 中聚合。
我在评论中验证了 erewok 上面写的内容:
According to the docs, you should be able to use a pattern like this in your prospectors.paths: /var/lib/docker/containers/*/*.log – erewok Apr 18 at 21:03
docker 容器 guid,表示为第一个 '*',在 filebeat 启动时被正确解析。我不知道添加容器时会发生什么。