Files
fire-safety-ymd/docs/project/operations/docker-test-deployment.md
2026-09-05 15:46:37 +08:00

16 KiB
Raw Blame History

测试服务器 Docker 部署手册

项 约定
目标服务器目录 /home/firee-safety-ymd
运行方式 宿主机 Nginx 终止 HTTPS,Docker Compose 运行单个 Go 服务
应用监听边界 只把容器端口发布到宿主机 127.0.0.1:8080
数据库 使用现有 PostgreSQL/PostGIS;本手册不创建数据库容器、不导入 SQL
状态 测试环境部署基线;目标服务器、公网 DNS/TLS 和 SuperAgent 回调仍需现场验证

本手册不包含真实 Token、API Key、数据库密码或旧 Nginx 配置内容。命令中的尖括号是服务器上需要替换的占位符;不要把 Secret 写进命令行、Nginx 文件、镜像构建参数或 Git。

1. 部署前提

本仓库在本手册编写时尚无 Git 提交。服务器不能直接 clone 当前工作区,必须由项目维护者先审阅变更、手动 commit 并 push 到远程仓库;本项目不会自动提交、推送或迁移服务器数据。以下命令以发布 main 分支为例。

在开发机提交前先检查暂存区;当前已有两份用户 Excel 处于暂存状态,git commit 会把它们一并提交,除非维护者明确取消暂存。不要把 .env 或被忽略的原始 SQL 强制加入 Git:

cd /Users/andy/IdeaProjects/fire-safety-ymd
git status --short
git diff --cached --stat
# 审阅后通过 IDE 或逐项执行 git add,只暂存确定要发布的路径。
git commit -m 'bootstrap fire-safety test deployment'
git push -u origin main

服务器需要具备:

  • Git、Docker Engine 和 Docker Compose v2(命令为 docker compose)。
  • 宿主机 Nginx、可由 Nginx 读取的 agent.nianxx.com 证书和私钥。
  • 到 PostgreSQL/PostGIS 的网络访问;数据库账号应为运行时只读账号。
  • 如由 SuperAgent 访问 MCP,防火墙、安全组和 TLS 必须允许按约定访问 443。

先确认工具版本,不要把配置文件内容打印到日志:

git --version
docker --version
docker compose version
nginx -v

如果 Nginx 尚未安装,可按发行版选择一种方式;用户已安装时跳过:

# Debian/Ubuntu
sudo apt-get update
sudo apt-get install -y nginx
# RHEL/CentOS/Fedora 系
sudo dnf install -y nginx

安装后确认服务:

sudo systemctl enable --now nginx
sudo systemctl status nginx --no-pager

2. 获取代码到 /home/firee-safety-ymd

首次部署

维护者完成 commit/push 后,在服务器上执行。私有仓库使用服务器已有的 Git 凭证管理方式,不要把 Git 密码或 Token 写入命令:

sudo mkdir -p /home/firee-safety-ymd
sudo chown "$(id -un):$(id -gn)" /home/firee-safety-ymd
git clone https://git.nianxx.cn/huangting/fire-safety-ymd.git /home/firee-safety-ymd
cd /home/firee-safety-ymd
git switch main

如果目录已经是本仓库的工作副本,先确认远程、分支和工作树,再只做快进更新:

cd /home/firee-safety-ymd
git remote -v
git status --short
git fetch --prune origin
git switch main
git pull --ff-only origin main

若 git status --short 有服务器本地改动,先确认归属;不要用 git reset --hard 或覆盖本地文件排障。

3. 创建服务器 .env

.env.example 只是字段说明,不能作为运行时 Secret 文件。首次部署时复制并收紧权限:

cd /home/firee-safety-ymd
cp .env.example .env
chmod 600 .env

用服务器上的受保护编辑器填写 .env,至少确认以下值。三个凭证必须分别生成,不能相互复用:

FIRE_SAFETY_HTTP_ADDR=:8080

FIRE_SAFETY_SUPERAGENT_ENABLED=true
FIRE_SAFETY_SUPERAGENT_BASE_URL=https://<superagent-api-host>
FIRE_SAFETY_SUPERAGENT_OPEN_API_KEY=<superagent-open-api-key>

FIRE_SAFETY_CHAT_ENABLED=true
FIRE_SAFETY_CHAT_AUTH_TOKEN=<独立的高熵xtoken>
FIRE_SAFETY_CHAT_SUBJECT_ID=fire-safety-ymd-chat-test-subject
FIRE_SAFETY_CHAT_COMPAT_APP_ID=<与Nginx路径完全一致的公开app-id>
# 仅浏览器实际 Origin;CLI/服务端调用可以留空
FIRE_SAFETY_CHAT_ALLOWED_ORIGINS=https://<actual-frontend-origin>

FIRE_SAFETY_MCP_ENABLED=true
FIRE_SAFETY_MCP_AUTH_TOKEN=<独立的高熵MCP-bearer>
FIRE_SAFETY_MCP_SCOPE_MODE=all
FIRE_SAFETY_MCP_ALLOWED_TOWNS=

FIRE_SAFETY_POSTGIS_ENABLED=true
FIRE_SAFETY_POSTGIS_DSN=<只读PostgreSQL连接串>
FIRE_SAFETY_POSTGIS_MIGRATION_DSN=
FIRE_SAFETY_POSTGIS_EXPECTED_SRID=4326

注意:

  • FIRE_SAFETY_CHAT_AUTH_TOKEN 只用于兼容对话入口的 xtoken;FIRE_SAFETY_MCP_AUTH_TOKEN 只用于 SuperAgent 调用 /mcp;FIRE_SAFETY_SUPERAGENT_OPEN_API_KEY 只用于 Go 服务访问 SuperAgent。三者必须不同。
  • FIRE_SAFETY_CHAT_COMPAT_APP_ID 是公开路径标识,不是 Secret;它必须与 Nginx 的精确 location = /api/v1/apps//completion 完全一致。
  • 使用 FIRE_SAFETY_MCP_SCOPE_MODE=all 时,FIRE_SAFETY_MCP_ALLOWED_TOWNS 必须保持为空。all 是服务账号级的固定查询表范围,不是最终用户级授权。
  • FIRE_SAFETY_POSTGIS_MIGRATION_DSN 不应配置给运行服务;迁移凭证只供一次性迁移命令使用,完成后应移除。
  • FIRE_SAFETY_HTTP_ADDR 在容器内应为 :8080,安全边界由 Compose 的 127.0.0.1:8080:8080 和宿主机 Nginx 提供;不要在 Compose 场景设为容器内的 127.0.0.1:8080。
  • 不要用没有 --quiet 的 docker compose config 或 docker inspect 把完整环境渲染到终端、CI 日志或工单;检查 .env 权限仍为 600。

4. 容器访问现有 PostgreSQL/PostGIS

本项目不在 Compose 中创建或初始化数据库。数据库继续由现有 PostgreSQL/PostGIS 运维,Go 服务只使用已完成 SRID 确认的只读数据源。

如果 PostgreSQL 在另一台机器:

  • DSN 使用数据库私网 DNS 或私网地址,不使用容器内的 127.0.0.1。
  • 数据库防火墙只允许测试服务器访问 5432;如启用 TLS,按数据库证书要求配置 DSN 的 sslmode,不要为了省事关闭证书校验。

如果 PostgreSQL 与 Docker 在同一台宿主机:

  • 容器内的 127.0.0.1 指向容器自身;DSN 使用 Compose 声明的 host.docker.internal(或运维明确提供的 Docker 网关地址)。
  • Linux Compose 需要有 host.docker.internal:host-gateway 映射;本仓库的 Compose 配置包含该映射时才能使用这个名称。
  • PostgreSQL 的 listen_addresses、pg_hba.conf 和主机防火墙只允许 Docker 网段中的只读账号访问,不要发布 5432 到公网。
  • 不要把迁移账号放入 .env;数据库配置变更先由管理员验证最小权限。

服务启动时进行 MCP PostGIS readiness 校验。SRID、几何类型或 WGS84 范围硬门禁失败会阻止服务装配;空/无效几何和缺 GiST 索引是当前已知 warning。GET /health 只是进程存活检查,不代表数据库、SuperAgent 或 MCP 已可用,必须同时检查 Compose 日志和实际接口。

5. 构建、启动和本机检查

以下命令在服务器项目目录执行。Compose 构建上下文不把 .env 作为 Dockerfile 构建参数:

cd /home/firee-safety-ymd
docker compose config --quiet
docker compose build --pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 api
curl --fail http://127.0.0.1:8080/health

预期结果:容器为 Up(健康状态由 Compose 显示),health 返回 HTTP 200;有限日志不应出现 DSN、密码、Token 或 API Key。docker compose config --quiet 只验证配置,不输出展开后的 Secret。若 Compose 不支持 --quiet,升级 Compose 或使用不会回显结果的等价校验方式。

失败时只读取有限日志:

docker compose ps
docker compose logs --tail=200 api

不要把完整日志粘贴到公共工单;先删去 URL 凭证、连接串、Header、联系人和精确位置。readiness 报错时核对数据库地址、只读权限和 FIRE_SAFETY_POSTGIS_EXPECTED_SRID=4326,不要关闭门禁或修改原始几何。

6. 安装/替换宿主机 Nginx 配置

仓库模板为 deploy/nginx/fire-safety-ymd.conf.example,不含真实 Secret。它把精确 Chat 兼容路径、/mcp 和可选 /health 反代到 127.0.0.1:8080,其他路径返回 404;详细 SSE 边界见 nginx-public-entry.md。

先只查找拥有 agent.nianxx.com 的配置文件名,避免使用 nginx -T 把旧文件中的 DashScope Key 打到终端:

sudo grep -RIl \
  'server_name[[:space:]]\+agent\.nianxx\.com' \
  /etc/nginx/conf.d /etc/nginx/sites-enabled 2>/dev/null

旧配置迁移前:

  1. 记录旧配置文件路径和归属;不要把旧配置内容复制到仓库或新模板。
  2. 如需回滚,将旧文件以 root 权限备份到不被 Nginx include 的目录并限制为 600;若含真实 DashScope Key,应由凭证所有者安排轮换。
  3. 把旧文件移出 *.conf include(例如改为 .disabled),避免同一域名出现两个冲突的 server 块。移动前确认目标是上一步查到的精确路径,不要批量操作整个 /etc/nginx。

部署模板的示例(目录因发行版而异;使用 sites-enabled 时按实际 include 位置调整):

sudo install -o root -g root -m 0644 \
  deploy/nginx/fire-safety-ymd.conf.example \
  /etc/nginx/conf.d/fire-safety-ymd.conf
sudo vi /etc/nginx/conf.d/fire-safety-ymd.conf

编辑时完成以下非 Secret 替换:

  • 将 replace-with-fire-safety-app-id 替换为 .env 中的 FIRE_SAFETY_CHAT_COMPAT_APP_ID。
  • 确认证书路径、域名和上游 127.0.0.1:8080 符合测试服务器。
  • 保留精确 location、SSE 的 proxy_buffering off/超时设置和其他路径的 404;不要恢复旧配置的 location /、DashScope proxy_pass、Authorization Key 或 Nginx if Token 比较。
  • limit_req_zone 必须位于 Nginx http 上下文;模板假定由 conf.d/*.conf 在 http {} 中 include。若 include 层级不同,把指令移到合法的 http 上下文。

确认旧配置已禁用且新文件不含 Secret 后,再验证并 reload:

sudo nginx -t
sudo systemctl reload nginx

如果 Nginx 不由 systemd 管理,使用发行版的 reload 方式;reload 前必须先通过 nginx -t。

7. 公网 Chat SSE 冒烟

先验证宿主机本地 /health 和 HTTPS /health,再用不含敏感信息的问题验证 SSE。将 Token 安全注入当前 shell(例如受保护的 Secret 管理器或交互式读取),不要把字面量写入 shell 历史:

read -rsp 'Chat xtoken: ' CHAT_XTOKEN
printf '\n'

首轮请求:

curl -N --fail \
  -H "xtoken: $CHAT_XTOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  --data '{"input":{"prompt":"观水镇附近有哪些水源候选?"},"parameters":{}}' \
  'https://agent.nianxx.com/api/v1/apps/<same-safe-app-id>/completion'

确认能读取 event: result,最后一个成功事件的 output.finish_reason 为 stop。只有 stop 事件的 output.text 可当作完成;中途断流、HTTP 错误或只有 finish_reason=null 不得当作可信答案。output.session_id 是本项目本地会话 ID,不是 SuperAgent 内部 Session ID。

后续轮次使用上一轮成功返回的本地会话 ID:

curl -N --fail \
  -H "xtoken: $CHAT_XTOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  --data '{"input":{"prompt":"再说明候选的限制","session_id":"<session-id-from-previous-response>"},"parameters":{}}' \
  'https://agent.nianxx.com/api/v1/apps/<same-safe-app-id>/completion'

至少验证:错误 xtoken 为 401;不存在的 app ID 和未列出的路径为 404。不要使用真实联系人、电话或精确敏感位置作为冒烟问题。

完成 Chat 测试后清理当前 shell 中的临时变量:

unset CHAT_XTOKEN

8. SuperAgent MCP 回调配置与冒烟

在 SuperAgent 的工具/MCP 配置中新增远程 MCP 服务:

URL: https://agent.nianxx.com/mcp
Method: POST
Authorization: Bearer <FIRE_SAFETY_MCP_AUTH_TOKEN>

该 Bearer 必须是 .env 中独立的 FIRE_SAFETY_MCP_AUTH_TOKEN,不要填 Chat xtoken 或 SuperAgent Open API Key。SuperAgent 还可能需要公网来源 IP 白名单、TLS CA 或自定义 Header,按实际平台和网络策略确认。

在服务器或受控终端用独立变量测试初始化:

read -rsp 'MCP bearer: ' MCP_BEARER
printf '\n'
curl --fail \
  -H "Authorization: Bearer $MCP_BEARER" \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"manual-probe","version":"1"}}}' \
  'https://agent.nianxx.com/mcp'

curl --fail --output /dev/null --write-out 'initialized HTTP %{http_code}\n' \
  -H "Authorization: Bearer $MCP_BEARER" \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  'https://agent.nianxx.com/mcp'

curl --fail \
  -H "Authorization: Bearer $MCP_BEARER" \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  'https://agent.nianxx.com/mcp'

确认第一步返回 protocolVersion: 2025-06-18,第二步返回 HTTP 202,第三步列出 7 个固定工具;再用无敏感信息的已知地名/点位做少量只读调用,核对正确 Bearer 成功、错误/缺失 Bearer 为 401、warnings 保留、非法参数不会变成任意 SQL。查询到资源记录不代表实时可用、路线已规划或正式集结点。

完成 MCP 测试后清理当前 shell 中的临时变量:

unset MCP_BEARER

9. 更新、重启与回滚

发布新版本

维护者先将目标版本 commit/push 后,服务器快进更新并重建:

cd /home/firee-safety-ymd
git fetch --prune origin
git switch main
git pull --ff-only origin main
docker compose config --quiet
docker compose build --pull
docker compose up -d --remove-orphans
docker compose ps
docker compose logs --tail=100 api

更新会重启单个 Go 进程,现有内存会话丢失;客户端按会话失效处理并重新开始对话。当前部署不支持多实例共享会话,也没有持久化历史或审计。

回滚

回滚到上一个已验证的 Git commit 或镜像版本。先保留现状信息并确认目标版本;不要用破坏性 reset 覆盖未知的服务器本地改动:

cd /home/firee-safety-ymd
git status --short
git log --oneline -5

由维护者确认目标 commit(例如 <verified-commit>)后,先以 detached HEAD 检出该已验证版本,再重新构建启动;这是显式回滚,不会自动覆盖服务器本地改动:

git fetch --prune origin
git switch --detach <verified-commit>
docker compose build --pull
docker compose up -d --remove-orphans
docker compose ps

回滚结束并准备恢复正常发布轨道时,再切回维护者指定的分支:

git switch main
git pull --ff-only origin main

如果是 Nginx 配置导致故障,先恢复 root-only 备份,确认旧域名配置没有重复 include,再执行:

sudo nginx -t
sudo systemctl reload nginx

回滚后重新检查本机和公网 /health、Chat 401/404 边界以及 MCP 初始化。docker compose down 只停止容器,不删除代码或数据库;明确需要停服时再执行:

cd /home/firee-safety-ymd
docker compose down

10. 完成判定与当前限制

本手册完成不代表公网部署已完成。现场交付至少应记录:

  • 目标机 Compose 配置校验、build、up、健康检查和无 Secret 的有限日志结果。
  • 目标机 nginx -t 和 reload 成功;443 证书、DNS、安全组及旧 location / 已确认不再生效。
  • Chat 首轮/多轮 SSE、错误凭证、未知 app/path 的实际 HTTPS 响应。
  • SuperAgent -> /mcp 的独立 Bearer、TLS/网络白名单、工具发现和至少一个受控只读工具调用。
  • 数据库未公开 5432,运行账号保持只读,PostGIS readiness warning 和 35 条无效面几何缺口已记录。

若目标服务器无法使用 host.docker.internal、Compose 不支持 --quiet、Nginx include 目录不同、证书路径不同或 SuperAgent 对 MCP 的认证格式不同,应先记录实际环境并调整部署 Spec;不要通过放开端口、写入 Token、关闭 readiness 或恢复全路径反代规避问题。