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.yml | OpenObserve、Collector、Pyroscope 与两个 Exporter |
build/observability/otel-collector.yaml | OTLP、hostmetrics、Prometheus receiver 与 OpenObserve exporter |
build/observability/.env.example | 部署变量模板,不包含真实凭据 |
build/observability/openobserve-dashboard-mysql-redis.json | MySQL/Redis 预置 Dashboard |
当前 Compose 固定使用:
openobserve/openobserve:v0.91.3otel/opentelemetry-collector-contrib:0.157.0prom/mysqld-exporter:v0.19.0oliver006/redis_exporter:v1.88.0grafana/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-collector 和 pyroscope 访问后端。
:::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_EMAIL、OPENOBSERVE_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
当前端口暴露策略:
| 服务 | 宿主端口 | 说明 |
|---|---|---|
| OpenObserve | 5080 | Web UI 与 API |
| Pyroscope | 4040 | Profile 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 1、redis_up 1。若为 0,先检查 exporter 日志、目标地址、ACL/授权和防火墙。
验证应用数据
- 确认应用容器与
otel-collector同在freekit-net。 - 访问普通 API 或触发 Job/Message 的真实任务。
- 查看 Collector 是否出现导出或认证错误。
- 登录
http://服务器IP:5080,选择默认组织default。 - 在 Traces、Logs、Metrics 中按
service.name查找freekit-host、freekit-job、freekit-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_up、mysql_global_status_threads_connected、mysql_global_status_queries、mysql_global_status_slow_queries - Redis:
redis_up、redis_connected_clients、redis_memory_used_bytes、redis_keyspace_hits_total、redis_keyspace_misses_total、redis_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 的 *_up 为 1,再检查 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-job、freekit-message。
:::warning 镜像内置不等于部署脚本均已启用
build/deploy_on_server.sh为 API 传入了上述 Pyroscope 变量。build/deploy_freekit_host.bash、build/deploy_job_host.bash、build/deploy_message_host.bash当前没有传入这些 Pyroscope 应用变量。- 三个 Dockerfile 虽然内置 profiler,但使用后面三个脚本部署时,必须额外补齐运行时配置,才能确认数据发往正确的 Pyroscope 服务。
:::
CPU 与 Wall Time 通常适合作为常驻基线;Allocation 与 Heap 会增加更多开销,应先在可控实例上评估,再决定是否长期启用。Pyroscope 不经过 Collector,数据也不会显示在 OpenObserve UI。
常见问题
Collector 返回 401 Unauthorized
检查:
.env中OPENOBSERVE_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 是
grpc或http/protobuf - 检查 Collector 的 traces pipeline 与日志
有 Trace/Metrics 但没有 Logs
Logs 使用独立的 Serilog Sink。检查对应 Host 的 appsettings.Production.json 是否加载、Sink endpoint 是否可达、最低日志级别是否过滤了目标日志。
安全与备份
- 仅允许管理员来源访问宿主机
5080和4040 - 推荐通过 Nginx/Caddy 提供 HTTPS,并关闭公网直接端口
- Collector
4317/4318不应无认证暴露公网 - Pyroscope 默认 UI 不应直接暴露到不受信网络
.env权限保持600,不要提交仓库- 定期备份
openobserve_data与pyroscope_datavolume /:/hostfs:ro给予 Collector 较广的宿主只读视图,只应在受信镜像和受控服务器上使用- 默认关闭 SQL 文本采集;如临时开启,设置访问控制和回收时间
相关文档与源码
- 可观测性总览与应用接入
- 独立 Collector 调试验证
build/observability/docker-compose.ymlbuild/observability/otel-collector.yamlbuild/observability/.env.examplebuild/observability/openobserve-dashboard-mysql-redis.jsonsrc/Services/Host/FreeKit.Host/Dockerfilesrc/Services/Host/FreeKit.Job.Host/Dockerfilesrc/Services/Host/FreeKit.MessageHandler.Host/Dockerfile