跳到主要内容

OpenObserve 完整部署

仓库的 build/observability/ 提供了与当前生产接入方式一致的可观测性栈:

FreeKit.Host / FreeKit.Job.Host / FreeKit.MessageHandler.Host
├── OpenTelemetry SDK ── Trace + Metrics ──┐
├── Serilog OTLP Sink ── Logs ─────────────┤
│ ▼
│ OpenTelemetry Collector
│ │ OTLP/HTTP + Basic Auth
│ ▼
│ OpenObserve

└── Pyroscope native profiler ─────────► Pyroscope

宿主机 ── hostmetrics ─────────────────────► Collector
MySQL ── mysqld_exporter ──────────────────► Collector
Redis ── redis_exporter ───────────────────► Collector

OpenObserve 存储和查询 Trace、Metrics、Logs;Pyroscope 单独保存性能剖析数据,二者不是同一个 UI。

仓库文件与固定版本

文件用途
build/observability/docker-compose.ymlOpenObserve、Collector、Pyroscope 与两个 Exporter
build/observability/otel-collector.yamlOTLP、hostmetrics、Prometheus receiver 与 OpenObserve exporter
build/observability/.env.example部署变量模板,不包含真实凭据
build/observability/openobserve-dashboard-mysql-redis.jsonMySQL/Redis 预置 Dashboard

当前 Compose 固定使用:

  • openobserve/openobserve:v0.91.3
  • otel/opentelemetry-collector-contrib:0.157.0
  • prom/mysqld-exporter:v0.19.0
  • oliver006/redis_exporter:v1.88.0
  • grafana/pyroscope:2.2.0

升级前应先检查对应组件的配置兼容性,避免直接改成 latest

1. 创建共享 Docker 网络

可观测性 Compose 声明 freekit-net 为 external network,因此启动前必须创建:

docker network inspect freekit-net >/dev/null 2>&1 || \
docker network create freekit-net

API、Job、Message 只有加入同一个网络,才能通过容器名 otel-collectorpyroscope 访问后端。

:::warning 多套 Compose 的网络边界

build/compose/freekit_pro_modules/docker-compose.yml 当前使用自己的默认网络,并不会因为名字相近而自动加入 freekit-net。如果应用由该 Compose 启动,需要在应用 Compose 中显式接入 external network,或把 endpoint 改成应用实际可达的 Collector 地址。

:::

2. 准备部署目录和环境变量

将整个 build/observability/ 目录上传到服务器,例如:

/opt/freekit-observability/
├── docker-compose.yml
├── otel-collector.yaml
├── openobserve-dashboard-mysql-redis.json
└── .env.example

复制变量模板:

cd /opt/freekit-observability
cp .env.example .env
chmod 600 .env

编辑 .env

OPENOBSERVE_ROOT_EMAIL=admin@example.com
OPENOBSERVE_ROOT_PASSWORD=replace-with-a-strong-password
OPENOBSERVE_AUTH_HEADER=Basic replace-with-base64-email-and-password

MYSQL_EXPORTER_ADDRESS=host.docker.internal:25324
MYSQL_EXPORTER_USER=freekit_exporter
MYSQL_EXPORTER_PASSWORD=replace-with-mysql-exporter-password

REDIS_EXPORTER_ADDRESS=redis://host.docker.internal:26739
REDIS_EXPORTER_USER=freekit_exporter
REDIS_EXPORTER_PASSWORD=replace-with-redis-exporter-password

OPENOBSERVE_AUTH_HEADER 是 Collector 写入 OpenObserve /api/default 时使用的 Basic Auth。生成值:

printf '%s' 'admin@example.com:replace-with-a-strong-password' | base64 -w0

把输出追加到 Basic 后。邮箱和密码必须与 OPENOBSERVE_ROOT_EMAILOPENOBSERVE_ROOT_PASSWORD 完全一致。

不要提交 .env、Base64 结果或生产密码。Base64 是编码,不是加密。

3. 创建最小权限监控账号

MySQL

Exporter 默认通过 host.docker.internal 访问宿主机映射端口。创建独立监控用户,并让密码与 .env 一致:

CREATE USER 'freekit_exporter'@'%' IDENTIFIED BY 'replace-with-mysql-exporter-password'
WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'freekit_exporter'@'%';

不要复用应用或 root 账号。若 MySQL 不在 Docker 宿主机上,将 MYSQL_EXPORTER_ADDRESS 改为容器可达的内网地址。

Redis 7+

为 Redis Exporter 创建独立 ACL 用户:

ACL SETUSER freekit_exporter reset on >replace-with-redis-exporter-password ~* &* -@all +@connection +@read +info +client +config|get +slowlog +memory +latency +scan

若 Redis 不在 Docker 宿主机上,同样将 REDIS_EXPORTER_ADDRESS 改成 freekit-net 内可达的地址。

4. 校验并启动

cd /opt/freekit-observability
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps

当前端口暴露策略:

服务宿主端口说明
OpenObserve5080Web UI 与 API
Pyroscope4040Profile UI
Collector OTLP不映射freekit-net 内的 4317/4318
MySQL Exporter不映射仅网络内 9104
Redis Exporter不映射仅网络内 9121

Collector 以只读方式挂载 /:/hostfs:ro,每 30 秒采集 CPU、负载、内存、磁盘、文件系统、网络和分页指标,并过滤 overlay、/proc/sys 等虚拟文件系统。

5. 接入 FreeKit 三个宿主

Trace/Metrics 的推荐配置:

OpenTelemetry__Enabled=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc

每个容器设置独立服务名:

FreeKit.Host OTEL_SERVICE_NAME=freekit-host
FreeKit.Job.Host OTEL_SERVICE_NAME=freekit-job
FreeKit.MessageHandler.Host OTEL_SERVICE_NAME=freekit-message

三个生产 appsettings.Production.json 已包含相同 endpoint 和各自服务名;环境变量会优先覆盖 Trace/Metrics 配置。Logs 由各生产配置中的 Serilog.Sinks.OpenTelemetry 单独发送,变更 Collector 地址时也要同步检查 Serilog Sink endpoint。

完整应用侧行为见 可观测性总览与应用接入

6. 验证服务和采集链路

宿主机可直接检查的服务

curl -fsS http://127.0.0.1:5080/healthz
docker logs --tail 100 openobserve
docker logs --tail 100 otel-collector
docker logs --tail 100 pyroscope
docker logs --tail 100 mysql-exporter
docker logs --tail 100 redis-exporter

OpenObserve 正常时返回:

{"status":"ok"}

只在 Docker 网络内可访问的端点

Exporter 和 Collector 健康端口没有映射到宿主机。不要在宿主机直接执行 curl http://mysql-exporter:9104/...,容器名只在 Docker 网络内解析。

可使用临时 curl 容器验证:

docker run --rm --network freekit-net curlimages/curl:8.12.1 \
-fsS http://otel-collector:13133/

docker run --rm --network freekit-net curlimages/curl:8.12.1 \
-fsS http://mysql-exporter:9104/metrics | grep '^mysql_up'

docker run --rm --network freekit-net curlimages/curl:8.12.1 \
-fsS http://redis-exporter:9121/metrics | grep '^redis_up'

预期 mysql_up 1redis_up 1。若为 0,先检查 exporter 日志、目标地址、ACL/授权和防火墙。

验证应用数据

  1. 确认应用容器与 otel-collector 同在 freekit-net
  2. 访问普通 API 或触发 Job/Message 的真实任务。
  3. 查看 Collector 是否出现导出或认证错误。
  4. 登录 http://服务器IP:5080,选择默认组织 default
  5. 在 Traces、Logs、Metrics 中按 service.name 查找 freekit-hostfreekit-jobfreekit-message

/health 的 ASP.NET Core Trace 被应用侧过滤,不能用它验证入站 Trace。它仍适合健康检查,但应另外访问普通接口。

指标可从以下名称或前缀开始排查:

  • 应用:http.server.*http.client.*process.runtime.dotnet.*
  • 宿主:system.cpu.*system.memory.*system.filesystem.*system.network.*system.paging.*
  • MySQL:mysql_upmysql_global_status_threads_connectedmysql_global_status_queriesmysql_global_status_slow_queries
  • Redis:redis_upredis_connected_clientsredis_memory_used_bytesredis_keyspace_hits_totalredis_keyspace_misses_totalredis_slowlog_length

Collector 对 MySQL/Redis 使用低基数白名单。mysql_perf_schema_events_statements 当前不在白名单内,不应作为验证指标。

7. 导入 MySQL/Redis Dashboard

仓库的 openobserve-dashboard-mysql-redis.json 使用当前 Collector 保留的指标,包括:

  • MySQL 可用性、连接、查询量、慢查询、Buffer Pool
  • Redis 可用性、连接、内存、命令量、命中率、慢日志

在 OpenObserve Dashboard 页面导入该 JSON。若面板无数据,先确认对应 exporter 的 *_up1,再检查 Collector 日志和 Metrics stream。

Pyroscope 性能剖析

Pyroscope UI 默认是:

http://服务器IP:4040

三个 Host Dockerfile 均包含 Pyroscope .NET profiler 1.4.0 和 CLR profiler 基础环境变量。运行容器时还需要:

-e PYROSCOPE_APPLICATION_NAME=freekit-host \
-e PYROSCOPE_SERVER_ADDRESS=http://pyroscope:4040 \
-e PYROSCOPE_PROFILING_ENABLED=1 \
-e PYROSCOPE_PROFILING_CPU_ENABLED=1 \
-e PYROSCOPE_PROFILING_WALLTIME_ENABLED=1 \
-e PYROSCOPE_PROFILING_ALLOCATION_ENABLED=1 \
-e PYROSCOPE_PROFILING_HEAP_ENABLED=1

Job 和 Message 的应用名应分别是 freekit-jobfreekit-message

:::warning 镜像内置不等于部署脚本均已启用

  • build/deploy_on_server.sh 为 API 传入了上述 Pyroscope 变量。
  • build/deploy_freekit_host.bashbuild/deploy_job_host.bashbuild/deploy_message_host.bash 当前没有传入这些 Pyroscope 应用变量。
  • 三个 Dockerfile 虽然内置 profiler,但使用后面三个脚本部署时,必须额外补齐运行时配置,才能确认数据发往正确的 Pyroscope 服务。

:::

CPU 与 Wall Time 通常适合作为常驻基线;Allocation 与 Heap 会增加更多开销,应先在可控实例上评估,再决定是否长期启用。Pyroscope 不经过 Collector,数据也不会显示在 OpenObserve UI。

常见问题

Collector 返回 401 Unauthorized

检查:

  • .envOPENOBSERVE_AUTH_HEADER 是否以 Basic 开头
  • Base64 原文是否严格为 email:password
  • 邮箱和密码是否与 OpenObserve root 用户一致
  • 修改 .env 后是否重建 Collector
docker compose up -d --force-recreate otel-collector

应用解析不到 otel-collector

检查网络:

docker network inspect freekit-net

输出的 Containers 中应同时包含应用容器和 otel-collector。容器内的 localhost 指向容器自己,不能代替 Collector 容器名。

OpenObserve 有指标但没有 Trace

  • 不要只请求 /health
  • 确认 OpenTelemetry.Enabled 或 endpoint 已启用 SDK
  • 确认 protocol 是 grpchttp/protobuf
  • 检查 Collector 的 traces pipeline 与日志

有 Trace/Metrics 但没有 Logs

Logs 使用独立的 Serilog Sink。检查对应 Host 的 appsettings.Production.json 是否加载、Sink endpoint 是否可达、最低日志级别是否过滤了目标日志。

安全与备份

  • 仅允许管理员来源访问宿主机 50804040
  • 推荐通过 Nginx/Caddy 提供 HTTPS,并关闭公网直接端口
  • Collector 4317/4318 不应无认证暴露公网
  • Pyroscope 默认 UI 不应直接暴露到不受信网络
  • .env 权限保持 600,不要提交仓库
  • 定期备份 openobserve_datapyroscope_data volume
  • /:/hostfs:ro 给予 Collector 较广的宿主只读视图,只应在受信镜像和受控服务器上使用
  • 默认关闭 SQL 文本采集;如临时开启,设置访问控制和回收时间

相关文档与源码

  • 可观测性总览与应用接入
  • 独立 Collector 调试验证
  • build/observability/docker-compose.yml
  • build/observability/otel-collector.yaml
  • build/observability/.env.example
  • build/observability/openobserve-dashboard-mysql-redis.json
  • src/Services/Host/FreeKit.Host/Dockerfile
  • src/Services/Host/FreeKit.Job.Host/Dockerfile
  • src/Services/Host/FreeKit.MessageHandler.Host/Dockerfile