ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Nginx日志分析平台实战:VictoriaLogs+Grafana替代ELK的轻量方案

Nginx日志分析平台实战:VictoriaLogs+Grafana替代ELK的轻量方案 1. Nginx原始日志让我睡不着的那几个晚上搞了七年运维最怕的不是服务器半夜宕机而是 Nginx 日志堆成山之后定位一个问题要花掉大半个晚上。刚带团队那会儿大家都觉得 grep 够用出问题就 ssh 到网关机器上 tail -f 硬看。说实话单机、低 QPS 的阶段这套确实能顶。但当流量上来、Nginx 前面还有多层负载、后端又有几十个微服务节点的时候原始日志的短板会被无限放大。VictoriaLogs、Grafana 这两个词我关注了很久真正促使我落地的是一次线上事故:某个凌晨接口超时飙到 90%我们需要同时查 8 台 Nginx access log还要跨多个实例对比同一条 trace_id。结果每台机器 grep 一次要二三十分钟等我把日志碎片拼起来业务已经恢复了。那次复盘会开得很难看但让我下了决心必须建一套正经的应用日志分析平台把 Nginx 日志从服务器上的文本文件变成随时能检索、能统计、能出图的数据资产。如果你也在靠 grep、tail、scp 处理 Nginx 日志这篇实战记录应该能帮你少踩几个坑。我讲的不只是安装步骤而是从日志格式改造、采集链路设计、查询语法到 Grafana 面板搭建的完整闭环。方案选型上我权衡过 ELK、Loki最终还是落在 VictoriaLogs Grafana Promtail 这套组合上接下来把为什么这么选、怎么搭、踩了哪些坑一次性讲透。1.1 grep排查在日志量上来之后会断崖式失效不少人觉得日志查询不就是 grep 吗这句话在小流量下没毛病甚至我自己也这么干过很久。但日志量上到每天几十 GB、上百 GB 之后几个现实问题会接连砸过来:跨节点检索靠手动拼凑:Nginx 多实例部署后一条请求可能落在任意一台机器的 access.log 里。你要先确定哪台机器再上去 grep如果没命中还要换下一台。时间全浪费在找日志在哪而不是看日志里有什么上。时间线对不齐:每台机器系统时间、时区、Nginx 编译参数可能有差异日志里的 $time_local 是本地时间多台机器放在一起看前后顺序是乱的。logrotate 一切割历史日志就断片:默认配置下日志按天切割一旦某天忘记处理或者压缩失败想查三天前的日志还要先去解压、拼接、再 grep效率极低。检索语法太原始:grep 能匹配文本但做不了统计 5xx 状态码占比算接口 P95 响应时间按域名分组看访问量这类分析。真要算只能把日志导出来用 Python 脚本处理每次都是临时工。这些痛点叠加在一起就不是忍一忍能解决的了。日志本质上是有时间序列属性的结构化数据应该交给专门的存储和查询引擎而不是躺在磁盘上等人用 shell 去啃。1.2 企业级日志平台不是炫技而是事故复盘的基本盘所谓企业级不是说功能越多越好而是必须满足几个底线要求:统一采集:不管 Nginx 部署在物理机、虚拟机还是容器里日志都能自动汇聚到一个地方。结构化解析:日志进入平台后是字段化的比如 status、request_uri、request_time 都能单独查询而不是一整行字符串。快速检索:面对几十 GB 一天的日志量按关键字、按时间范围、按状态码组合检索必须秒级返回而不是每次等一个离线任务。可视化分析:能直接生成趋势图、占比图、Top N 列表让开发、运维在同一个面板上看到问题。告警能力:配合 Grafana Alerting可以在 5xx 比例突增、P95 超阈值时及时通知而不是等用户反馈。这套能力以前用 ELK 也能搭但 ELK 的组件多、资源占用高、维护成本重中小团队往往搭得起养不起。我身边不少同事折腾完 ELK 最后退回 grep就是因为被 Elasticsearch 的 JVM 调到崩溃、Logstash 的 pipeline 吞日志等问题劝退。所以这次选型我的核心诉求很明确资源占用要轻、部署要简单、查询要快最好不引入太多新组件。1.3 为什么挑中 VictoriaLogs 而不是继续用 ELK/Loki先说我试过的几个方案:ELKElasticsearch Logstash Kibana:功能确实强但一个三节点的 Elasticsearch 集群起步就是十几 GB 内存Logstash 还要单独吃资源。对于只查 Nginx 访问日志这个场景属于杀鸡用牛刀而且 Find 排查问题的时候Kibana 查询语法对不熟的人有点劝退。Loki:轻量、和 Grafana 集成好但 LogQL 的标签设计把日志拆得很细查询长日志或者做统计分析时语法写起来总觉得别扭。我们团队用了一段时间P95 响应时间这类统计需求很难用 LogQL 直观地写出来。VictoriaLogs:VictoriaMetrics 团队出的日志存储引擎主打单二进制、低资源占用、高压缩率查询语言 LogsQL 上手快。更关键的是它兼容 Loki、Elasticsearch 的采集协议Promtail、Fluent Bit 这些现成客户端可以直接对接不用为日志接入重新发明轮子。我选型时在测试环境做了一轮对比:同样灌入一天约 20GB 的 Nginx 访问日志VictoriaLogs 单机内存占用 1GB 左右磁盘压缩后约 2GB 不到相同数据量如果塞进 Elasticsearch即便场景调优过磁盘占用也远高于这个数。对于绝大多数中小团队这个差距足够改变决策了。2. VictoriaLogs的底层设计和查询语法为什么敢说吃日志很香2.1 单二进制、列式存储、字段即索引VictoriaLogs 最吸引我的一点就是部署极其简单。它不像 Elasticsearch 那样要起多个角色节点也不像 Loki 那样要把 chunk 和 index 分开管理默认情况下一个二进制文件跑起来就能用。我第一次启动它的时候甚至有点不真实感——就一个进程连配置文件都不强制要。底层存储上VictoriaLogs 用的是列式日志存储数据按时间组织成段并且按字段单独存储。这个设计和 ClickHouse 的思路有点像日志写入后每一列独立压缩查询时只扫描命中的字段而不是把整行数据都读出来。对于访问日志这种场景——每一行都是结构化的 JSON字段可预期、可枚举——列式存储的压缩率和扫描性能都非常好看。另一个关键设计是字段即索引。VictoriaLogs 不是靠预先定义 mapping 来建立倒排索引而是把每条日志里的字段自动识别出来。查询的时候LogsQL 可以直接用字段名过滤比如 status:500、request_uri:/api/order不需要在摄入阶段先建索引模板。这也意味着如果后面想给 Nginx 日志增加一个新字段直接在 log_format 里加就行平台侧什么都不用改。2.2 流、字段、时间线:先建立三个心智模型用 VictoriaLogs 之前我建议先理解三个核心概念:时间线_time:每条日志都有一个时间戳默认是摄入时间也可以从日志内容中解析。检索时第一件事就是圈定时间范围这既是习惯也是性能关键——VictoriaLogs 会先在对应的时间分区里扫描。流_stream:日志在采集时会打上标签比如 jobnginx-access、hostweb-01这些标签组合起来就是一条日志流。理解成日志来自哪个来源就行了。查询时可以用流过滤条件快速缩小范围。字段_fields:日志正文里解析出来的键值对比如 status、request_time、upstream_addr。字段是查询和统计的主要对象。这三个概念搞明白LogsQL 的很多语法就能猜个大概了。比如我想看最近 10 分钟所有 500 错误第一时间想到的就是时间范围 字段过滤的组合这也是 LogsQL 最常用的写法。2.3 兼容多种采集协议不逼你换客户端很多人担心引入新日志平台后要重写采集器。VictoriaLogs 这点做得很到位它直接兼容 Loki 的 push API、Elasticsearch 的 bulk API也支持 Syslog、Journald 等输入方式。这意味着已经在用Promtail采集 Loki 日志的把 clients 里的 URL 指向 VictoriaLogs 就行已经在用Fluent Bit的可以通过 HTTP 输出插件往 Loki 端点推送已经在用Filebeat的可以走 Elasticsearch 兼容接口想快速测试的直接 curl 往里塞一条 JSON 就能验证。这个特性在落地时帮了大忙。当时我们团队日志采集已经有一部分用了 Promtail切换 VictoriaLogs 时只需要改配置文件里的 endpoint不需要对日志采集链路做大手术。下面我会把 Promtail 的具体配置贴出来你可以直接抄。3. 部署:VictoriaLogs Grafana 从零跑到能查日志3.1 硬件规划和目录我建议的起步配置是 4C8G 的虚拟机按一天 20~50GB 日志量、保留 14 天来算基本够用。如果你峰值流量特别高可以加到 8C16GVictoriaLogs 本身对硬件比较克制最吃资源的是查询时的内存日常写入阶段 2C4G 也能跑但查询大时间范围时会吃力。磁盘方面有条件就上 SSD日志检索本质是 IO 密集操作机械盘在扫几十 GB 数据时会明显变慢。目录结构我习惯这样规划:/opt/victoria-logs/bin # 二进制 /opt/victoria-logs/conf # 配置文件如果需要 /var/lib/victoria-logs/data # 日志数据目录 /var/log/victoria-logs/ # 运行日志生产环境建议单独建一个系统用户跑 VictoriaLogs不要用 root。后续如果做权限收敛或者接审计系统独立的运行账号会省很多事。3.2 用二进制部署 VictoriaLogs先去 GitHub 的 VictoriaMetrics Releases 页面下载最新版victoria-logs-linux-amd64压缩包解压后把victoria-logs-prod或解压出来的二进制名放到 /usr/local/bin 下。我习惯把版本固定住不追最新稳定优先。解压后先手动跑一下确认版本:./victoria-logs-prod -version确认没问题后创建运行用户和数据目录:useradd -r -s /sbin/nologin victorialogs mkdir -p /var/lib/victoria-logs/data chown -R victorialogs:victorialogs /var/lib/victoria-logs chown -R victorialogs:victorialogs /opt/victoria-logs然后写一个 systemd unit让服务可以开机自启、异常重启:[Unit] DescriptionVictoriaLogs single-node log service Afternetwork-online.target Wantsnetwork-online.target [Service] Uservictorialogs Groupvictorialogs ExecStart/usr/local/bin/victoria-logs-prod \ -storageDataPath/var/lib/victoria-logs/data \ -retentionPeriod14d \ -httpListenAddr0.0.0.0:9428 \ -loggerOutputstdout Restartalways RestartSec10 LimitNOFILE1048576 [Install] WantedBymulti-user.target这里解释几个关键参数:-storageDataPath:数据存放路径一定要放在磁盘空间充足、监控覆盖到的目录。后续容量管理围绕这个目录做。-retentionPeriod:保留周期我设的是 14d。日志平台不是数据仓库没必要永久存访问日志设短一点既能控制磁盘成本也能减少查询扫过的数据量。个别需要长留的可以后面在采集端做二次归档。-httpListenAddr:默认就是 9428 端口一般不用改。如果 VictoriaLogs 和 Grafana 不在同一台机器注意防火墙要放行这个端口。LimitNOFILE:进程能打开的句柄数。高并发写入时如果句柄数不够会出现 too many open files 的问题所以提前放开。启动后看一眼日志输出确认没有报错:systemctl daemon-reload systemctl enable --now victorialogs systemctl status victorialogs接着验证 HTTP 接口有没有起来:curl http://127.0.0.1:9428/select/vmui如果浏览器能打开 vmui 界面说明服务已经正常工作了。vmui 是 VictoriaLogs 自带的查询界面后续临时查日志、验证数据有没有进来我都会先用它比去 Grafana 配面板快得多。3.3 Grafana 安装和 VictoriaLogs 数据源Grafana 的安装方式很多用 DEB/RPM 包、二进制、Docker 都行。我这边是直接用官方 DEB 包装的:wget https://dl.grafana.com/oss/release/grafana_10.4.0_amd64.deb sudo dpkg -i grafana_10.4.0_amd64.deb sudo systemctl enable --now grafana-server装完先登录 Grafana默认 3000 端口初始账号 admin/admin然后安装 VictoriaLogs 数据源插件。插件 ID 是victoriametrics-logs-datasource:grafana-cli plugins install victoriametrics-logs-datasource sudo systemctl restart grafana-server在 Grafana 里添加数据源时选择 VictoriaLogs 类型即可URL 填 VictoriaLogs 的地址比如http://127.0.0.1:9428。有一个 Explore mode 选项我一般选 Logs Explorer这样在 Explore 页面里可以直接用字段浏览器的方式筛选日志对不熟悉 LogsQL 的同事更友好。保存后点 Save test能连通就说明数据源配置没问题。如果不想每次在页面上手工点也可以用 provisioning 的方式把数据源固化下来顺便把 UID 写死这个好处到后面踩坑部分你就知道了:# /etc/grafana/provisioning/datasources/victorialogs.yml apiVersion: 1 datasources: - name: VictoriaLogs uid: victorialogs-prod type: victoriametrics-logs-datasource access: proxy url: http://127.0.0.1:9428 isDefault: false jsonData: exploreMode: logsExplorer3.4 冒烟验证平台起好之后先手动塞一条日志确认链路通不通。VictoriaLogs 兼容 Loki 的 push API我习惯用 curl 模拟:curl http://127.0.0.1:9428/insert/loki/api/v1/push \ -H Content-Type: application/json \ -d {streams:[{stream:{job:smoke-test},values:[[$(date %s)000000000,hello victorialogs]]}]}然后去 vmui 页面查一下:_time:now-5m AND job:smoke-test能查到这一条测试日志就说明 VictoriaLogs 的摄入和查询都正常了。接下来可以放心地进入采集链路改造环节。4. 日志接入:把 Nginx 访问日志改造成结构化 JSON4.1 为什么 Nginx 日志要用 JSON 格式大多数 Nginx 默认访问日志格式是 combined一行文本用空格和引号分隔。这种格式人眼读还行但机器解析很痛苦。虽然 Promtail 和 Fluent Bit 都有对应的正则解析器但正则匹配在字段变化时非常脆弱只要某个字段里多了一个引号、空格或者奇怪的字符整行解析就可能失败日志静默丢弃问题反而更难查。所以我强烈建议如果要把 Nginx 日志接入日志平台第一步不是配采集器而是先把 Nginx 的 log_format 改成 JSON。JSON 的好处有几点:天然结构化:进入 VictoriaLogs 后 status、request_time 这些字段自动可用查询和统计不需要再写正则。解析零失败:只要 JSON 合法字段就不会错位不会再出现日志进来了但关键字段是空的这类怪问题。采集端更简单:Promtail、Fluent Bit 对这个格式几乎是开箱即用。4.2 nginx.conf 里的 log_format 改造在 nginx.conf 的 http 块里加一个自定义 JSON 格式:log_format json_combined escapejson { time_iso8601:$time_iso8601, remote_addr:$remote_addr, request_method:$request_method, request_uri:$request_uri, server_name:$server_name, status:$status, body_bytes_sent:$body_bytes_sent, request_time:$request_time, http_referer:$http_referer, http_user_agent:$http_user_agent, upstream_addr:$upstream_addr, upstream_response_time:$upstream_response_time };注意几个细节:escapejson 必须加:不加的话user_agent 里的引号、特殊字符会把 JSON 结构打坏。status 和 request_time 不要用引号包起来:这两个字段我希望 VictoriaLogs 识别成数值类型这样后面才能做数值比较和统计。字符串类型的 200 和数值类型的 200 在日志平台里是两个概念类型不统一非常痛苦。用 $time_iso8601 而不是 $time_local:前者带时区信息格式是 RFC3339Promtail 解析时间戳时不容易出错。这个坑后面专门讲。然后在要采集的 server 块里指定这个格式并加上缓冲参数:access_log /var/log/nginx/access.json.log json_combined buffer64k flush5s;buffer 和 flush 的意义是减少磁盘 IO让日志按块刷盘。代价是如果进程突然崩溃最后几秒的日志可能会丢。对于访问日志这种非强一致的数据为了性能做一点妥协是可以接受的。如果你的业务对日志完整性要求特别高就不要开 buffer直接access_log /var/log/nginx/access.json.log json_combined;。改完之后nginx -t检查语法然后 reload:nginx -t nginx -s reload去新日志文件里看一眼确认输出是合法 JSON:tail -2 /var/log/nginx/access.json.log | jq .4.3 结合 logrotate 做好切割和补偿Nginx 的 access.log 不会自己切割需要配 logrotate。这里要给 Promtail 留一个心眼:Promtail 是靠 position 文件记录读取到哪个位置的如果 logrotate 切割时把原文件内容搬走了、又新建了同名文件Promtail 需要能感知到文件变化。我用的 logrotate 配置如下:/var/log/nginx/access.json.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 www-data adm sharedscripts postrotate [ -f /var/run/nginx.pid ] kill -USR1 $(cat /var/run/nginx.pid) endscript }create 0640 www-data adm保证切割后新文件的属主和权限正确否则 Promtail 可能没有权限读取新文件。postrotate里向 Nginx 发送 USR1 信号让 Nginx 重新打开新的日志文件句柄。这个信号很重要不发的后果就是 Nginx 继续往旧文件写入而旧文件已经被改名日志就丢了。4.4 Promtail 配置与 VictoriaLogs 对接Promtail 是 Grafana Loki 生态的采集器因为 VictoriaLogs 兼容 Loki push API所以直接把 Promtail 的 endpoint 指向 VictoriaLogs 就行。安装 Promtail 我一般直接用二进制下载解压后写一个 config.yml:server: http_listen_port: 9080 grpc_listen_port: 0 positions: filename: /var/lib/promtail/positions.yaml clients: - url: http://victoria-log-server:9428/insert/loki/api/v1/push scrape_configs: - job_name: nginx-access static_configs: - targets: [localhost] labels: job: nginx-access host: web-01 __path__: /var/log/nginx/access.json.log pipeline_stages: - json: expressions: time_iso8601: time_iso8601 - timestamp: source: time_iso8601 format: RFC3339Nano这一段配置里有两个地方要解释清楚:第一为什么只用 json stage 提取时间戳没有把所有字段都提取出来?因为 VictoriaLogs 本身能识别 JSON message 里的字段。Promtail 把原始 JSON 整行推送过去之后VictoriaLogs 在摄入时会自动解析字段所以不需要在 Promtail 里做一次字段展开。只需要把time_iso8601提取出来用来设置日志时间戳避免使用摄入时间代替日志真实产生时间。第二为什么 labels 只保留 job 和 host?很多初学 Loki 的人会把 status、request_uri、method 都做成 label。这在 Loki 的原始设计里还能接受但在 VictoriaLogs 这条链路上字段本来就是可查询的没必要全部提成流标签。尤其是 client_ip、request_uri 这类高基数字段如果做成流标签会产生海量日志流严重拖慢写入和查询。日志流标签越稳定越好job 和 host 这种来源标识才是它们该做的活。启动 Promtail:./promtail -config.fileconfig.yml生产上同样建议用 systemd 托管这里就不重复贴 unit 了照抄 VictoriaLogs 的模板改一下路径就行。4.5 验证数据进没进来Promtail 起来后先看它的日志有没有报错。确认没有连接异常后去 vmui 里查一下:_time:now-10m AND job:nginx-access能看到 Nginx 的 JSON 访问日志说明采集链路已经通了。再点开一条日志确认 status、request_time、upstream_addr 这些字段都被正确解析。如果字段缺失优先检查 Nginx 端 log_format 是不是写错了字段名或者escapejson有没有生效。5. LogsQL 实战:检索、统计和慢请求定位5.1 先搞懂一条 LogsQL 的组成LogsQL 的查询由两部分组成过滤条件 管道操作。过滤条件负责圈定日志范围管道操作负责对命中的日志做统计、排序、截取等处理。最简单的查询就是直接打一个关键字:payment_error这会检索所有 message 里包含 payment_error 的日志。生产环境我一般都会先加时间范围和字段过滤避免扫全量数据:_time:now-1h AND status:500_time:now-1h是时间范围缩写等价于最近一小时。status:500是字段精确匹配要求 status 字段的值等于数字 500。这个组合几乎是我每天用最多的查询。5.2 日常检索举例下面这些查询都是我在生产环境里实际用过的字段名和你的 Nginx log_format 保持一致即可:场景LogsQL 写法最近1小时所有5xx错误_time:now-1h AND (status:500 OR status:502 OR status:503 OR status:504)某个域名下的404请求_time:now-1h AND server_name:example.com AND status:404慢请求响应时间超过1秒_time:now-1h AND request_time:1某个客户端IP的访问记录_time:now-24h AND remote_addr:1.2.3.4消息里包含关键字且状态5xx_time:now-1h AND status:499 AND timeout这里有个小技巧:5xx 状态码可以写成status:499相比把 500、502、503、504 一个个列出来代码更简洁也不容易漏。前提是 status 字段在日志里是数值类型这又回到了前面强调的写成 JSON 时不要给数值字段加引号。5.3 用统计管道把日志变成指标检索只是日志平台的基础真正体现价值的是统计能力。以前算状态码分布要导日志用脚本跑现在一条 LogsQL 直接出数。按状态码统计请求量:_time:now-6h AND job:nginx-access | stats by (status) count() as requests这个查询把最近六小时日志按 status 分组计数结果是一张三列的表:status、requests。可以在 vmui 或 Grafana 里直接看也可以接到饼图、柱状图上。算 P95 响应时间:_time:now-1h AND job:nginx-access | stats quantile(0.95, request_time) as p95如果还想看每个上游节点upstream_addr的平均响应时间和最大响应时间:_time:now-1h AND job:nginx-access | stats by (upstream_addr) avg(request_time) as avg_rt, max(request_time) as max_rt找 Top 20 慢接口:_time:now-24h AND job:nginx-access AND request_time:1 | stats by (request_uri) avg(request_time) as avg_rt | sort by (avg_rt) desc | limit 20这些统计查询的使用门槛并不高核心是先确认字段名、字段类型正确再决定用哪个聚合函数。真正常见的坑恰恰不是语法而是字段类型到了查询阶段发现是字符串导致聚合函数直接报错。这个我在下一节踩坑部分细说。5.4 实践建议:标签不要滥用使用 VictoriaLogs 的时候最容易产生的一个错误心态是:反正日志字段都能查那我在采集端把所有字段都标上不是更快吗我的建议是:采集端只要保留稳定、低基数的标签比如 job、host、environment环境名。像 status、request_uri、remote_addr 这类字段让它们在日志正文里保持 JSON 结构查询时用 LogsQL 过滤即可。原因有二:写入性能:日志流数量等于标签组合数。标签越多、组合越复杂流越多底层需要维护的元数据越大。查询性能:走字段过滤和走流过滤在 VictoriaLogs 里差不了太多因为底层列式存储本身做了优化。牺牲写入侧的性能去换查询侧那一点便利不划算。6. Grafana 面板:从看日志到看业务6.1 Explore 还是 Logs ExplorerVictoriaLogs 数据源插件在 Grafana 里有两种使用方式。一个是标准的 Explore 页面直接写 LogsQL 查询结果以日志流形式展示适合定位问题的时候快速翻日志。另一个是 Logs Explorer 模式本质上是一个可视化的字段浏览器左边列出日志流和字段你点选条件右边自动出日志不用手写语法。我的个人习惯是:临时排查问题用 Explore做固定监控面板时用 Logs Explorer 或者直接把 LogsQL 写进 Panel。不管哪种模式最终执行的底层都是 LogsQL只是交互方式不同。6.2 一套够用的访问日志面板我做的第一个生产面板就围绕 Nginx 访问日志的核心指标展开包含这些 Panel:面板数据来源用途单位时间请求量日志数计数看流量趋势配合业务发版、活动观察波动HTTP 状态码分布stats by status count()快速发现 5xx 抬头P95 响应时间趋势stats quantile(0.95, request_time)监控整体服务质量Top 10 慢接口sort by avg(request_time) desc limit 10定位性能瓶颈上游节点错误统计stats by upstream_addr count if status 499发现后端异常节点热门请求 URIstats by request_uri count() sort desc limit 10了解流量集中点在 Grafana 里新建 Panel 时数据源选 VictoriaLogs查询模式可以用 LogsQL。比如请求量趋势查询框里直接写:_time:now-${__range} AND job:nginx-access | stats count() as requests时间变量$__range会自动跟随 Dashboard 右上角的时间选择器这样同一块面板换个时间范围就能回看历史。这是我比较推荐的做法不要把时间写死。6.3 固定数据源 UID 与跨环境迁移这里要提一个很实际的建议用 provisioning 方式创建数据源时给每个数据源固定一个 UID。Grafana 的 Dashboard JSON 里Panel 是通过 datasource UID 来引用数据源的。如果 UID 随机生成一旦数据源重新创建或从另一个环境导出导入旧面板就会集体失效报出 datasource was not found 这类错误。固定 UID 很简单看前面 provisioning 配置里的uid: victorialogs-prod。以后不管数据源怎么迁移、重建只要 UID 不变面板引用就永远有效。这个习惯真的能帮你省去大量维护 Dashboard 的心力。7. 生产环境踩坑:升级、时区、字段类型的血泪7.1 Grafana 报datasource im7_otuvz was not found是怎么回事这个报错我在升级 Grafana 和 VictoriaLogs 插件后遇到过网上问的人也很多。报错原文是:failed to upgrade legacy queries datasource im7_otuvz was not found核心原因是:Grafana 在加载 Dashboard 时发现某个 Panel 引用的数据源 UID 是im7_otuvz但当前数据源列表里已经找不到这个 UID 了。常见触发场景有:卸载并重装了某个日志数据源插件把 Grafana 从旧版本升级旧版的数据源 UID 和插件类型写法不兼容从其他环境导入了 Dashboard但原环境的数据源 UID 没同步过来。解决思路也不是让你去恢复什么遗留数据而是把面板的引用改到新数据源上。最粗暴但最有效的方式是:在 Grafana 的 Data Sources 页面找到当前可用的 VictoriaLogs 数据源复制它的 UIDURL 上能看到。打开报错的 Dashboard进入 JSON Model。搜索im7_otuvz把所有datasource: {type: ..., uid: im7_otuvz}替换成新的 UID。保存 Dashboard。如果面板比较多直接在 JSON 里全局替换是最快的。如果只有一两个 Panel 坏了也可以直接删掉旧 Panel 重新建。为了避免以后再犯记得用 provisioning 把 UID 固定下来就像 6.3 节说的那样。7.2 时间戳来源不一致日志跑到奇奇怪怪的时间线我有一次查日志发现明明刚产生的访问日志在 vmui 里搜索不到但过一阵子又能搜到了。后来发现是时间戳问题:Promtail 没有从日志里提取时间字段VictoriaLogs 默认用了摄入时间作为日志时间而 Promtail 的摄入时间又可能因为网络缓冲区、批量推送存在几秒到几十秒的延迟。如果你希望日志时间反映请求真实发生的时间必须让 Promtail 解析日志里的时间字段。这就是 4.4 节配置里那段 timestamp stage 存在的意义。还有一个容易踩的时区坑:Promtail 的 timestamp stage 在解析$time_local这种不带时区的字符串时会默认当成 UTC 处理而 Nginx 的$time_local是服务器本地时间。如果服务器是东八区解析出来的时间就会慢 8 小时查询结果看起来很诡异。解决办法就是我在 4.2 节强调的:log_format 里用$time_iso8601它带完整的时区偏移Promtail 按 RFC3339 解析就不会错。7.3 同名字段的类型战争这个坑最隐蔽也最折磨人。现象是:同一个字段名 status在部分日志里是数字 200在另一部分日志里是字符串 200结果查询status:499时报错或者统计时结果对不上。造成这种问题的原因通常有两个:Nginx log_format 里某个字段的引号写得不一致:有的 server 块用了旧的 combined 格式有的用了新的 JSON 格式Promtail/Fluent Bit 在采集端对字段做了处理比如通过 labels 把值强制变成了字符串。解决思路也很简单:从源头统一。第一所有 Nginx 接入平台的日志格式必须一致第二采集端不要把数值字段提成标签。如果已经出现一批脏数据VictoriaLogs 在查询时可以通过类型转换函数处理但更重要的是把采集链路的规范定住避免继续产生脏数据。7.4 VictoriaLogs 自身也要监控日志平台是给整个团队兜底的但它自己挂了往往没人及时发现。VictoriaLogs 暴露了/metrics端点格式兼容 Prometheus/VictoriaMetrics可以直接接到 Grafana 里监控。我建议至少盯四个指标:进程在线状态:VictoriaLogs 进程挂了要第一时间告警磁盘使用率:数据目录磁盘满了写入会直接失败每日摄入量:看每天的字节数有没有异常突增或突降查询延迟:监控查询 P99如果变慢说明要么数据量涨了要么查询语法不够高效。如果你已经在跑 Prometheus 或者 VictoriaMetrics直接加一个 scrape job 指向 VictoriaLogs 的 9428 端口就行。这一步看起来不是核心功能但生产环境里的稳定性全靠这些细节点撑住。最后再分享一个我自己实操中最受益的小习惯:日常临时查日志我很少先打开 Grafana而是直接用 VictoriaLogs 自带的 vmui 界面。界面里可以点字段过滤、看时间分布响应速度快而且不会占用 Grafana 的并发查询额度。Grafana 更多是留给固定面板 团队共享vmui 则是个人排障的第一站。这套组合用下来我们团队查 Nginx 日志的平均耗时从十几分钟降到了一两分钟这大概就是工具选对了之后的真实体感。
返回列表