香港服务器轻量日志聚合:Loki + Promtail + Grafana实现多服务统一日志查询
多个服务分散在不同日志文件里,出问题时要在各台服务器上分别 grep,效率低且容易遗漏关联信息。ELK(Elasticsearch + Logstash + Kibana)是成熟的日志方案,但内存消耗动辄 8GB 以上。Grafana Loki 专为日志而生,采用与 Prometheus 相同的标签索引方式,只索引标签不索引全文,内存占用约 500MB,是香港 VPS 上最实用的日志聚合方案。
一、Loki 架构
<code">
各服务器(安装 Promtail)
│ 推送日志流
├── Nginx 访问日志
├── 应用错误日志
└── Docker 容器日志
│
▼
Loki(日志存储引擎,运行在香港服务器)
│
▼
Grafana(日志查询 + 可视化 + 告警)
│ LogQL 查询语言
└── 搜索 / 过滤 / 聚合日志
二、Docker Compose 部署 Loki + Grafana
<code"># /opt/loki/docker-compose.yml
version: '3.8'
services:
loki:
image: grafana/loki:3.1.0
container_name: loki
restart: unless-stopped
ports:
- "127.0.0.1:3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- /opt/loki/config/loki.yaml:/etc/loki/local-config.yaml
- /opt/loki/data:/loki
grafana:
image: grafana/grafana:latest
container_name: grafana-loki
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: GrafanaPass2026
GF_AUTH_ANONYMOUS_ENABLED: "false"
volumes:
- grafana_data:/var/lib/grafana
volumes:
grafana_data:<code"># /opt/loki/config/loki.yaml
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
ingester:
wal:
enabled: true
dir: /loki/wal
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 1h
max_chunk_age: 1h
chunk_retain_period: 30s
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
storage_config:
tsdb_shipper:
active_index_directory: /loki/tsdb-index
cache_location: /loki/tsdb-cache
filesystem:
directory: /loki/chunks
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h # 只接受7天内的日志
ingestion_rate_mb: 20
max_entries_limit_per_query: 5000
retention_period: 720h # 日志保留30天
compactor:
working_directory: /loki/compactor
retention_enabled: true<code"># 启动
mkdir -p /opt/loki/{config,data}
cd /opt/loki && docker compose up -d
# 在 Grafana 中添加 Loki 数据源:
# Configuration → Data Sources → Add → Loki
# URL: http://loki:3100
# Save & Test三、安装 Promtail(日志采集代理)
<code"># 在每台需要采集日志的服务器上安装 Promtail # (可以是同一台服务器,也可以是多台) # 下载 Promtail wget https://github.com/grafana/loki/releases/download/v3.1.0/promtail-linux-amd64.zip unzip promtail-linux-amd64.zip mv promtail-linux-amd64 /usr/local/bin/promtail chmod +x /usr/local/bin/promtail
<code"># /etc/promtail/config.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml # 记录已读取的日志位置(断点续传)
clients:
- url: http://香港Loki服务器IP:3100/loki/api/v1/push
scrape_configs:
# ── Nginx 访问日志 ──
- job_name: nginx-access
static_configs:
- targets: [localhost]
labels:
job: nginx
log_type: access
host: hk-web-1
__path__: /var/log/nginx/access.log
pipeline_stages:
- regex:
expression: '^(?P\S+) .* \[(?P[^\]]+)\] "(?P\S+) (?P\S+) \S+" (?P\d+) (?P\d+)'
- labels:
method:
status:
- timestamp:
source: time_local
format: "02/Jan/2006:15:04:05 -0700"
# ── Nginx 错误日志 ──
- job_name: nginx-error
static_configs:
- targets: [localhost]
labels:
job: nginx
log_type: error
host: hk-web-1
__path__: /var/log/nginx/error.log
# ── 应用日志(JSON 格式)──
- job_name: app-log
static_configs:
- targets: [localhost]
labels:
job: myapp
host: hk-web-1
__path__: /var/log/myapi/error.log
pipeline_stages:
- json:
expressions:
level: level
message: msg
- labels:
level:
# ── Docker 容器日志 ──
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: container
- source_labels: ['__meta_docker_container_log_stream']
target_label: stream<code"># 创建 systemd 服务 cat > /etc/systemd/system/promtail.service << 'EOF' [Unit] Description=Promtail Log Collector After=network.target [Service] ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail/config.yml Restart=on-failure User=root [Install] WantedBy=multi-user.target EOF systemctl enable --now promtail systemctl status promtail
四、LogQL 查询语法
<code">
在 Grafana → Explore → Loki 中使用 LogQL 查询日志:
# 查看 Nginx 所有 5xx 错误
{job="nginx", log_type="access"} |= "\" 5"
# 过去1小时的 404 请求(带 URL 过滤)
{job="nginx"} | json | status="404" | line_format "{{.request_uri}}"
# 应用错误日志(ERROR 级别)
{job="myapp"} | json | level="ERROR"
# 统计每分钟的 5xx 错误数(Rate 聚合)
rate({job="nginx"} |= "status=5" [1m])
# 特定关键词搜索(全文搜索)
{job="myapp"} |= "payment failed"
# 多服务关联查询(同一时间段的 nginx + app 日志)
{host="hk-web-1"} | line_format "{{.container}} | {{.msg}}"
五、日志告警配置
<code">
Grafana → Alerting → Alert Rules → New Alert Rule
示例:5分钟内出现超过10次 500 错误时告警
规则配置:
- Data source: Loki
- Query A:
sum(count_over_time({job="nginx"} |= "status=500" [5m]))
- Condition: A > 10
- Evaluate every: 1m
通知渠道:
- Contact Points → Add → Feishu
- 填写飞书 Webhook URL
告警消息模板:
⚠️ Nginx 500错误告警
服务器:hk-web-1
过去5分钟错误次数:{{ $values.A }}
时间:{{ now | date "2006-01-02 15:04:05" }}
请立即检查应用日志!
六、Grafana Dashboard 日志面板
<code">
推荐面板配置(在 Dashboard 中添加):
面板1:实时日志流(Logs Panel)
查询:{job=~"nginx|myapp"} | json
显示:最新 200 条日志,自动刷新
面板2:HTTP 状态码分布(Bar Chart)
查询:sum by(status) (count_over_time({job="nginx"} | json [1h]))
面板3:错误日志时间分布(Time Series)
查询:sum(rate({job="myapp"} | json | level="ERROR" [5m]))
面板4:Nginx 请求量趋势
查询:sum(rate({job="nginx", log_type="access"}[5m]))
面板5:Top 10 错误 URL
查询(使用 LogQL 聚合):
topk(10, sum by(request_uri) (count_over_time({job="nginx"} |= "status=500" [24h])))
七、Loki vs ELK 资源对比
| 指标 | Loki(本文方案) | ELK(Elasticsearch + Logstash + Kibana) |
|---|---|---|
| 最低内存 | ~500MB | ~8GB |
| 存储效率 | 高(只索引标签,内容压缩存储) | 低(全文索引,磁盘占用是原始日志的3~5倍) |
| 查询速度(全文搜索) | 慢(需要扫描原始日志) | 快(倒排索引) |
| 查询速度(按标签过滤) | 快 | 快 |
| 适合场景 | 中小规模,资源受限服务器 | 大规模,需要复杂全文搜索 |
八、总结
Loki + Promtail + Grafana 在香港 VPS 上仅需 1GB 内存即可稳定运行,是 ELK 栈资源消耗的约 10%。对于日均日志量在 GB 级以下的中小型服务,Loki 完全够用——标签过滤定位问题、关键词搜索查原因、告警规则及时发现异常,日志运维的核心需求全部覆盖。