Files
DadaoTask/2.Design/DEPLOY.md
T

6.5 KiB
Raw Blame History

部署(生产 · openclaw-01)

适用于当前唯一的线上环境:openclaw-01(192.168.8.188),访问入口 http://192.168.8.188:8200。 本地开发见 DEVELOPMENT.md。部署铁律见仓库根 AGENTS.md 第 2 节。

1. 生产拓扑

浏览器 ──HTTP──▶ openclaw-01:8200
                  └ gunicorn(2 × UvicornWorker) ── app.main:app
                        ├ /api/v1/**            业务 API
                        └ /                     前端产物 web/dist(单页应用,404 回退 index.html)
                                            │
                   ┌────────────────────────┼────────────────────────┐
                   ▼                        ▼                        ▼
        fnsvr-01:8432 PG 17.6         fnsvr-01:6379 Redis      本机 backend/var/uploads
        库 dadaotask_prod              db 6(独立库号)            附件文件
        角色 dadao_prod_app(RLS)
        角色 dadao_prod_owner(迁移)

为什么是这个形状:

  • 生产库和开发库在同一个 PG 实例(fnsvr-01),所以生产用独立的库 + 独立的角色。复用 dadao_app 等于拿到开发口令就能连生产。迁移里的 GRANT/DEFAULT PRIVILEGES 按 APP_DB_ROLE / OWNER_DB_ROLE 生成,靠 backend/app/roles.py 注入。
  • Redis 用 6 号库隔离:共享实例没有密码,靠库号避免和别的项目撞键(验证码、幂等键)。
  • 没有 nginx:openclaw 账号无 sudo 密码,改不了系统 nginx,所以后端单端口同时托管 API 与前端产物(同源,免 CORS)。root 愿意配合时按 deploy/prod/nginx.dadaotask.conf.example 加反代,不用改应用。
  • 用户级 systemd:openclaw-01 上所有服务(easymemo、portal-site…)都是 systemctl --user 起的,本方案沿用,全程不需要 root。

2. 首次部署(一次性)

前置:能 SSH 到 openclaw-01;fnsvr-01 的 root 权限(只在建库这一步需要)。

2.1 建生产库与角色(在 fnsvr-01 上,root)

# 口令自己生成,别用示例里的
OWNER_PW='<dadao_prod_owner 的口令>' APP_PW='<dadao_prod_app 的口令>' \
  bash deploy/prod/provision-db.sh

会创建:库 dadaotask_prod(owner dadao_prod_owner)、角色 dadao_prod_owner / dadao_prod_app(NOSUPERUSER NOBYPASSRLS,否则 RLS 会被跳过)、public schema 归 owner、REVOKE CREATE ... FROM PUBLIC。建表与 RLS 策略由 alembic 负责(2.4)。

2.2 代码落地(在 openclaw-01 上,openclaw 身份)

git clone --branch main --single-branch http://gitea.tatta.cn/Tatta/DadaoTask.git ~/app/dadaotask

2.3 配置(不进仓库)

mkdir -p ~/.config/dadaotask && chmod 700 ~/.config/dadaotask
cp ~/app/dadaotask/deploy/prod/env.production.example ~/.config/dadaotask/env.production
chmod 600 ~/.config/dadaotask/env.production
vim ~/.config/dadaotask/env.production        # 替换全部 __CHANGE_ME__

必填项:DATABASE_URL / MIGRATION_DATABASE_URL(2.1 的口令)、JWT_SECRET(≥32 位随机串)、REDIS_URL、WEB_DIST_DIR、UPLOAD_DIR。

两个踩过的坑:

  • CORS_ORIGINS 必须写成 JSON 数组('["http://...","http://..."]')。它是 list 字段,逗号分隔的字符串会让 pydantic-settings 当 JSON 解析并抛 SettingsError,alembic 这一步就挂。
  • Redis 有口令:fnsvr-01 的 redis 开了 requirepass(口令看 docker inspect redis),写进 URL 时要百分号编码(# → %23),否则连不上、发码/登录直接 500。

当前状态是内测模式:短信通道还没接(非 dev provider 直接 501),所以 SMS_PROVIDER=dev + INTERNAL_TEST_LOGIN=true,固定验证码 123456。启动日志会打 WARNING。只允许内网访问;短信通道接好后删掉 INTERNAL_TEST_LOGIN 并把 SMS_PROVIDER 换成真实供应商。

2.4 装服务单元

mkdir -p ~/.config/systemd/user
cp ~/app/dadaotask/deploy/prod/dadaotask-api.service ~/.config/systemd/user/
systemctl --user daemon-reload

2.5 首次上线

bash ~/app/dadaotask/deploy/prod/deploy.sh

3. 日常发布(每次上线)

# 本地:提交并 push 到 main(生产只跑仓库 HEAD)
# 服务器:
bash ~/app/dadaotask/deploy/prod/deploy.sh

脚本做六件事,任何一步失败即中止:代码对齐 origin/main(git reset --hard,禁止服务器手工改)→ 装 Python 依赖 → alembic upgrade head(用迁移角色)→ 在服务器上 npm ci && npm run build → 写 env.build 并重启 → 校验 /health 与 /version 的 commit 等于 HEAD。

前端产物只在服务器上构建,不允许本地上传 dist(AGENTS.md 铁律 3)。

4. 部署后校验

curl -s http://192.168.8.188:8200/health          # {"status":"ok","db":true,...}
curl -s http://192.168.8.188:8200/version         # commit 必须等于 git rev-parse --short HEAD
curl -sI http://192.168.8.188:8200/ | head -1     # 200,text/html(前端产物)

浏览器验收:打开 http://192.168.8.188:8200 → 手机号任意 + 验证码 123456 登录 → 任务/工单/知识各点一遍。

5. 运维

systemctl --user status  dadaotask-api      # 状态
systemctl --user restart dadaotask-api      # 重启
journalctl --user -u dadaotask-api -f       # 日志(gunicorn 输出到 stdout)
  • 附件落盘:backend/var/uploads(按工作区分子目录),删库不删文件,备份时一起打包
  • 回滚:cd ~/app/dadaotask && git reset --hard <旧 commit> && bash deploy/prod/deploy.sh;数据库迁移不回滚,回滚代码前确认旧代码兼容新表结构
  • 端口:8200(systemd 单元与 deploy.sh 的 PORT 默认值,两处要一致)

6. 已知限制(当前阶段)

限制 影响 解除方式
没有 HTTPS / 域名 只能内网 IP 访问,令牌明文传输 域名 + nginx + 证书(需 root)
内测固定验证码 谁都能用任意手机号登录 接短信通道,关 INTERNAL_TEST_LOGIN
无 pgvector 语义检索走 real[] 全表扫描,数据量大了会慢 上独立 pgvector 实例(deploy/docker-compose.vector.yml)
上传目录在本机磁盘 机器坏了附件就没了 备份 backend/var/uploads,或将来到对象存储
共享 PG / Redis 别人占满连接或内存会波及本站 独立实例