从零搭建 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: ""
避坑要点:
-
JMESPath 表达式必须用双引号包裹,例如
timestamp: '"@timestamp"',否则会报SyntaxError -
正则提取项目名:从文件名如
xxx_api.access.log中提取出xxx_api -
__path__通配符:*access*.log避免采集 error 等非 JSON 日志 -
时间格式:务必与 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
原因:
-
中央服务器 Loki 未正常运行(端口未监听)
-
云安全组没有放行 3100 端口
-
使用了公网 IP 通信,不稳定
5.2 解决步骤
- 中央服务器确认 Loki 正常
docker ps | grep loki # 状态 Up netstat -tlnp | grep 3100 # 监听中 curl http://localhost:3100/ready # 返回 ready
- 云安全组入方向添加规则
协议:TCP 端口:3100 授权对象:业务服务器内网IP(或临时 0.0.0.0/0)
- 业务服务器测试连通性
curl -v http://<中央服务器IP>:3100/ready # 返回 HTTP/1.1 200 OK 表示成功
- 推荐使用内网 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 强制刷新,或开无痕模式验证。
七、验证日志入库
-
登录 Grafana
http://<中央服务器IP>:3000(账号 admin/admin) -
添加 Loki 数据源:
-
左侧齿轮 → Data Sources → Add data source → Loki
-
URL:
http://loki:3100 -
点击 Save & Test,应提示成功
-
-
左侧 Explore,选择 Loki 数据源,输入查询:
{job="nginx_access"} -
调整时间范围,点击 Run query,应能查看到 JSON 日志内容
提示:若查询无结果,请手动触发几条访问请求后再试。
总结与后续建议
至此,你的日志系统已经完整跑通,具备:
-
集中式日志查询
-
灵活的标签筛选(按项目、服务器、状态码等)
-
丰富的可视化图表
-
实时告警能力

浙公网安备 33010602011771号