香港服务器轻量日志聚合:Loki + Promtail + Grafana实现多服务统一日志查询

香港服务器轻量日志聚合: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 完全够用——标签过滤定位问题、关键词搜索查原因、告警规则及时发现异常,日志运维的核心需求全部覆盖。

Telegram