找回密码
 立即注册

QQ登录

只需一步,快速开始

peUfSYR.png
查看: 45|回复: 0

Day 20 Docker容器日志管理

[复制链接]

876

主题

13

回帖

2808

积分

管理员

积分
2808
发表于 2026-3-29 16:17:15 | 显示全部楼层 |阅读模式
Docker 30天实战系列 · Day 20

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

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

本文你将学到

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

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

    ━━━━━━━━━━━━━━━━━━━━

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

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

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

    Docker 处理日志的方式其实很简单:
    1. +------------------+
    2. |    应用程序       |
    3. |  stdout / stderr |
    4. +--------+---------+
    5.          |
    6.          v
    7. +--------+---------+
    8. |   Docker Engine   |
    9. |   (日志驱动)      |
    10. +--------+---------+
    11.          |
    12.     +----+----+
    13.     |         |
    14.     v         v
    15. json-file  syslog  ... (其他驱动)
    复制代码

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

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

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

    基本用法

    最简单的查看日志方式:
    1. # 启动一个测试容器
    2. docker run -d --name log-test nginx:alpine
    3. # 查看容器日志
    4. docker logs log-test
    复制代码

    预期输出类似:
    1. /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    2. /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    3. ...
    复制代码

    实时追踪日志

    调试的时候最常用的是
    1. -f
    复制代码
    参数,相当于
    1. tail -f
    复制代码

    1. # 实时追踪日志
    2. docker logs -f log-test
    复制代码

    然后开另一个终端访问一下这个 Nginx:
    1. curl http://localhost:80
    复制代码

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

    按时间筛选

    日志多了之后,全量查看就不现实了。Docker 提供了时间筛选:
    1. # 查看最近 30 分钟的日志
    2. docker logs --since 30m log-test
    3. # 查看最近 100 行
    4. docker logs --tail 100 log-test
    5. # 查看某个时间段的日志
    6. docker logs --since 2024-01-01T10:00:00 --until 2024-01-01T11:00:00 log-test
    7. # 显示时间戳
    8. docker logs -t log-test
    复制代码

    组合使用

    实际排障中,通常会组合使用:
    1. # 显示最近 50 行并实时追踪,带时间戳
    2. docker logs --tail 50 -f -t log-test
    复制代码

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

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

    什么是日志驱动?

    Docker 的日志驱动决定了日志的存储方式和目的地。默认使用
    1. 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 驱动,再运行
    1. docker logs
    复制代码
    会直接报错。

    查看当前日志驱动
    1. # 查看 Docker 默认日志驱动
    2. docker info --format '{{.LoggingDriver}}'
    3. # 查看某个容器的日志驱动
    4. docker inspect --format '{{.HostConfig.LogConfig.Type}}' log-test
    复制代码

    预期输出:
    1. json-file
    复制代码

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

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

    给它加上大小限制:
    1. # 单个容器指定日志选项
    2. docker run -d --name log-limited \
    3.   --log-opt max-size=10m \
    4.   --log-opt max-file=3 \
    5.   nginx:alpine
    复制代码

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

    要给所有容器设置默认值,编辑 Docker daemon 配置:
    1. # 编辑 Docker 配置文件
    2. cat > /etc/docker/daemon.json << 'EOF'
    3. {
    4.   "log-driver": "json-file",
    5.   "log-opts": {
    6.     "max-size": "10m",
    7.     "max-file": "5",
    8.     "compress": "true"
    9.   }
    10. }
    11. EOF
    12. # 重启 Docker 使配置生效(注意:会重启所有容器)
    13. sudo systemctl restart docker
    复制代码

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

    配置 syslog 驱动

    如果你的公司已经有 syslog 基础设施(比如 rsyslog),可以直接把容器日志接入:
    1. # 发送到本地 syslog
    2. docker run -d --name log-syslog \
    3.   --log-driver=syslog \
    4.   --log-opt syslog-address=udp://localhost:514 \
    5.   --log-opt tag="myapp-{{.Name}}" \
    6.   nginx:alpine
    复制代码
    1. tag
    复制代码
    选项很重要,它让你在 syslog 里能区分不同容器的日志。
    1. {{.Name}}
    复制代码
    会自动替换为容器名。

    关闭日志

    有些容器确实不需要日志(比如纯计算任务),可以用
    1. none
    复制代码
    驱动:
    1. docker run -d --name no-logs \
    2.   --log-driver=none \
    3.   alpine ping localhost
    复制代码

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

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

    找到日志文件

    json-file 驱动的日志文件存在这里:
    1. # 查看容器日志文件路径
    2. docker inspect --format '{{.LogPath}}' log-test
    复制代码

    输出类似:
    1. /var/lib/docker/containers/<container-id>/<container-id>-json.log
    复制代码

    查看日志占用空间
    1. # 查看所有容器的日志大小
    2. docker ps -a --format '{{.Names}}' | while read name; do
    3.   log_path=$(docker inspect --format '{{.LogPath}}' "$name" 2>/dev/null)
    4.   if [ -n "$log_path" ] && [ -f "$log_path" ]; then
    5.     size=$(du -sh "$log_path" | cut -f1)
    6.     echo "$name: $size"
    7.   fi
    8. done
    复制代码

    手动清理日志

    如果日志已经很大了,可以手动清空(不是删除文件):
    1. # 清空某个容器的日志(不要用 rm,会导致 Docker 无法写入)
    2. truncate -s 0 $(docker inspect --format '{{.LogPath}}' log-test)
    复制代码

    但这只是应急手段,治本的方法还是配置
    1. max-size
    复制代码
    1. max-file
    复制代码


    五、集中式日志方案

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

    架构概览
    1. +----------+     +----------+     +----------+
    2. | 容器 A   |     | 容器 B   |     | 容器 C   |
    3. | stdout   |     | stdout   |     | stdout   |
    4. +----+-----+     +----+-----+     +----+-----+
    5.      |                |                |
    6.      v                v                v
    7. +------------------------------------------------+
    8. |           日志收集层 (Fluentd / Filebeat)        |
    9. +------------------------+-----------------------+
    10.                          |
    11.                          v
    12. +------------------------+-----------------------+
    13. |           日志存储层 (Elasticsearch / Loki)      |
    14. +------------------------+-----------------------+
    15.                          |
    16.                          v
    17. +------------------------+-----------------------+
    18. |           日志展示层 (Kibana / Grafana)          |
    19. +------------------------------------------------+
    复制代码

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

    用 Docker Compose 搭建 EFK 方案

    下面是一个可以直接跑起来的精简版:
    1. # docker-compose-efk.yml
    2. services:
    3.   elasticsearch:
    4.     image: elasticsearch:8.12.0
    5.     environment:
    6.       - discovery.type=single-node
    7.       - xpack.security.enabled=false
    8.       - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    9.     ports:
    10.       - "9200:9200"
    11.     volumes:
    12.       - es-data:/usr/share/elasticsearch/data
    13.   kibana:
    14.     image: kibana:8.12.0
    15.     ports:
    16.       - "5601:5601"
    17.     environment:
    18.       - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    19.     depends_on:
    20.       - elasticsearch
    21.   fluentd:
    22.     build:
    23.       context: .
    24.       dockerfile: Dockerfile.fluentd
    25.     ports:
    26.       - "24224:24224"
    27.       - "24224:24224/udp"
    28.     volumes:
    29.       - ./fluentd/conf:/fluentd/etc
    30.     depends_on:
    31.       - elasticsearch
    32.   # 示例应用:使用 fluentd 日志驱动
    33.   webapp:
    34.     image: nginx:alpine
    35.     ports:
    36.       - "8080:80"
    37.     logging:
    38.       driver: fluentd
    39.       options:
    40.         fluentd-address: localhost:24224
    41.         tag: docker.webapp
    42. volumes:
    43.   es-data:
    复制代码

    Fluentd 配置文件:
    1. # fluentd/conf/fluent.conf
    2. <source>
    3.   @type forward
    4.   port 24224
    5.   bind 0.0.0.0
    6. </source>
    7. <match docker.**>
    8.   @type elasticsearch
    9.   host elasticsearch
    10.   port 9200
    11.   logstash_format true
    12.   logstash_prefix docker
    13.   include_tag_key true
    14.   flush_interval 5s
    15. </match>
    复制代码

    轻量替代:Loki + Grafana

    EFK 虽然强大,但资源消耗不小。如果你的规模不大,Loki 是个更轻量的选择:
    1. # docker-compose-loki.yml
    2. services:
    3.   loki:
    4.     image: grafana/loki:2.9.0
    5.     ports:
    6.       - "3100:3100"
    7.     command: -config.file=/etc/loki/local-config.yaml
    8.   grafana:
    9.     image: grafana/grafana:latest
    10.     ports:
    11.       - "3000:3000"
    12.     environment:
    13.       - GF_SECURITY_ADMIN_PASSWORD=admin
    14.     depends_on:
    15.       - loki
    16.   promtail:
    17.     image: grafana/promtail:2.9.0
    18.     volumes:
    19.       - /var/lib/docker/containers:/var/lib/docker/containers:ro
    20.       - /var/run/docker.sock:/var/run/docker.sock
    21.     command: -config.file=/etc/promtail/config.yml
    22.     depends_on:
    23.       - loki
    复制代码

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

    六、生产环境最佳实践

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

    1. 一定要限制日志大小
    1. {
    2.   "log-opts": {
    3.     "max-size": "10m",
    4.     "max-file": "5"
    5.   }
    6. }
    复制代码

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

    2. 日志要结构化
    1. {"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 显示的日志不全,是怎么回事?

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

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

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

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

    有两种方案:一是修改应用配置,让它输出到 stdout(推荐);二是如果实在改不了,可以在 Dockerfile 里用符号链接把日志文件指向 stdout:
    1. RUN ln -sf /dev/stdout /var/log/app.log
    复制代码

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

    小结

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

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

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

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

    ━━━━━━━━━━━━━━━━━━━━
    Docker 30天实战系列 | Day 20 of 30
  • 您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    扫码关注微信公众号

    Archiver|手机版|小黑屋|风叶林

    GMT+8, 2026-5-8 21:41 , Processed in 0.160951 second(s), 20 queries .

    Powered by 风叶林

    © 2001-2026 Discuz! Team.

    快速回复 返回顶部 返回列表