对于大多数 Docker 生产环境,如果主要目的是故障排查和基础监控,优先推荐 Loki;如果需要复杂的全文检索、数据分析或已有 Elastic 架构,则选择 ELK。
先说结论:资源敏感选 Loki,分析深度选 ELK,不要盲目跟风。
- 适合:Loki 适合云原生、K8s/Docker swarm 及资源受限场景;ELK 适合需要复杂聚合分析的企业级场景。
- 重点看:索引机制差异,Loki 只索引标签导致存储更低,ELK 全文索引导致查询更强但资源消耗大。
- 别忽略:查询语言学习成本(LogQL vs Lucene)以及长期存储的磁盘规划。
核心架构与部署示例
选型阶段不需要立刻部署,先通过架构草图评估复杂度。Loki 通常配合 Promtail 和 Grafana,ELK 通常需要 Filebeat、Logstash(可选)、Elasticsearch 和 Kibana。以下是基于 Docker Compose 的最小化生产部署参考。
前提条件:确保 Docker 守护进程配置为默认 json-file 日志驱动,否则 /var/lib/docker/containers 路径无效。若使用 journald 则需通过 systemd 采集,本文示例基于 json-file。
# Loki 栈 docker-compose.yml 示例
version: '3.8'
services:
loki:
image: grafana/loki:2.9.0
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki-config:/etc/loki
- loki-data:/loki
deploy:
resources:
limits:
memory: 1G
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
- ./promtail-config:/etc/promtail
command: -config.file=/etc/promtail/config.yaml
user: root # 读取宿主机日志目录通常需要 root 权限
grafana:
image: grafana/grafana:10.0.0
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
volumes:
loki-data:
grafana-data:
# ELK 栈 docker-compose.yml 示例 (简化版)
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.9.0
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms1g -Xmx1g # 生产环境需根据内存调整
volumes:
- es-data:/usr/share/elasticsearch/data
deploy:
resources:
limits:
memory: 2G # ES 建议至少 2GB 起步
filebeat:
image: docker.elastic.co/beats/filebeat:8.9.0
user: root
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
kibana:
image: docker.elastic.co/kibana/kibana:8.9.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
volumes:
es-data:采集配置关键点
日志采集配置直接决定数据质量。注意挂载宿主机日志目录时,若开启 Docker 用户命名空间重映射(userns-remap),可能需要调整权限或使用特权模式。
Promtail 配置片段 (config.yaml):
scrape_configs:
- job_name: containers
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/lib/docker/containers/*/*-json.log
pipeline_stages:
- json:
expressions:
output: log
stream: stream
attrs:
- labels:
stream:
- output:
source: outputFilebeat 配置片段 (filebeat.yml):
filebeat.inputs:
- type: container
paths:
- /var/lib/docker/containers/*/*.log
processors:
- add_docker_metadata:
host: "unix:///var/run/docker.sock"
output.elasticsearch:
hosts: ["http://elasticsearch:9200"]日志轮转配置:避免单个日志文件过大影响采集性能,建议在宿主机配置 /etc/logrotate.d/docker-containers。
/var/lib/docker/containers/*/*.log {
rotate 7
daily
compress
size=100M
missingok
delaycompress
copytruncate
}查询语言实战对比
查询效率直接影响排查速度。Loki 使用 LogQL,ELK 使用 KQL (Kibana Query Language) 或 Lucene。
场景:查找过去 5 分钟内包含 "ERROR" 且容器名为 "api-server" 的日志。
- Loki (LogQL):
{container_name="api-server"} |= "ERROR" - ELK (KQL):
container.name : "api-server" AND message : "ERROR"
场景:统计错误级别分布。
- Loki: 支持聚合但性能受限于标签基数,复杂聚合不如 ELK。
- ELK:
GET /_search { "aggs": { "levels": { "terms": { "field": "log.level" } } } }原生支持高效聚合。
资源限制与运维保障
生产环境必须设置资源限制,防止日志组件耗尽宿主机资源。
- 内存配置:Elasticsearch 默认堆内存较大,需根据文档调整 JVM 配置;Loki 相对轻量,但仍需限制缓存大小以防 OOM。
- 时间同步:确保所有 Docker 宿主机和日志服务器开启 NTP 时间同步,否则日志顺序混乱会导致排查困难。
- 磁盘告警:未配置保留策略(Retention Policy)的日志系统会无限占用磁盘。务必设置基于时间或大小的删除策略,并配置磁盘使用率告警(如超过 80% 触发)。
验证与排查
1. 连通性检查
在 Grafana 或 Kibana 界面添加数据源,执行简单查询。确认能看到最近 5 分钟内的容器日志。
# 手动测试 Loki 接入
curl -G http://localhost:3100/loki/api/v1/query_range `--data-urlencode` 'query={job="varlogs"}' `--data-urlencode` 'start=$(date -d "5 minutes ago" +%s)000000000' `--data-urlencode` 'end=$(date +%s)000000000'2. 资源监控
观察日志组件所在的容器资源使用率。在正常日志写入压力下,CPU 使用率应保持稳定,无持续飙升;内存使用不应触及限制值。
3. 日志完整性
故意在业务容器中输出一条带特定标识的测试日志,确认能在查询界面检索到,且时间戳误差在可接受范围内(通常秒级)。
生产环境常见风险
- Loki 标签基数过高:不要将用户 ID、请求 ID 等高基数信息作为 Loki 的标签(Labels),这会导致索引爆炸。这些信息应放在日志内容中,通过查询语句过滤。
- Elasticsearch 内存溢出:生产环境不要使用默认配置运行 Elasticsearch。需根据数据量调整堆内存,通常建议不超过 31GB,并关闭 swaps 以保证稳定性。
- 权限不足:直接挂载宿主机目录可能需要特权模式或特定用户组权限,若遇到 Permission Denied,检查容器 user 配置或宿主机目录 ACL。