冷蟊初退 孤灯野澜 志起鸡鸣 墓不悲秋 技术交流 软件开发 商业合作 加Q:411239339

Day 20 Docker容器日志管理

浏览:10次阅读
没有评论

共计 5945 个字符,预计需要花费 15 分钟才能阅读完成。

Docker 30 天实战系列 · Day 20

半夜两点,线上服务突然告警,用户反馈登录失败。你火急火燎打开服务器,发现容器跑了二十几个,日志文件散落在各个角落,有的在 /var/log 里,有的在容器内部,有的干脆找不到。折腾了四十分钟才定位到问题——一个数据库连接池耗尽的报错,就静静躺在某个容器的标准输出里。

这种 " 出了事找不到日志 " 的窘境,相信不少人都经历过。今天我们就来彻底解决这个问题。

本文你将学到

  • docker logs 命令的各种实用技巧
  • Docker 日志驱动的工作原理和配置方法
  • json-file、syslog 等常用日志驱动的对比选择
  • 集中式日志方案的设计思路与实战搭建
  • 生产环境日志管理的最佳实践

阅读时间 : 约 12 分钟
实操时间 : 约 30 分钟
难度等级 : 中级


一、先搞明白:容器日志去哪了?

在聊具体命令之前,我们先理清一个基本问题——容器里的日志到底去哪了?

打个比方,传统部署就像你在自己家做饭,锅碗瓢盆都在固定位置,想找什么伸手就到。容器化部署则像是叫了一群厨师到不同的移动厨房里做菜,每个厨房都是独立的,你站在外面根本不知道里面发生了什么。

Docker 处理日志的方式其实很简单:

+------------------+
|    应用程序       |
|  stdout / stderr |
+--------+---------+
         |
         v
+--------+---------+
|   Docker Engine   |
|   (日志驱动)      |
+--------+---------+
         |
    +----+----+
    |         |
    v         v
 json-file  syslog  ... (其他驱动)

Docker 默认会捕获容器中进程写到 stdout(标准输出)和 stderr(标准错误)的内容,然后交给「日志驱动」来处理。你可以把日志驱动理解为一个 " 快递公司 ",负责把日志 " 包裹 " 送到指定的 " 目的地 "。

这就引出一个关键原则: 让你的应用把日志输出到 stdout/stderr,而不是写到文件里。 这样 Docker 才能接管日志的收集和分发。

二、docker logs:你的第一个调试工具

基本用法

最简单的查看日志方式:

# 启动一个测试容器
docker run -d --name log-test nginx:alpine

# 查看容器日志
docker logs log-test

预期输出类似:

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
...

实时追踪日志

调试的时候最常用的是 -f 参数,相当于 tail -f

# 实时追踪日志
docker logs -f log-test

然后开另一个终端访问一下这个 Nginx:

curl http://localhost:80

你会看到日志窗口实时蹦出一行访问记录。按 Ctrl+C 退出追踪。

按时间筛选

日志多了之后,全量查看就不现实了。Docker 提供了时间筛选:

# 查看最近 30 分钟的日志
docker logs --since 30m log-test

# 查看最近 100 行
docker logs --tail 100 log-test

# 查看某个时间段的日志
docker logs --since 2024-01-01T10:00:00 --until 2024-01-01T11:00:00 log-test

# 显示时间戳
docker logs -t log-test

组合使用

实际排障中,通常会组合使用:

# 显示最近 50 行并实时追踪,带时间戳
docker logs --tail 50 -f -t log-test

这大概是我用得最多的一条命令了,简单粗暴但非常实用。

三、日志驱动:给日志选个好 " 快递公司 "

什么是日志驱动?

Docker 的日志驱动决定了日志的存储方式和目的地。默认使用 json-file 驱动,把日志存成 JSON 格式的文件。但 Docker 支持十几种日志驱动,常用的有这几个:

驱动名称 存储位置 适用场景 docker logs 可用
json-file 本地 JSON 文件 开发 / 小规模部署
local 本地压缩文件 单机部署(性能更好)
syslog Syslog 服务 已有 syslog 基础设施
journald systemd journal Linux 系统集成
fluentd Fluentd 收集器 集中式日志方案
none 不保存 对日志无需求的容器

这里有个坑要特别注意: 除了 json-file、local 和 journald,其他驱动都不支持 docker logs 命令。 也就是说,如果你配了 syslog 驱动,再运行 docker logs 会直接报错。

查看当前日志驱动

# 查看 Docker 默认日志驱动
docker info --format '{{.LoggingDriver}}'

# 查看某个容器的日志驱动
docker inspect --format '{{.HostConfig.LogConfig.Type}}' log-test

预期输出:

json-file

配置 json-file 驱动(推荐起步方案)

json-file 是默认驱动,但默认配置有个致命问题—— 日志文件会无限增长 ,直到把磁盘撑爆。这就像家里的垃圾桶没人倒,迟早要溢出来。

给它加上大小限制:

# 单个容器指定日志选项
docker run -d --name log-limited \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx:alpine

这样每个日志文件最大 10MB,最多保留 3 个文件,总共最多 30MB。旧的日志文件会自动轮转删除。

要给所有容器设置默认值,编辑 Docker daemon 配置:

# 编辑 Docker 配置文件
cat > /etc/docker/daemon.json << 'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5",
    "compress": "true"
  }
}
EOF

# 重启 Docker 使配置生效(注意:会重启所有容器)sudo systemctl restart docker

注意: 修改 daemon.json 只对新创建的容器生效,已有容器不受影响。

配置 syslog 驱动

如果你的公司已经有 syslog 基础设施(比如 rsyslog),可以直接把容器日志接入:

# 发送到本地 syslog
docker run -d --name log-syslog \
  --log-driver=syslog \
  --log-opt syslog-address=udp://localhost:514 \
  --log-opt tag="myapp-{{.Name}}" \
  nginx:alpine

tag 选项很重要,它让你在 syslog 里能区分不同容器的日志。{{.Name}} 会自动替换为容器名。

关闭日志

有些容器确实不需要日志(比如纯计算任务),可以用 none 驱动:

docker run -d --name no-logs \
  --log-driver=none \
  alpine ping localhost

这样既不占磁盘也不消耗 IO,但出了问题你也没处查,慎用。

四、日志文件在哪?怎么清理?

找到日志文件

json-file 驱动的日志文件存在这里:

# 查看容器日志文件路径
docker inspect --format '{{.LogPath}}' log-test

输出类似:

/var/lib/docker/containers//-json.log

查看日志占用空间

# 查看所有容器的日志大小
docker ps -a --format '{{.Names}}' | while read name; do
  log_path=$(docker inspect --format '{{.LogPath}}' "$name" 2>/dev/null)
  if [-n "$log_path"] && [-f "$log_path"]; then
    size=$(du -sh "$log_path" | cut -f1)
    echo "$name: $size"
  fi
done

手动清理日志

如果日志已经很大了,可以手动清空(不是删除文件):

# 清空某个容器的日志(不要用 rm,会导致 Docker 无法写入)truncate -s 0 $(docker inspect --format '{{.LogPath}}' log-test)

但这只是应急手段,治本的方法还是配置 max-sizemax-file

五、集中式日志方案

当你管理的容器从几个变成几十上百个,在每台机器上一个个 docker logs 显然不现实。这时候就需要集中式日志方案。

架构概览

+----------+     +----------+     +----------+
| 容器 A   |     | 容器 B   |     | 容器 C   |
| stdout   |     | stdout   |     | stdout   |
+----+-----+     +----+-----+     +----+-----+
     |                |                |
     v                v                v
+------------------------------------------------+
|           日志收集层 (Fluentd / Filebeat)        |
+------------------------+-----------------------+
                         |
                         v
+------------------------+-----------------------+
|           日志存储层 (Elasticsearch / Loki)      |
+------------------------+-----------------------+
                         |
                         v
+------------------------+-----------------------+
|           日志展示层 (Kibana / Grafana)          |
+------------------------------------------------+

这就是经典的 EFK(Elasticsearch + Fluentd + Kibana)或 ELK(Elasticsearch + Logstash + Kibana)架构。

用 Docker Compose 搭建 EFK 方案

下面是一个可以直接跑起来的精简版:

# docker-compose-efk.yml
services:
  elasticsearch:
    image: elasticsearch:8.12.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"
    volumes:
      - es-data:/usr/share/elasticsearch/data

  kibana:
    image: kibana:8.12.0
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch

  fluentd:
    build:
      context: .
      dockerfile: Dockerfile.fluentd
    ports:
      - "24224:24224"
      - "24224:24224/udp"
    volumes:
      - ./fluentd/conf:/fluentd/etc
    depends_on:
      - elasticsearch

  # 示例应用:使用 fluentd 日志驱动
  webapp:
    image: nginx:alpine
    ports:
      - "8080:80"
    logging:
      driver: fluentd
      options:
        fluentd-address: localhost:24224
        tag: docker.webapp

volumes:
  es-data:

Fluentd 配置文件:

# fluentd/conf/fluent.conf

  @type forward
  port 24224
  bind 0.0.0.0



  @type elasticsearch
  host elasticsearch
  port 9200
  logstash_format true
  logstash_prefix docker
  include_tag_key true
  flush_interval 5s

轻量替代:Loki + Grafana

EFK 虽然强大,但资源消耗不小。如果你的规模不大,Loki 是个更轻量的选择:

# docker-compose-loki.yml
services:
  loki:
    image: grafana/loki:2.9.0
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    depends_on:
      - loki

  promtail:
    image: grafana/promtail:2.9.0
    volumes:
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock
    command: -config.file=/etc/promtail/config.yml
    depends_on:
      - loki

Loki 的哲学是 " 像 Prometheus,但用于日志 "——只索引标签,不索引全文,所以资源消耗远小于 Elasticsearch。

六、生产环境最佳实践

总结几条在生产中踩过坑之后的经验:

1. 一定要限制日志大小

{
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

我见过太多次磁盘被日志撑爆的事故了,这是最基础也最容易被忽略的配置。

2. 日志要结构化

{"time":"2024-01-15T10:30:00Z","level":"error","msg":"connection refused","service":"user-api","trace_id":"abc123"}

比起一行纯文本,结构化日志在检索和分析时效率高一个量级。

3. 合理使用日志级别

  • DEBUG:开发调试用,生产环境关掉
  • INFO:关键业务流程节点
  • WARN:可以容忍但需要关注的情况
  • ERROR:需要人工介入的问题

4. 加上请求追踪 ID

分布式系统里,一个请求可能经过多个服务。给每个请求分配一个唯一 ID,日志里带上它,排障时就能串联整个调用链。

5. 敏感信息脱敏

日志里千万不要出现密码、token、身份证号这些敏感信息。写日志前做好脱敏处理。

常见问题 Q&A

Q1: docker logs 显示的日志不全,是怎么回事?

大概率是日志驱动配了 max-sizemax-file,旧日志被轮转掉了。这是正常行为。如果需要长期保留日志,应该用集中式方案把日志发送到外部存储。

Q2: 容器重启后日志还在吗?

如果使用 json-file 驱动,容器 stop/start 后日志还在,但 docker rm 删除容器后日志也会被删除。如果容器是通过 docker run --rm 启动的,退出后日志也会消失。所以重要的日志一定要外发。

Q3: 应用日志写到文件里而不是 stdout,怎么办?

有两种方案:一是修改应用配置,让它输出到 stdout(推荐);二是如果实在改不了,可以在 Dockerfile 里用符号链接把日志文件指向 stdout:

RUN ln -sf /dev/stdout /var/log/app.log

Nginx 的官方镜像就是这么干的。

小结

今天我们从最基础的 docker logs 命令出发,一路聊到了日志驱动配置和集中式日志方案。核心要点回顾:

  • docker logs 是排障的第一选择,记住 -f--tail--since 这几个参数
  • 日志驱动决定了日志的去向,json-file 适合起步,生产环境考虑集中式方案
  • 一定要配置 max-sizemax-file,防止磁盘被撑爆
  • 集中式方案推荐 EFK 或 Loki + Grafana,根据规模选择
  • 应用日志输出到 stdout/stderr,让 Docker 来管理

日志管理看着不起眼,但它是你和线上系统之间的 " 眼睛 "。配好了平时感觉不到它的存在,出了问题它就是救命稻草。

明天是 Day 21 Docker 安全最佳实践 ,我们来聊聊怎么让你的容器环境更加安全可靠。容器安全这个话题,很多人觉得 " 我又不是搞安全的 ",但其实几个基础配置就能挡住绝大多数问题,明天见。


Docker 30 天实战系列 | Day 20 of 30

正文完
创作不易,扫码加点动力
post-qrcode
 0
果较瘦
版权声明:本站原创文章,由 果较瘦 于2026-03-29发表,共计5945字。
转载说明:除特殊说明外本站文章皆由果较瘦原创发布,转载请注明出处。