diff --git a/app/main.py b/app/main.py index 7a3bffe..6b3ec04 100644 --- a/app/main.py +++ b/app/main.py @@ -120,6 +120,9 @@ if settings.cors_origins_list: allow_headers=["*"], ) +# 接口指标埋点(放在 CORS 之后 = 最外层:测到含 CORS 的完整耗时)。未配置观测时中间件自 no-op。 +app.add_middleware(RequestMetricsMiddleware) + @app.get("/health", tags=["meta"]) def health() -> dict[str, str]: diff --git a/deploy/nginx/app-api.shaguabijia.com.conf b/deploy/nginx/app-api.shaguabijia.com.conf index dcef0f7..4e72656 100644 --- a/deploy/nginx/app-api.shaguabijia.com.conf +++ b/deploy/nginx/app-api.shaguabijia.com.conf @@ -19,7 +19,11 @@ server { ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; - client_max_body_size 4m; + # 上传接口(反馈/上报截图、头像)业务上限 = 最多 6 张 × 每张 5MB + # (见 app _MAX_IMAGES / AVATAR_MAX_BYTES)≈ 30MB,留余量设 32m。 + # 低于此值时带截图的反馈会在到达 uvicorn 前就被 nginx 413,表现为「提交经常失败」 + # (纯文字反馈体积小、不受影响 → 呈现为「时好时坏」)。根治仍需客户端上传前压缩。 + client_max_body_size 32m; location / { proxy_pass http://127.0.0.1:8770; diff --git a/deploy/nginx/observe.shaguabijia.com.conf b/deploy/nginx/observe.shaguabijia.com.conf index dd2c01e..0c9e274 100644 --- a/deploy/nginx/observe.shaguabijia.com.conf +++ b/deploy/nginx/observe.shaguabijia.com.conf @@ -1,37 +1,28 @@ -# OpenObserve 监控台反代(单独子域名)。前提: -# - DNS: observe.shaguabijia.com A 记录 → 本机公网 IP -# - 证书: /etc/nginx/ssl/observe.shaguabijia.com.pem|.key(同现有域名放法) -# - OpenObserve 只绑 127.0.0.1:5080(见 docker-compose.prod.yml) -# 三重防护:IP 白名单 + nginx TLS + OpenObserve 自身登录。 -# 只你/少数人看的话,更省事的是 SSH 隧道(README「UI 访问」方案 A),不用域名/证书。 +# OpenObserve 监控台反代(observe.shaguabijia.com)。证书走 Certbot/Let's Encrypt,与 admin-web 一致。 +# +# 前置(一次性): +# 1) DNS: observe.shaguabijia.com A 记录 → 本服务器公网 IP +# 2) 证书: sudo certbot certonly --nginx -d observe.shaguabijia.com +# (options-ssl-nginx.conf / ssl-dhparams.pem 首次跑 certbot 时已生成,admin-web 在用即已存在) +# 3) OpenObserve 只绑 127.0.0.1:5080(见 docker-compose.prod.yml),本文件把它反代出公网 +# 4) nginx -t 通过后 systemctl reload nginx +# +# 安全:OO 有自身登录。监控台不必对全网裸开——本机办公网无固定出口 IP,故在 nginx 层加 Basic Auth 兜底; +# 将来有固定 IP 可改用【IP 白名单】块(更省事,可去掉 Basic Auth)。 -# 80 → 301 → 443 server { - listen 80; - listen [::]:80; server_name observe.shaguabijia.com; - return 301 https://$host$request_uri; -} - -# 443 SSL 反代到本地 OpenObserve (127.0.0.1:5080) -server { - listen 443 ssl http2; - listen [::]:443 ssl http2; - server_name observe.shaguabijia.com; - - ssl_certificate /etc/nginx/ssl/observe.shaguabijia.com.pem; - ssl_certificate_key /etc/nginx/ssl/observe.shaguabijia.com.key; - - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - ssl_session_cache shared:SSL:10m; - - # ★ 只放行你的固定出口 IP(办公/家宽)。监控台不必对全网开。 - allow 1.2.3.4; # ← 换成真实 IP,可多行 - deny all; client_max_body_size 10m; + # —— IP 白名单:办公网无固定出口 IP,暂不用;将来有固定 IP 可改用这块(比 Basic Auth 省事)—— + # allow 1.2.3.4; # ← 换成你的真实出口 IP,可多行 + # deny all; + + # —— Basic Auth:无固定 IP 的兜底密码(生成 .htpasswd_observe 的命令见 README/下方)—— + auth_basic "OpenObserve"; + auth_basic_user_file /etc/nginx/conf.d/.htpasswd_observe; + location / { proxy_pass http://127.0.0.1:5080; proxy_http_version 1.1; @@ -39,9 +30,28 @@ server { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - # OpenObserve 有实时/流式,需透传 WebSocket + # OpenObserve 有实时/流式面板,需透传 WebSocket proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 300s; } + + # IPv6 这行不带 ipv6only=on:该选项对 [::]:443 全局只能设一次,admin-web 那个 server 块已设(否则 nginx 报 duplicate listen options) + listen [::]:443 ssl; # managed by Certbot + listen 443 ssl; # managed by Certbot + ssl_certificate /etc/letsencrypt/live/observe.shaguabijia.com/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/observe.shaguabijia.com/privkey.pem; # managed by Certbot + include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot +} + +server { + if ($host = observe.shaguabijia.com) { + return 301 https://$host$request_uri; + } # managed by Certbot + + listen 80; + listen [::]:80; + server_name observe.shaguabijia.com; + return 404; # managed by Certbot } diff --git a/deploy/openobserve/dashboard-api-metrics.json b/deploy/openobserve/dashboard-api-metrics.json index 2812958..2563ad1 100644 --- a/deploy/openobserve/dashboard-api-metrics.json +++ b/deploy/openobserve/dashboard-api-metrics.json @@ -1,5 +1,6 @@ { "version": 8, + "dashboardId": "api-metrics", "title": "接口监控 (QPS / 耗时 / 错误率)", "description": "app-server 接口 QPS、P50/P95/P99 耗时、5xx 错误率。数据流 app_requests。", "role": "", diff --git a/deploy/openobserve/docker-compose.prod.yml b/deploy/openobserve/docker-compose.prod.yml index 4340915..ddf4b10 100644 --- a/deploy/openobserve/docker-compose.prod.yml +++ b/deploy/openobserve/docker-compose.prod.yml @@ -1,7 +1,7 @@ # 生产用 OpenObserve(单机)。相对本地版 docker-compose.yml 的区别: # - 端口只绑 127.0.0.1 → 公网/外网都到不了(UI 访问走 SSH 隧道或 nginx 反代,见 README) # - root 密码走环境变量(放同目录 .env,已 gitignore,勿提交) -# - 数据用 docker 命名卷(免 bind-mount 权限坑)+ 内存上限(与 app/PG 共存防抢内存) +# - 数据 bind-mount 到宿主 /data 分区(需预建目录 + 确认容器可写)+ CPU/内存上限(与 app/PG 共存防抢内存) # # 用法: # 1) 本目录建 .env(已 gitignore): @@ -10,18 +10,24 @@ # 3) 开机自起: sudo systemctl enable docker services: openobserve: - image: public.ecr.aws/zinclabs/openobserve:latest + image: public.ecr.aws/zinclabs/openobserve:v0.91.2 container_name: openobserve ports: - - "127.0.0.1:5080:5080" # ★只绑本机;写成 5080:5080 = 裸暴露公网,别这么干 + - "127.0.0.1:5080:5080" # 只绑本机,安全 environment: ZO_ROOT_USER_EMAIL: "admin@shaguabijia.com" ZO_ROOT_USER_PASSWORD: "${OO_ROOT_PASSWORD:?请先在 deploy/openobserve/.env 里设 OO_ROOT_PASSWORD}" ZO_DATA_DIR: "/data" + ZO_COMPACT_DATA_RETENTION_DAYS: "30" # 超 30 天自动删,防爆盘(默认 3650 天=10年) + ZO_TELEMETRY: "false" # 关匿名遥测(内网自用);变量名是 ZO_TELEMETRY,不是 *_ENABLED volumes: - - openobserve_data:/data # docker 命名卷;想固定到某块盘改成 bind: /your/disk:/data + - /data/openobserve/data:/data # 绑定挂载到宿主机的 /data/openobserve/data 目录(建议该目录所在分区有 20G+ 空间) restart: unless-stopped - mem_limit: 1g # 按机器内存调;查询重可给 2g - -volumes: - openobserve_data: + deploy: + resources: + limits: # 硬上限:防 OO 查询/ingest 抢爆 CPU/内存,拖垮同机 PG+app + cpus: '2.0' + memory: 3G + logging: # 容器 stdout 日志上限,防爆盘 + driver: json-file + options: { max-size: "10m", max-file: "3" }