35. 容器日志管理
1. 容器日志概述
容器日志管理包括日志收集、存储、分析和告警。Docker 默认将容器日志输出到标准输出(stdout)和标准错误(stderr),但生产环境需要集中日志管理方案。
┌─────────────────────────────────────────────────────────────┐ │ 容器日志管理架构 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 日志采集 │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │json-file│ │ syslog │ │fluentd │ │ │ │ │ │(默认) │ │ │ │ │ │ │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ └───────┼────────────┼────────────┼────────────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 日志传输 │ │ │ │ Fluentd / Logstash / Filebeat │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 日志存储 │ │ │ │ Elasticsearch / Loki / Splunk │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 日志可视化 │ │ │ │ Kibana / Grafana / Splunk │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘2. 日志驱动
2.1 查看日志驱动
# 查看当前日志驱动dockerinfo|grep"Logging Driver"# 查看容器日志驱动dockerinspect container_name|grep-A5"LogConfig"# 查看支持的日志驱动dockersystem info|grep-A20"Logging Driver"2.2 json-file(默认)
# 使用 json-file 驱动dockerrun-d--log-driver json-file --log-opt max-size=10m --log-opt max-file=3nginx# 配置日志选项# /etc/docker/daemon.json{"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3","compress":"true"}}2.3 syslog
# 发送日志到 syslogdockerrun-d--log-driver syslog\--log-opt syslog-address=tcp://192.168.1.100:514\--log-opt syslog-facility=daemon\--log-opttag="nginx"\nginx# 本地 syslogdockerrun-d--log-driver syslog\--log-opt syslog-address=unix:///dev/log\nginx2.4 fluentd
# 发送日志到 fluentddockerrun-d--log-driver fluentd\--log-opt fluentd-address=localhost:24224\--log-opttag="docker.{{.Name}}"\nginx2.5 journald
# 使用 journald 驱动(Linux)dockerrun-d--log-driver journald\--log-opttag="nginx"\nginx# 查看 journald 日志journalctlCONTAINER_NAME=nginx-f2.6 gelf(Graylog Extended Log Format)
# 发送到 Graylogdockerrun-d--log-driver gelf\--log-opt gelf-address=udp://graylog:12201\--log-opttag="nginx"\nginx3. 日志查看与过滤
3.1 基础日志命令
# 查看日志dockerlogs container_namedockerlogs-fcontainer_name# 实时跟踪dockerlogs--tail100container_namedockerlogs--since2024-01-01 container_namedockerlogs--until2024-01-02 container_namedockerlogs--timestampscontainer_name# 组合使用dockerlogs-f--tail50--since1h container_name3.2 日志过滤
# 使用 grep 过滤dockerlogs container_name|grepERRORdockerlogs container_name|grep-ierrordockerlogs container_name|tail-100|grep"404"# 使用 jq 解析 JSON 日志dockerlogs container_name|jq'.message'dockerlogs container_name|jq'select(.level == "error")'# 时间范围dockerlogs container_name--since5m--until2m4. ELK 日志方案
4.1 docker-compose.yml
version:'3.8'services:elasticsearch:image:docker.elastic.co/elasticsearch/elasticsearch:7.17.0container_name:elasticsearchenvironment:-discovery.type=single-node-ES_JAVA_OPTS=-Xms512m-Xmx512mvolumes:-es-data:/usr/share/elasticsearch/dataports:-"9200:9200"networks:-elklogstash:image:docker.elastic.co/logstash/logstash:7.17.0container_name:logstashvolumes:-./logstash.conf:/usr/share/logstash/pipeline/logstash.confports:-"5000:5000/tcp"-"5000:5000/udp"depends_on:-elasticsearchnetworks:-elkkibana:image:docker.elastic.co/kibana/kibana:7.17.0container_name:kibanaenvironment:-ELASTICSEARCH_HOSTS=http://elasticsearch:9200ports:-"5601:5601"depends_on:-elasticsearchnetworks:-elkvolumes:es-data:networks:elk:driver:bridge4.2 Logstash 配置
# logstash.confinput{tcp{port=>5000codec=>json_lines}udp{port=>5000codec=>json_lines}}filter{grok{match=>{"message"=>"%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}"}}date{match=>["timestamp","ISO8601"]}}output{elasticsearch{hosts=>["elasticsearch:9200"]index=>"docker-logs-%{+YYYY.MM.dd}"}stdout{codec=>rubydebug}}4.3 运行 ELK
# 启动 ELKdocker-composeup-d# 配置 Docker 使用 GELF 驱动dockerrun-d--log-driver gelf\--log-opt gelf-address=udp://localhost:5000\--log-opttag="myapp"\nginx# 访问 Kibana# http://localhost:56015. Loki + Grafana 方案
5.1 docker-compose.yml
version:'3.8'services:loki:image:grafana/loki:2.8.0ports:-"3100:3100"command:-config.file=/etc/loki/local-config.yamlnetworks:-loggingpromtail:image:grafana/promtail:2.8.0volumes:-/var/lib/docker/containers:/var/lib/docker/containers:ro-/var/run/docker.sock:/var/run/docker.sock-./promtail-config.yaml:/etc/promtail/config.yamlcommand:-config.file=/etc/promtail/config.yamlnetworks:-loggingdepends_on:-lokigrafana:image:grafana/grafana:9.5.0ports:-"3000:3000"environment:-GF_SECURITY_ADMIN_PASSWORD=adminnetworks:-loggingdepends_on:-lokinetworks:logging:driver:bridge5.2 Promtail 配置
# promtail-config.yamlserver:http_listen_port:9080grpc_listen_port:0positions:filename:/tmp/positions.yamlclients:-url:http://loki:3100/loki/api/v1/pushscrape_configs:-job_name:dockerdocker_sd_configs:-host:unix:///var/run/docker.sockrefresh_interval:10srelabel_configs:-source_labels:['__meta_docker_container_name']regex:'/(.*)'target_label:'container'-source_labels:['__meta_docker_container_image']target_label:'image'-source_labels:['__meta_docker_container_label_com_docker_compose_service']target_label:'service'6. 日志轮转配置
6.1 全局配置
// /etc/docker/daemon.json{"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3","compress":"true"}}6.2 容器级别配置
# 运行容器时指定dockerrun-d\--log-opt max-size=10m\--log-opt max-file=3\--log-optcompress=true\nginx6.3 清理日志
# 清空容器日志truncate-s0$(dockerinspect--format='{{.LogPath}}'container_name)# 脚本清理所有容器日志forcontainerin$(dockerps-aq);dolog_path=$(dockerinspect--format='{{.LogPath}}'$container)if[-f"$log_path"];thentruncate-s0$log_pathecho"Cleaned log for container$container"fidone7. 日志分析
7.1 常用分析命令
# 统计错误数量dockerlogs container_name|grep-cERROR# 统计访问最多的 IP(nginx 日志)dockerlogs web|awk'{print $1}'|sort|uniq-c|sort-rn|head-10# 统计响应状态码dockerlogs web|awk'{print $9}'|sort|uniq-c# 查找慢请求dockerlogs web|awk'{if ($10 > 1) print $0}'7.2 日志监控脚本
#!/bin/bash# log-monitor.shERROR_THRESHOLD=10ERROR_COUNT=$(dockerlogs--since5m container_name|grep-cERROR)if[$ERROR_COUNT-gt$ERROR_THRESHOLD];thenecho"WARNING: High error rate detected:$ERROR_COUNTerrors in last 5 minutes"# 发送告警fi8. 日志最佳实践
✅ 应用日志规范
- 输出到 stdout/stderr:让 Docker 捕获日志
- 结构化日志:使用 JSON 格式,便于解析
- 包含上下文:时间戳、日志级别、请求 ID
- 避免敏感信息:不在日志中打印密码、token
- 合理日志级别:生产环境使用 INFO/WARN/ERROR
✅ 日志管理建议
# Python 结构化日志示例importloggingimportjsonclassJSONFormatter(logging.Formatter):defformat(self,record):log_entry={"timestamp":self.formatTime(record),"level":record.levelname,"message":record.getMessage(),"module":record.module,"function":record.funcName}returnjson.dumps(log_entry)9. 常用命令速查
| 操作 | 命令 |
|---|---|
| 查看日志 | docker logs |
| 实时跟踪 | docker logs -f |
| 最后 N 行 | docker logs --tail N |
| 时间范围 | docker logs --since |
| 查看日志路径 | docker inspect --format='{{.LogPath}}' |
| 清空日志 | truncate -s 0 $(docker inspect --format='{{.LogPath}}' container) |
| 配置日志驱动 | --log-driver |
| 日志轮转 | --log-opt max-size=10m |
10. 常见问题
Q1: 日志占用磁盘空间过大?
配置日志轮转,设置 max-size 和 max-file。
Q2: docker logs 命令无法使用?
检查是否使用了非 json-file 日志驱动。
Q3: 如何查看已删除容器的日志?
容器删除后日志也被删除,需要提前保存。
11. 小结
- 日志驱动:json-file、syslog、fluentd、journald、gelf
- 日志轮转:限制大小和文件数量
- ELK/Loki:集中日志解决方案
- 日志查看:logs 命令和过滤
- 日志分析:统计错误、访问量等
- 最佳实践:结构化日志、合理级别
- 监控告警:基于日志的错误检测
- 生产环境必须配置日志集中管理