从零搭建 Nginx 日志分析系统(Loki + Promtail + Grafana)完整避坑指南

背景

  • 6 台业务服务器,20+ 个项目,Nginx 访问日志为 JSON 格式,每日总日志量约 1G

  • 只有 1 个人维护,希望用最简便、最经济的方式实现日志集中、查询、统计和告警

  • 最终选型:Grafana Loki + Promtail + Grafana,全免费,资源占用极低

一、环境与架构概览

┌──────────────────┐
│ 业务服务器 ×6 │ (CentOS 7.6, Nginx JSON 日志)
│ Promtail 采集 │
└────────┬─────────┘
│ push (3100)
┌────────▼─────────┐
│ 中央监控服务器 │ (CentOS 7.6, Docker 部署)
│ Loki + Grafana │
└──────────────────┘

  • 所有业务服务器安装 Promtail,读取 /usr/local/nginx/logs/*access*.log

  • 中央服务器用 docker-compose 运行 Loki 和 Grafana

  • 日志从 Promtail 推送到 Loki,Grafana 展示和告警

二、安装 Docker 及 Docker Compose(所有服务器)

2.1 安装 Docker

踩坑提醒:直接使用官方源会报 TCP connection reset by peer,必须换国内镜像。

# 卸载旧版本(如有)
yum remove -y docker docker-client docker-client-latest docker-common docker-latest \
              docker-latest-logrotate docker-logrotate docker-engine

# 安装必要工具
yum install -y yum-utils device-mapper-persistent-data lvm2

# 添加阿里云 Docker CE 源
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 安装 Docker
yum install -y docker-ce docker-ce-cli containerd.io

# 启动并设置开机自启
systemctl start docker
systemctl enable docker

故障排查
若启动失败,检查 /etc/docker/daemon.json 中是否有废弃字段 "graph",删除即可(详见下文)。

2.2 配置 Docker 镜像加速器(重要!)

mkdir -p /etc/docker
cat > /etc/docker/daemon.json <<EOF
{
  "registry-mirrors": [
    "https://docker.xuanyuan.me",
    "https://docker.m.daocloud.io",
    "https://docker.1ms.run"
  ]
}
EOF

systemctl daemon-reload
systemctl restart docker

 

2.3 安装 Docker Compose

踩坑:GitHub 直连超时,使用 DaoCloud 镜像。

# 通过 DaoCloud 镜像下载
curl -L "https://get.daocloud.io/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" \
     -o /usr/local/bin/docker-compose

chmod +x /usr/local/bin/docker-compose
docker-compose --version

如果 DaoCloud 也不可用,可用 pip 安装:

yum install -y python3-pip
pip3 install docker-compose

或者最简单从网站下载docker-composer文件,再上传到服务器

下载地址:https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-Linux-x86_64

 

三、中央服务器部署 Loki + Grafana

 

3.1 目录结构

mkdir -p /opt/monitor/{loki-config,loki-data,grafana-data}
cd /opt/monitor

3.2 Loki 配置文件

创建 /opt/monitor/loki-config/loki-config.yaml

auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  retention_period: 30d

3.3 docker-compose.yml

version: "3.8"

services:
  loki:
    image: grafana/loki:2.9.10
    container_name: loki
    restart: always
    ports:
      - "3100:3100"
    volumes:
      - ./loki-config/loki-config.yaml:/etc/loki/local-config.yaml
      - ./loki-data:/loki
    command: -config.file=/etc/loki/local-config.yaml

  grafana:
    image: grafana/grafana:10.4.0
    container_name: grafana
    restart: always
    ports:
      - "3000:3000"
    volumes:
      - ./grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_DEFAULT_LANGUAGE=zh-CN   # 中文界面
    depends_on:
      - loki

3.4 启动并解决权限错误

docker-compose up -d

大概率会遇到的错误
docker logs loki 显示 mkdir /loki/rules: permission denied

解决方法:修改数据目录的拥有者为 Loki 容器内的 UID(10001)。

chown -R 10001:10001 /opt/monitor/loki-data
docker-compose restart loki

同理,若 Grafana 反复重启,执行:

chown -R 472:472 /opt/monitor/grafana-data
docker-compose restart grafana

验证

docker ps -a | grep loki    # 状态 Up
netstat -tlnp | grep 3100   # 有监听

四、业务服务器部署 Promtail

4.1 日志格式示例

你的 Nginx 日志为 JSON,类似:

{"@timestamp":"20/May/2026:08:10:01 +0800","remote_addr":"1.2.3.4","request":"GET /api HTTP/1.0","status":"200",...}

4.2 Promtail 配置文件

创建目录:mkdir -p /opt/promtail/config

创建  vim /opt/promtail/config/promtail-config.yaml,注意缩进必须用空格

server:
  http_listen_port: 9080
  grpc_listen_port: 0

clients:
  - url: http://<中央服务器IP>:3100/loki/api/v1/push   # 内网IP更好

scrape_configs:
  - job_name: nginx_access
    static_configs:
      - targets:
          - localhost
        labels:
          job: nginx_access
          server: server-01          # 每台机器不同
          __path__: /usr/local/nginx/logs/*access*.log   # 只采集 access 日志
    pipeline_stages:
      - json:
          expressions:
            timestamp: '"@timestamp"'
            remote_addr: ""
            request: ""
            status: ""
            bytes: ""
            up_resp_time: "up_resp_time"
            request_time: "request_time"
            user_agent: "user_agent"
            request_body: ""
            host: ""
      - timestamp:
          source: timestamp
          format: "02/Jan/2006:15:04:05 -0700"   # 注意匹配你的时间格式
      - labels:
          host: ""
          status: ""
          server: ""
      - regex:
          source: filename
          expression: '.*/(?P<project>[^.]+)\..*\.log$'
      - labels:
          project: ""

避坑要点

  1. JMESPath 表达式必须用双引号包裹,例如 timestamp: '"@timestamp"',否则会报 SyntaxError

  2. 正则提取项目名:从文件名如 xxx_api.access.log 中提取出 xxx_api

  3. __path__ 通配符*access*.log 避免采集 error 等非 JSON 日志

  4. 时间格式:务必与 Nginx $time_local 一致(02/Jan/2006:15:04:05 -0700

4.3 启动 Promtail 容器

docker run -d \
  --name promtail \
  --restart always \
  --network host \
  -v /opt/promtail/config/promtail-config.yaml:/etc/promtail/config.yml \
  -v /usr/local/nginx/logs:/usr/local/nginx/logs:ro \
  grafana/promtail:2.9.10 \
  -config.file=/etc/promtail/config.yml

验证docker logs -f promtail 应看到 tail routine: started,且无 error sending batch

五、网络连通性排查(血泪史)

5.1 Promtail 报错:context deadline exceeded / connection refused

原因

  1. 中央服务器 Loki 未正常运行(端口未监听)

  2. 云安全组没有放行 3100 端口

  3. 使用了公网 IP 通信,不稳定

5.2 解决步骤

  1. 中央服务器确认 Loki 正常
    docker ps | grep loki         # 状态 Up
    netstat -tlnp | grep 3100    # 监听中
    curl http://localhost:3100/ready   # 返回 ready

     

  2. 云安全组入方向添加规则
    协议:TCP
    
    端口:3100
    
    授权对象:业务服务器内网IP(或临时 0.0.0.0/0

     

  3. 业务服务器测试连通性
    curl -v http://<中央服务器IP>:3100/ready
    # 返回 HTTP/1.1 200 OK 表示成功

     

  4. 推荐使用内网 IP:修改 Promtail 配置中的 url 为中央服务器的内网地址,然后 docker restart promtail

六、Grafana 中文界面设置(中央宿主机)

6.1 环境变量方式

在 docker-compose.yml 的 grafana 服务中增加:

environment:
  - GF_DEFAULT_LANGUAGE=zh-CN

然后重建容器:docker-compose up -d grafana

如果仍显示英文,可能是浏览器缓存,使用 Ctrl+Shift+R 强制刷新,或开无痕模式验证。

七、验证日志入库

  1. 登录 Grafana http://<中央服务器IP>:3000(账号 admin/admin)

  2. 添加 Loki 数据源:

    • 左侧齿轮 → Data Sources → Add data source → Loki

    • URL: http://loki:3100

    • 点击 Save & Test,应提示成功

  3. 左侧 Explore,选择 Loki 数据源,输入查询:

    {job="nginx_access"}
  4. 调整时间范围,点击 Run query,应能查看到 JSON 日志内容

提示:若查询无结果,请手动触发几条访问请求后再试。

总结与后续建议

至此,你的日志系统已经完整跑通,具备:

  • 集中式日志查询

  • 灵活的标签筛选(按项目、服务器、状态码等)

  • 丰富的可视化图表

  • 实时告警能力

 

 

posted @ 2026-05-20 11:40  FkLee  阅读(142)  评论(0)    收藏  举报