12. Docker 日志管理与监控:ELK 日志收集 + Prometheus 性能监控
容器跑着跑着突然挂了,你慌忙打开终端------日志在哪看?10 个容器同时出问题,你要一个一个
docker logs去翻?服务器 CPU 飙到 90%,到底是哪个容器在作妖?搞容器,不看日志和监控,就等于闭着眼睛开车。 本文手把手带你搭建两套"生产级"方案:ELK 集中收集日志 + Prometheus/Grafana 实时监控容器性能,看完你就是团队里的"容器运维担当"。

▲ 不看日志和监控,等于闭着眼睛开车;这套组合拳让你对集群里每个容器的状况一目了然。
一、Docker 日志基础:你至少得会这几招
1.1 docker logs 命令速查
这是最基础、用得最多的命令,先把常用参数记牢:
# 查看容器全部日志
docker logs my-container
# 实时跟踪日志输出(类似 tail -f)
docker logs -f my-container
# 只看最后 100 行
docker logs --tail 100 my-container
# 显示时间戳
docker logs -t my-container
# 查看某个时间点之后的日志
docker logs --since "2025-01-01T00:00:00" my-container
# 组合使用:实时跟踪 + 只看最后 50 行 + 带时间戳
docker logs -f --tail 50 -t my-container
小技巧 :排查问题时,先用
--tail看最近的日志,别一上来就全量输出------容器跑了几天的话,日志量可能有好几个 G。
1.2 日志驱动:Docker 的日志往哪存?
Docker 支持多种日志驱动(Logging Driver),决定了容器日志的"归宿":
| 日志驱动 | 存储位置 | 适用场景 | 优缺点 |
|---|---|---|---|
| json-file(默认) | 容器宿主机本地 JSON 文件 | 单机开发/测试 | ✅ 简单直接 ❌ 不支持集中收集,日志文件会越来越大 |
| syslog | 系统 syslog 服务 | 已有 syslog 基础设施的环境 | ✅ 复用现有系统 ❌ 配置略复杂 |
| journald | systemd journal | CentOS/RHEL 等 systemd 系统 | ✅ 与系统日志统一管理 ❌ 仅 Linux |
| gelf | Graylog / Logstash 等 | 需要集中收集日志 | ✅ 原生支持远程发送 ❌ 需要额外部署接收端 |
| fluentd | Fluentd 聚合器 | 大规模日志收集 | ✅ 功能强大、插件丰富 ❌ 部署复杂 |
| none | 不记录任何日志 | 安全敏感或性能极致场景 | ✅ 零开销 ❌ 出问题了完全没线索 |
查看当前容器的日志驱动:
docker inspect my-container --format='{{.HostConfig.LogConfig.Type}}'
# 输出:json-file
1.3 日志轮转:别让磁盘被日志撑爆
默认的 json-file 驱动会把日志一直往文件里追加,不做任何轮转。跑上几个月,一个容器的日志文件轻松突破几个 G,最终把磁盘吃满,整个服务器宕机------这是生产环境最常见的"低级故障"之一。
解决方案:在 Docker 的 daemon 配置里全局设置日志轮转:
编辑 /etc/docker/daemon.json(没有就创建):
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
| 参数 | 含义 | 推荐值 |
|---|---|---|
max-size |
单个日志文件的最大大小 | 50m ~ 100m |
max-file |
最多保留几个日志文件 | 3 ~ 5 |
上面这个配置的意思是:单个日志文件最大 50MB,保留最近 3 份,超过后自动轮转删除旧的。最多占 50×3=150MB 磁盘空间。
配置完重启 Docker 生效:
sudo systemctl restart docker
注意 :
daemon.json的全局配置只对新建容器 生效。已运行的容器需要删除重建,或者在docker run/docker-compose.yml里单独指定log-opts。
在 docker-compose.yml 里单独配置:
services:
my-app:
image: my-app:latest
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
二、docker stats:一秒看清容器资源消耗
在搭建重量级监控之前,先认识一个轻量级但超实用的命令:
# 查看所有运行中容器的资源使用情况
docker stats
# 只看指定容器
docker stats nginx mysql redis
# 非持续模式,采样一次就退出(适合脚本调用)
docker stats --no-stream
输出示例:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
a1b2c3d4e5f6 nginx 0.05% 12.5MiB / 7.78GiB 0.16% 1.2kB / 650B 0B / 0B 5
b2c3d4e5f6a1 mysql 1.23% 356.2MiB / 7.78GiB 4.47% 15.6MB / 8.3MB 45.1MB / 120MB 38
c3d4e5f6a1b2 redis 0.02% 8.3MiB / 7.78GiB 0.10% 520kB / 310kB 0B / 4.1kB 4
| 列名 | 含义 | 关注点 |
|---|---|---|
| CPU % | 容器占用的 CPU 百分比 | 持续超过 80% 就该警惕了 |
| MEM USAGE / LIMIT | 当前内存使用量 / 内存上限 | 接近 LIMIT 说明要 OOM 了 |
| MEM % | 内存使用百分比 | --- |
| NET I/O | 网络输入/输出 | 突然飙升可能有异常流量 |
| BLOCK I/O | 磁盘读写量 | 持续高 I/O 说明在做大量磁盘操作 |
| PIDS | 容器内进程数 | 异常增多可能有进程泄漏 |
实战建议 :日常巡检时
docker stats --no-stream跑一次就够了,把结果存进日志文件,方便事后对比。
三、实战一:ELK Stack 集中收集 Docker 日志
3.1 为什么需要 ELK?
单机时代 docker logs 够用。但当你的服务拆成 10 个、20 个微服务,分布在 3 台服务器上时,你不可能 SSH 到每台机器上一个个翻日志。
ELK Stack 就是来解决这个问题的:
容器A日志 ──┐
容器B日志 ──┼──▶ Logstash(收集+过滤)──▶ Elasticsearch(存储+索引)──▶ Kibana(可视化查询)
容器C日志 ──┘

▲ 从"到处翻日志"变成"一个页面搜全部"------这就是 ELK 的意义。
| 组件 | 角色 | 通俗解释 |
|---|---|---|
| Elasticsearch | 搜索引擎 + 存储 | 日志的"大仓库",支持全文检索 |
| Logstash | 日志收集 + 过滤 + 转发 | 日志的"快递员",从各容器收件、整理、送进仓库 |
| Kibana | 可视化仪表盘 | 日志的"浏览器",在网页上搜索、过滤、画图 |
3.2 docker-compose.yml 一键部署 ELK
创建目录 elk-stack/,写入以下文件:
docker-compose.yml:
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- ES_JAVA_OPTS=-Xms512m -Xmx512m
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
networks:
- elk
logstash:
image: docker.elastic.co/logstash/logstash:8.12.0
container_name: logstash
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "5044:5044"
- "5000:5000/tcp"
- "5000:5000/udp"
depends_on:
- elasticsearch
networks:
- elk
kibana:
image: docker.elastic.co/kibana/kibana:8.12.0
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
networks:
- elk
volumes:
es_data:
networks:
elk:
driver: bridge
logstash.conf(Logstash 管道配置,放在同级目录):
input {
# 监听 TCP 5000 端口,接收容器发来的日志
tcp {
port => 5000
codec => json
}
# 也可以直接读取 Docker 的 JSON 日志文件
file {
path => "/var/lib/docker/containers/*/*.log"
start_position => "beginning"
codec => json
}
}
filter {
# 如果是 Docker JSON 日志格式,解析字段
if [log] {
json {
source => "log"
}
}
# 添加时间戳
date {
match => ["time", "ISO8601"]
target => "@timestamp"
}
# 删除不需要的字段,减少存储
mutate {
remove_field => ["host", "agent", "ecs", "input", "@version"]
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "docker-logs-%{+YYYY.MM.dd}"
}
# 调试用:同时在控制台输出
stdout {
codec => rubydebug
}
}
启动:
cd elk-stack
docker compose up -d
# 等待 30 秒左右让 Elasticsearch 初始化完成
# 查看状态
docker compose ps
3.3 在 Kibana 里查看日志
-
打开浏览器访问 http://你的服务器IP:5601
-
左侧菜单 → Stack Management → Index Patterns → Create index pattern
-
输入
docker-logs-*,时间字段选@timestamp -
创建完成后,左侧菜单 → Discover,就能看到所有容器的日志了
Kibana 的查询语法非常强大,几个常用的:
# 搜索包含 "error" 的日志
log: "error"
# 搜索指定容器的日志
container_name: "my-app"
# 搜索最近 5 分钟内的 ERROR 级别日志
level: "ERROR" AND @timestamp:[now-5m TO now]
# 排除健康检查日志
NOT log: "healthcheck"
生产建议 :Elasticsearch 至少分配 2GB 堆内存(
ES_JAVA_OPTS=-Xms2g -Xmx2g),并配置 ILM(Index Lifecycle Management)策略自动清理 30 天前的旧索引,否则磁盘会持续增长。
四、实战二:Prometheus + Grafana + cAdvisor 容器监控
4.1 三件套各自干什么?
cAdvisor(采集容器指标)──▶ Prometheus(存储时序数据)──▶ Grafana(可视化仪表盘)
| 组件 | 角色 | 通俗解释 |
|---|---|---|
| cAdvisor | Google 开源的容器监控代理 | 跑在每台宿主机上,自动采集所有容器的 CPU、内存、网络、磁盘指标 |
| Prometheus | 时序数据库 + 监控告警 | 定时"拉取"cAdvisor 暴露的指标,存成时间序列,支持 PromQL 查询和告警规则 |
| Grafana | 可视化仪表盘 | 把 Prometheus 里的数据画成漂亮的图表,一眼看清趋势 |
4.2 docker-compose.yml 一键部署监控三件套
创建目录 monitoring/,写入:
docker-compose.yml:
version: '3.8'
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.1
container_name: cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
privileged: true
devices:
- /dev/kmsg:/dev/kmsg
networks:
- monitoring
prometheus:
image: prom/prometheus:v2.51.0
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=15d'
depends_on:
- cadvisor
networks:
- monitoring
grafana:
image: grafana/grafana:10.4.0
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- grafana_data:/var/lib/grafana
depends_on:
- prometheus
networks:
- monitoring
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridge
prometheus.yml(Prometheus 配置,放在同级目录):
global:
scrape_interval: 15s # 每 15 秒拉取一次指标
evaluation_interval: 15s # 每 15 秒评估一次告警规则
scrape_configs:
# 采集 cAdvisor 暴露的容器指标
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
metrics_path: '/metrics'
# 采集 Prometheus 自身的指标(可选)
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
启动:
cd monitoring
docker compose up -d
4.3 验证各组件
# 检查 cAdvisor 是否正常(浏览器也可以访问)
curl -s http://localhost:8080/containers/ | head -5
# 检查 Prometheus 是否采集到 cAdvisor 的指标
curl -s http://localhost:9090/api/v1/targets | python -m json.tool | grep cadvisor
# 检查 Grafana 是否启动
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000
# 输出 200 说明正常
4.4 在 Grafana 里看监控图表
-
用
admin/admin123登录(在 docker-compose.yml 里配置的) -
添加数据源 :左侧菜单 → Connections → Add new connection → 选 Prometheus → URL 填
http://prometheus:9090→ Save & Test -
导入现成的 Docker 监控仪表盘(这才是重点!):
-
左侧菜单 → Dashboards → Import
-
输入 Dashboard ID:193(这是社区最经典的 Docker 监控模板)
-
选择刚才创建的 Prometheus 数据源
-
点击 Import
-
导入后你就能看到:
-
所有容器的 CPU 使用率折线图
-
每个容器的内存占用柱状图
-
网络收发流量的实时曲线
-
磁盘 I/O 读写速率
-
容器数量总览
其他推荐仪表盘 ID :
893(Docker 容器详细视图)、11600(Node Exporter + cAdvisor 综合面板)。在 Grafana 官网 grafana.com/grafana/dashboards 可以按关键词搜索更多。
4.5 配置告警规则(进阶)
在 prometheus.yml 同级创建 alert_rules.yml:
groups:
- name: docker_alerts
rules:
# 容器 CPU 使用率超过 80%,持续 5 分钟
- alert: HighCPUUsage
expr: rate(container_cpu_usage_seconds_total{name!=""}[5m]) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "容器 {{ $labels.name }} CPU 使用率过高"
description: "{{ $labels.name }} 的 CPU 使用率在过去 5 分钟内超过 80%,当前值:{{ $value }}%"
# 容器内存使用率超过 90%
- alert: HighMemoryUsage
expr: (container_memory_usage_bytes{name!=""} / container_spec_memory_limit_bytes{name!=""} * 100) > 90
for: 3m
labels:
severity: critical
annotations:
summary: "容器 {{ $labels.name }} 内存即将耗尽"
description: "{{ $labels.name }} 内存使用率超过 90%,当前值:{{ $value }}%"
# 容器挂了
- alert: ContainerDown
expr: time() - container_last_seen{name!=""} > 60
for: 1m
labels:
severity: critical
annotations:
summary: "容器 {{ $labels.name }} 已停止"
在 prometheus.yml 里加载告警规则:
rule_files:
- 'alert_rules.yml'
再在 docker-compose.yml 的 prometheus 服务里加一个 volumes 映射:
volumes:
- ./alert_rules.yml:/etc/prometheus/alert_rules.yml:ro
重启后 Prometheus 就会按规则自动评估,触发告警时可以在 Grafana 的 Alerting 面板看到,也可以对接钉钉、企业微信、邮件等通知渠道。
五、日志最佳实践:五条铁律
5.1 应用日志必须输出到 stdout / stderr
这是 Docker 的"第一原则"。不要把日志写到容器内的文件里!
# ❌ 错误示范:日志写到容器内文件
import logging
logging.basicConfig(filename='/app/logs/app.log')
# ✅ 正确示范:日志输出到 stdout
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
原因很简单:Docker 的日志驱动只能收集 stdout/stderr 的输出。如果你写到文件里,docker logs 什么都看不到,ELK 也收不到。
5.2 日志格式统一用 JSON
# 用 structlog 或 python-json-logger 输出结构化 JSON 日志
import structlog
logger = structlog.get_logger()
logger.info("user_login", user_id=12345, ip="192.168.1.100", status="success")
# 输出:{"event": "user_login", "user_id": 12345, "ip": "192.168.1.100", "status": "success", "timestamp": "2025-06-15T10:30:00Z"}
JSON 格式的好处:ELK 能自动解析每个字段,你可以按 user_id、ip、status 做精确过滤和聚合统计,比纯文本搜索效率高几个数量级。
5.3 五条铁律速查表
| 序号 | 铁律 | 原因 |
|---|---|---|
| 1 | 日志输出到 stdout/stderr | Docker 日志驱动只收集标准输出 |
| 2 | 用 JSON 格式 | 方便 ELK 解析和结构化查询 |
| 3 | 配置日志轮转 | 防止磁盘被日志撑爆 |
| 4 | 不要在日志里打印敏感信息 | 密码、Token、身份证号等绝对不能出现在日志里 |
| 5 | 区分日志级别 | DEBUG/INFO/WARN/ERROR 分级,生产环境只开 INFO 以上 |
六、总结:两套方案怎么选?
| 需求 | 推荐方案 | 资源消耗 |
|---|---|---|
| 单机开发调试 | docker logs + docker stats |
零开销 |
| 多台服务器日志集中查询 | ELK Stack | Elasticsearch 至少 2GB 内存 |
| 容器性能实时监控 + 告警 | Prometheus + Grafana + cAdvisor | Prometheus 按数据量,一般 512MB 起步 |
| 生产环境完整方案 | ELK + Prometheus/Grafana 都上 | 建议独立监控服务器 |
本文要点回顾:
-
docker logs是最基础的日志查看工具,配合--tail、-f、--since高效排查问题 -
日志轮转必须配置 ,否则磁盘迟早被撑爆,
max-size+max-file两条搞定 -
ELK Stack 实现日志集中收集,告别 SSH 到每台机器翻日志的日子
-
Prometheus + Grafana + cAdvisor 实现容器性能实时监控,配上告警规则,出问题第一时间通知
-
日志输出到 stdout/stderr + JSON 格式,这两条是 Docker 日志的铁律
下集预告 :容器跑起来了,日志监控也上了,但如果有人通过容器漏洞攻进你的服务器呢?下一篇我们聊聊 Docker 安全最佳实践------镜像扫描、最小权限、网络隔离、seccomp 配置,给你的容器穿上"防弹衣"。