# app 业务服务部署 把 **gateway + home + api + mcp + timer** 五个集群(RPCX/ETCD)服务打成**一个镜像** `starpivot-app`, 推送到私有仓库 `docker-registry.ideapsound.com`,再到目标机拉取重启。容器内用 shell(entrypoint) 管理 5 个进程, 任一进程退出即整体重启。 > 约定:仓库**只提交模板**(`*.example`)。部署脚本会把**模板 + `docker-compose.yml`** 同步到服务器, > 但**绝不上传/覆盖**你在服务器上填好的真实 `.env` / `confs/*.yaml`(首次需登录服务器照模板复制并填值)。 ## 文件 | 文件 | 说明 | |---|---| | `../../apps/services/Dockerfile.voitrans` | 镜像构建(编译 5 个二进制 + entrypoint),构建上下文 = `apps/services/` | | `../../apps/services/entrypoint.voitrans.sh` | 容器入口,拉起并守护 5 个服务进程 | | `docker-compose.yml` | 运行镜像,把部署目录挂为容器 `/app`,`.env` 注入容器(此文件由你放到服务器) | | `confs/*.yaml.example` | 5 个服务配置的**模板**;真实 `confs/*.yaml` 被 gitignore,基础设施端点/密钥用 `${VAR}` 由 `.env` 注入 | | `env/env.example` | 运行时环境**模板**(只一份,不分区域);真实 `.env` 被 gitignore | | `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 远端拉取重启(固定 tag `dev-latest`) | | `prod-build.sh` | **生产①**:构建带版本号镜像并推送(`:版本` + `:latest`),不部署 | | `prod-deploy.sh` | **生产②**:选区域 + 版本 tag 部署,支持回滚 | ## 用法 ### 一、在服务器上准备好真实配置(一次性) 脚本每次会同步 `docker-compose.yml` + `*.example` 模板到目标机。**首次**需登录服务器把模板复制成真实文件并填值 (`cp env.example .env`、`for f in confs/*.yaml.example; do cp "$f" "${f%.example}"; done`,dev-deploy 缺真实文件时也会提示): ``` <部署目录>/ ├── docker-compose.yml # 从本目录拷贝 ├── .env # 照 env/env.example 填真实值(DSN/Redis/ETCD/NATS/CLUSTER_TAG/ │ # GATEWAY_TOKEN_KEY/GATEWAY_ENCRYPT_KEY/ANALYZE_APP_NAME 等) ├── confs/*.yaml # 照 confs/*.yaml.example 填(${VAR} 端点保持,密钥按需填) └── confs/ 附属文件 # 见下「需要提供的东西」 ``` dev 部署目录默认 `/home/work/starpivot/app`;prod 按**应用**取自 `prod-deploy.sh` 的 `APPS` 表(如 `ym` → `/home/work/ym-a11`)。 > 2026-09-13:prod 的可选区域只剩 `along`(云雁的 shanghai/japan/singapore 已移除,私钥在另一台开发机上)。 ### 二、部署 开发: ```bash cd deploy/voitrans ./dev-deploy.sh along # 发布到阿龙测试环境(构建 dev-latest → 推送 → 远端拉取重启) ./dev-deploy.sh yunyan # 发布到云雁测试环境 ./dev-deploy.sh # 不带参数 → 交互选择测试环境 ``` 生产(构建与部署分离): ```bash ./prod-build.sh 0.1.1 # 构建并推送 starpivot-app:0.1.1 (+latest) ./prod-deploy.sh ym along 0.2.1 # 部署应用 ym 的 0.2.1 到阿龙生产 ./prod-deploy.sh ym along 0.1.4 # 部署 ym 到阿龙正式机(目录 /home/work/ym-a11,容器 ym-a11;镜像走阿龙仓库,需先 ./prod-build.sh 0.1.4 along) ./prod-deploy.sh ym along rollback # 回滚到上一版本(与当前版本来回切换) ./prod-deploy.sh # 不带参数 → 交互引导:选应用 / 区域 / 版本 ``` `prod-deploy.sh` 的三项参数(应用 / 区域 / tag|rollback)**按内容识别、顺序随意、可任意省略**,缺哪项就在交互里选哪项: - 只给 `./prod-deploy.sh 0.1.4` → 交互里选应用和区域(以前会把 `0.1.4` 当成区域、把选区域那步整个跳过,最后报「未知区域: 0.1.4」); - 认不出的参数当场报错,不会被猜成别的东西; - ③ 选版本这步会**列出仓库里已推的 tag**(Docker Registry v2 API,取不到就退回手工输入), 并标出该应用远端「当前运行 / 上一版」,也可以直接敲 tag 或 `rollback`; - 省略应用但给了区域+版本时,仍按旧用法落到默认应用(`voitrans`)—— 会有黄色提示,且在终端里会多一次确认。 > 改了服务器上的 `.env` 后,必须经 `up`(脚本里的 `docker compose up -d`)**重建容器**才生效,`restart` 不会重读环境变量。 > prod 的版本/回滚记录存服务器上的 `.deploy_tag` / `.deploy_tag.prev`,不动你维护的 `.env`。 ## 需要填写 / 提供的东西 - **部署目标与凭据**:写死在各脚本顶部。 - `dev-deploy.sh`:`env_profile()` 里各测试环境(**目前只有 `along`**)的 `REGISTRY/DEV_HOST/DEV_KEY/DEV_DIR`。 - `prod-deploy.sh`:`region_profile()` 里各区域(**目前只有 `along`**)的 `DEPLOY_HOST`; 另有 `REGION_COMPANY`(该区域拉哪个公司的仓库,可用环境变量 `COMPANY` 覆盖)。 远端目录只由 `APPS` 表决定(`ym` → `/home/work/ym-a11`),区域里**不再覆盖目录**;容器名由应用 key 推导为 `<应用>-a11`, 部署时以 `CONTAINER_NAME` 注入 compose 并写回服务器 `.env`,所以同一台机可以并存多个应用。 `docker-compose.yml` 也由脚本按应用生成后下发(模板里占位的 service `app` 会被改写成 `<应用>-a11`), 与远端一致就跳过、不一致才备份 `.bak` 后更新;服务器上只需维护 `.env` 与 `confs/*.yaml`。 - 仓库账号 `admin` / 密码已写死;`DEPLOY_KEY` 默认 `~/.ssh/id_rsa`。 - ⚠️ 本目录脚本纳入 git,**公开仓库前务必清空** `*_HOST` 与 `REGISTRY_PASS`。 - **基础设施端点**:填服务器 `.env` 的 `ETCD_SERVERS / DB_ADMIN_DSN / DB_DEVICE_DSN / REDIS_* / NATS_URL`, 以及统计上报 `ANALYZE_APP_NAME`(须与 console 注册表应用名一致)/ `ANALYZE_REGION`、集群隔离 `CLUSTER_TAG`、 网关 `GATEWAY_TOKEN_KEY` 与 v2 加密 `GATEWAY_ENCRYPT_KEY`(须与 Flutter 端 AES key 一致)。 - **confs 引用的附属文件**(按需放入服务器部署目录,会随工作目录挂入容器 `/app`): - `home.yaml`:`./smart-bluetooth-447104-2b269474bd72.json`(Google 语音)、`google_auth.ApiKeyFile` 指的那份 Firebase 服务账号 json(Google 登录;也可把 json 内容直接内嵌进 yaml,以 `{` 开头即可,就不用发文件)、 `./apiclient_key.pem`(微信支付)、`confs/ip2region_v4.xdb` 与 `v6.xdb`(IP 库)、 `wordfilter/*.txt`(敏感词,模板里**默认已全部注释**,要用才放文件并取消注释)。 - `confs/ip2region_v4.xdb` / `v6.xdb` 由 `dev-deploy.sh` / `prod-deploy.sh` **自动下发**(prod 只在远端缺失时传一次),不用手工放。 - ⚠️ 登录/支付/翻译这几类缺文件**不会**杀容器,只在启动日志留一条 `init sys.xxx err: ...(xx未配置,该功能不可用)` 的 WARN,对应功能静默不可用—— 阿龙测试机现在就缺 Google 登录与微信支付/支付宝那几份。真正会 panic 的是下面这几类: - ⚠️ 缺这些文件会让对应 sys 在初始化时 **panic**,entrypoint 随即杀掉整个容器 → 无限重启。 这种失败的日志有欺骗性:最扎眼的往往是 `Table 'xxx.userdevice' doesn't exist`(那些表本该由 home 里的 user 模块启动时建,home 起不来自然没表),真凶却是上面几十行处的 `panic: init sys.wordfilter err: no found file:...`。 `prod-deploy.sh` 现在会在部署前做一次前置检查,缺文件直接拦下并告诉你缺哪些。 ## 端口(容器内,对外经反代/NPM 暴露) `gateway` 7001 RPCX / 7100 HTTP · `home` 7002 · `timer` 7003 · `mcp` 7004 / 7300 HTTP · `api` 7005 / 8080 Console。 依赖的 `etcd / mysql / redis / nats` 须在 `DOCKER_NETWORK`(默认 `1panel-network`)网络内,用容器名连接。