OpenObserve + Vector:把 Nginx 日志接入自建可观测平台
内网服务越堆越多,日志全靠 tail 和 grep,出问题得挨个容器找。想找个开源可观测平台,ELK 太重,Loki 查询语法劝退,最后选了 OpenObserve(下称 O2)–单容器、Rust 写的、资源占用小。两天时间从零部署,再用 Vector 把 Nginx Proxy Manager 的访问日志接进去,全流程记一下。
为什么选 OpenObserve
ELK 那套 Elasticsearch + Logstash + Kibana,个人/小团队用就是杀鸡用牛刀,内存起步 4G。Loki + Grafana 轻是轻,但 LogQL 和查询体验实在提不起兴趣。O2 的卖点:
- 单二进制单容器,Rust 实现,内存占用小
- 日志/指标/链路追踪三合一,还带 Dashboard 和告警
- 数据落 Parquet 列存 + S3,官方号称存储成本比 ELK 低 140 倍
- UI 是类 Kibana 的查询界面,SQL 语法直接查
小规模自托管场景,O2 基本是目前最省心的选择。
部署形态
O2 生产模式建议外部 Postgres 存元数据,流数据落 S3。我的拓扑:
1 | 公网域名 -> NPM 反代 -> O2 容器 (host 网络, 端口 5080) |
Postgres 元数据库
O2 的组织、用户、Dashboard 定义都存 Postgres,流数据(日志本体)不进库。新建一个库就行:
1 | CREATE DATABASE openobserve; |
S3 侧给 O2 建一个专用账号,只授权目标 bucket 的 5 个最小 action,别图省事直接用 admin key。
容器配置
镜像用 Docker Hub 的 openobserve/openobserve:latest。别用 GHCR 源,拉镜像 403,我第一次就卡在这。
核心环境变量:
1 | environment: |
ZO_ROOT_USER_EMAIL/PASSWORD 不配容器直接 panic,起不来。
踩坑记录
坑 1:S3 provider 填 s3 会卡住
ZO_S3_PROVIDER=s3 会触发 AWS SDK 去 probe 169.254.169.254(EC2 元数据服务),自建机房没有这东西,进程卡在探测上不动。改成 minio 再加一个 AWS_EC2_METADATA_DISABLED=true,立刻正常。
坑 2:parquet job 疑似不上传
日志进来 UI 能查到,但 S3 bucket 里半天没文件,看起来像 parquet 合并任务没跑。其实是误判–O2 的数据先写本地 WAL,parquet job 每 2 秒扫一次 WAL,攒够一批才落 S3。把日志级别开到 debug 就能看到 executing job:
1 | RUST_LOG=info,openobserve_jobs::job::file::parquet=debug |
坑 3:断电最多丢 600 秒日志
WAL 是异步批量的,正常 docker stop 数据 100% 保留(实测过),但 SIGKILL/断电场景最多丢 600 秒窗口内的数据。这是 O2 的设计取舍,不是 bug。介意的话把 ZO_MAX_FILE_RETENTION_TIME 调小,代价是刷盘更频繁。
坑 4:Dashboard API 的 schema 混着大小写
用 API 批量建 Dashboard 得踩一遍 v3 schema:大部分字段是 camelCase,但 PanelFields 里的 stream_type/aggregation_function 是 snake_case,promql_legend 在 query.config 里面不在顶层。UI 手点没这问题,写脚本才知道疼。
Vector 采 NPM 日志
O2 部署完只是个空壳,得有数据进来。第一件事:把 NPM(Nginx Proxy Manager)的访问日志接进去。
为什么用 Vector 不用 O2 自带 agent:NPM 日志在宿主机文件里,Vector 的 file source + transform 够用,还有个现成的 NPM 日志解析逻辑。整个采集链:
1 | NPM 容器 /data/logs/*.log |
部署形态
Vector 和 NPM 放同一个 Portainer stack,NPM 的数据目录 bind mount 给 Vector:
1 | services: |
**别挂 /var/run/docker.sock**。网上教程清一色挂 sock,实际 file source 根本用不到 docker API,挂了纯属扩大攻击面。
vector.toml 核心配置
1 | [sources.npm_access] |
几个关键点:
- NPM 日志路径多一层:
/var/log/npm/logs/,不是/var/log/npm/ - framing 必须 array:O2 的 JSON ingest 端点拒收单个 JSON object,写成数组或者 content-type 用
application/json才行 - 自我循环过滤:Vector 往 O2 写数据的请求本身会经过 NPM 反代,落进 access log,又被 Vector 采集,无限套娃。过滤条件用 URI 精确匹配,别按 host 过滤–会把别的服务走 O2 的请求也误伤
- 别覆盖解析出来的 host 字段:给 Vector 自己的身份标记用
.agent_host,直接写.host会把日志里真实的 host 冲掉
建组织 + Token
O2 按 org 隔离数据,一个应用一个 org 是比较干净的做法:
1 | # 建 org(名字只能字母数字下划线,不许连字符) |
root 账号自动能访问所有 org,不用手动加成员。
效果
数据进来后建了个 Dashboard:请求 QPS、状态码分布、2xx/4xx/5xx 趋势、Top IP/URI/Host、错误趋势。以前”这个域名最近老 502”这种问题,现在是点两下的事。
NPM error log 也一并接了(同一套 file source + 正则,换个解析 regex),severity 字段天然适合做告警:severity = "error" AND msg LIKE "%upstream%",5 分钟出一条就推飞书。
结
两天从零到日志可查、Dashboard 可用。O2 这套对个人/小团队确实友好:部署简单、存储便宜、查询不卡。坑主要在 S3 provider、WAL 落盘机制和 Dashboard API 的 schema 这些角落里,踩一遍就知道怎么绕了。
日志只是第一步,指标(otelcol 采 PG/Redis)已经在路上,下一步把链路追踪也接进来。