Linux 容器日志收集与监控实践

1次阅读

容器日志默认输出到/var/lib/docker/containers//-json.log;docker logs查不到新日志主因是应用重定向日志、json日志轮转或容器重启导致文件丢失。

Linux 容器日志收集与监控实践

容器日志默认输出到哪里?docker logs 为什么查不到新日志?

Docker 容器默认把 stdoutstderr 直接写入本地 JSON 文件(路径类似 /var/lib/docker/containers/<container-id>/<container-id>-json.log</container-id></container-id>),docker logs 就是读这个文件。但问题常出在:

  • 应用自己重定向了日志(比如写了 /var/log/app.log),那 docker logs 根本看不到
  • 日志轮转后旧文件被删,而 docker logs --since 只能查 JSON 日志的元数据时间,不保证文件还存在
  • 容器重启过,旧日志文件被清理(取决于 log-driver 配置)

实操建议:

  • 先确认应用是否真的往 stdout 输出:进容器执行 ls -l /proc/1/fd/{1,2},看是否指向 pipe:socket:,而不是文件
  • 查看当前日志驱动:docker inspect <container-id> | jq '.HostConfig.LogConfig.Type'</container-id>,默认是 json-file,但生产环境建议切到 syslogfluentd
  • 不要依赖 docker logs 做长期归档——它没压缩、没索引、不支持按字段过滤

怎么让容器日志自动发到 elk 或 Loki?fluentdfilebeat 选哪个?

直接挂载宿主机日志目录再用 filebeat 采集,看似简单,但容易漏日志(容器启动/销毁瞬间)、权限错乱、路径硬编码。更稳的做法是让 Docker 自己把日志转发出去。

fluentd 适合已有 ruby 生态或需要复杂过滤(比如动态提取 level 字段、改写 message);filebeat 更轻量、资源占用低、原生支持 docker 模块自动识别容器元数据(container.id, image 等)。

实操建议:

  • 给 Docker daemon 配 log-driver: "fluentd""filebeat"(需先启对应服务)
  • fluentd 配置里必须设 @id docker-logs 并启用 buffer,否则容器高频打日志时会丢
  • filebeat 的 docker.input 模块默认只读 json-file,如果用了 journald 驱动,得换用 journald 输入类型
  • 所有转发链路都要加 TLS 和重试:filebeat 到 Logstash、Logstash 到 ES/Loki,任何一环断开都可能丢日志

docker logs --tail 100 很慢?JSON 日志文件太大怎么优化?

docker logs --tail N 是从 JSON 日志文件末尾往前逐行扫描,不是随机读。文件超 1GB 后,哪怕只 tail 10 行也可能卡几秒——因为 Docker 要解析每行 JSON 的 timestamp 字段来倒序定位。

根本原因有两个:

  • 日志没分级(所有 info/warn/Error 混在一起),导致体积膨胀
  • JSON 日志没压缩,且 Docker 默认不轮转(max-sizemax-file 都是 10M/3 个,远不够)

实操建议:

  • 启动容器时强制限制日志大小:--log-opt max-size=50m --log-opt max-file=5
  • 如果用 json-file 驱动,加 --log-opt labels=env,service 把标签写进日志,方便后续过滤
  • 禁用日志时间戳(--log-opt tag="{{.ImageName}}/{{.Name}}"),避免重复记录时间——应用自己打的时间更准
  • 不要用 docker logs -f 在生产环境实时盯屏,它会持续 hold 文件句柄,影响 logrotate

Loki 查询慢、rate() 计算不准?容器日志的 label 设计很关键

Loki 不存日志内容,只存 label + 时间戳 + 流(stream)。查询性能和聚合准确度,几乎全靠 label 是否合理。常见坑是:

  • 所有容器共用一个 job="docker",导致 {job="docker"} 匹配几百万流,查 rate({job="docker"}[5m]) 直接 OOM
  • 用容器 ID 当 container label,每次重建就变,无法关联历史
  • 忘记加 Namespacepod(K8s 场景),导致跨环境日志混在一起

实操建议:

  • Docker 场景下,label 至少包含:job(按业务域分,如 api, worker)、envprod/staging)、service(来自 --label service=auth-api
  • K8s 场景下,用 Promtail 的 kubernetes 插件自动注入 namespace, pod_name, container_name,别手动写死
  • 避免用高基数 label:比如 request_iduser_id,Loki 会为每个唯一值建索引,吃光内存

日志监控不是“通了就行”,label 结构、日志源头控制、转发链路容错——这三块没对齐,后面查问题永远慢半拍。

text=ZqhQzanResources