Browse Source

deploy: 移除本机连不上的 6 台服务器,顺带清掉测试里写死的下线机器凭据

云雁(yunyan)/灵谱(lingpu) 的部署目标从所有脚本移除,ENVS / PROD_ENVS / REGIONS
现在都只剩 along。那 6 台(云雁测试/上海/日本/新加坡、灵谱测试/正式)的 SSH 私钥路径
指向的是另一台开发机,本机一台都登不上(22 端口通、无可用密钥),留在交互菜单里只会误选。

- 删 deploy/{console,admin,app}/dev-deploy.sh 的 lingpu、yunyan 两段 env_profile
- 删 deploy/{console,admin}/prod-deploy.sh 的 yunyan、lingpu 两段
- 删 deploy/app/prod-deploy.sh 的 shanghai/japan/singapore 三段 region_profile
- 删 deploy/admin/deploy.sh:早期脚本,只指向灵谱且生产位留空,已被
  dev-deploy/prod-deploy 取代(CLAUDE.md 里"要同步两份 deploy.sh"也是错的,
  console 那份早就不存在)
- IP 与私钥路径不再记在文档里,需要时从 git 历史取回

镜像仓库刻意保留:registry.voitrans.net / registry.lingpu.net 都在,prod-build.sh
仍可选 yunyan|lingpu 推镜像(三家仓库本机都能读写,协作者仍从他那台机部署这两家)。

顺带修掉三处把已下线机器地址连明文数据库密码一起写死的测试:
- sys/nats/nats_test.go 有个真 bug——`url := "nats://<写死地址>"` 紧接着 `if url == ""`,
  常量永远不为空,所谓"默认跳过"从来没生效:每次 go test 都在连那台早已废弃的机器,
  白等 4.5 秒超时。现在真正读 NATS_URL 了。
- modules/{api,echomeet}/model_test.go 的 DSN 改走 API_TEST_DSN / ECHOMEET_TEST_DSN

另外给 app/prod-deploy.sh 加了一条明确报错:输入已移除的区域名(如 shanghai)原本会被
is_tag_like 当成版本号,报出"版本指定了两次"这种驴唇不对马嘴的错。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main
Rodger-Wang 4 weeks ago
parent
commit
223f7d0a44
  1. 17
      apps/services/modules/api/model_test.go
  2. 20
      apps/services/modules/echomeet/model_test.go
  3. 9
      apps/services/sys/nats/nats_test.go
  4. 88
      deploy/README.md
  5. 13
      deploy/admin/README.md
  6. 184
      deploy/admin/deploy.sh
  7. 27
      deploy/admin/dev-deploy.sh
  8. 30
      deploy/admin/prod-deploy.sh
  9. 16
      deploy/app/README.md
  10. 27
      deploy/app/dev-deploy.sh
  11. 4
      deploy/app/prod-build.sh
  12. 46
      deploy/app/prod-deploy.sh
  13. 10
      deploy/console/README.md
  14. 27
      deploy/console/dev-deploy.sh
  15. 30
      deploy/console/prod-deploy.sh
  16. 9
      deploy/dev-deploy-all.sh
  17. 5
      deploy/registry-profiles.sh
  18. 2
      docs/运维-容器断网与免费证书排查.md

17
apps/services/modules/api/model_test.go

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
@ -369,10 +370,22 @@ func Test_GenerateMac(t *testing.T) {
fmt.Printf("解析结果: productid=0x%04X, serial=%d\n", parsedProductid, parsedSerial) fmt.Printf("解析结果: productid=0x%04X, serial=%d\n", parsedProductid, parsedSerial)
} }
// Test_DB 连真实库建表/查数据做一次性排查,默认跳过。
//
// DSN 从环境变量取,未设置即跳过。
// ⚠️ 原先写死的是云雁上海机的地址**和明文库密码**——那台机 2026-09-13 已从本仓库移除
// (本机没有它的钥匙),写死的凭据留在 git 里只是风险,跑起来也只会连不上然后静默 return。
//
// API_TEST_DSN='root:pwd@tcp(host:3306)/admin?charset=utf8mb4&parseTime=True&loc=Local' \
// go test -v -run Test_DB ./modules/api/
func Test_DB(t *testing.T) { func Test_DB(t *testing.T) {
//jdbc:mysql://139.224.212.237:3307/admin?user=root&password=li13451234&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC dsn := os.Getenv("API_TEST_DSN")
if dsn == "" {
t.Skip("未设置 API_TEST_DSN,跳过")
}
if sys, err := mysql.NewSys( if sys, err := mysql.NewSys(
mysql.SetMySQLDsn("root:li13451234@tcp(139.224.212.237:3307)/admin?charset=utf8mb4&parseTime=True&loc=Local"), mysql.SetMySQLDsn(dsn),
); err != nil { ); err != nil {
fmt.Printf("err:%v", err) fmt.Printf("err:%v", err)
return return

20
apps/services/modules/echomeet/model_test.go

@ -1,16 +1,30 @@
package echomeet_test package echomeet_test
import ( import (
"os"
"fmt"
"testing"
"yunyan/comm" "yunyan/comm"
"yunyan/lego/sys/mysql" "yunyan/lego/sys/mysql"
"yunyan/pb" "yunyan/pb"
"fmt"
"testing"
) )
// Test_InitAuthorizeTable 连真实业务库做一次性排查,默认跳过。
//
// DSN 从环境变量取,未设置即跳过。
// ⚠️ 原先写死的是云雁上海机的地址**和明文库密码**——那台机 2026-09-13 已从本仓库移除
// (本机没有它的钥匙),写死的凭据留在 git 里只是风险,跑起来也只会连不上然后静默 return。
//
// ECHOMEET_TEST_DSN='root:pwd@tcp(host:3306)/echomeet?charset=utf8mb4&parseTime=True&loc=Local' \
// go test -v -run Test_InitAuthorizeTable ./modules/echomeet/
func Test_InitAuthorizeTable(t *testing.T) { func Test_InitAuthorizeTable(t *testing.T) {
dsn := os.Getenv("ECHOMEET_TEST_DSN")
if dsn == "" {
t.Skip("未设置 ECHOMEET_TEST_DSN,跳过")
}
if sys, err := mysql.NewSys( if sys, err := mysql.NewSys(
mysql.SetMySQLDsn("root:li13451234@tcp(139.224.212.237:3306)/echomeet?charset=utf8mb4&parseTime=True&loc=Local"), mysql.SetMySQLDsn(dsn),
); err != nil { ); err != nil {
fmt.Printf("err:%v", err) fmt.Printf("err:%v", err)
return return

9
apps/services/sys/nats/nats_test.go

@ -1,6 +1,7 @@
package nats package nats
import ( import (
"os"
"testing" "testing"
"time" "time"
) )
@ -11,14 +12,16 @@ import (
// //
// 用法: // 用法:
// //
// NATS_URL=nats://47.253.88.66:4222 go test -v -run TestNatsConnect ./sys/nats/ // NATS_URL=nats://<host>:4222 go test -v -run TestNatsConnect ./sys/nats/
// //
// 它走和 home 启动时完全一样的路径:OnInit(连接 + 取 JetStream 上下文), // 它走和 home 启动时完全一样的路径:OnInit(连接 + 取 JetStream 上下文),
// 再用 core 发布 + flush 证明不仅连得上、还能真正把数据发到服务端。 // 再用 core 发布 + flush 证明不仅连得上、还能真正把数据发到服务端。
func TestNatsConnect(t *testing.T) { func TestNatsConnect(t *testing.T) {
url := "nats://47.253.88.66:4222" // ⚠️ 这里原先是 `url := "nats://<写死的地址>"` 紧接着 `if url == ""`——常量永远不为空,
// 于是「默认跳过」从来没生效过:每次 go test 都在连那台早已下线的机器,白等一次超时才算过。
url := os.Getenv("NATS_URL")
if url == "" { if url == "" {
t.Skip("未设置 NATS_URL,跳过。示例: NATS_URL=nats://47.253.88.66:4222 go test -v -run TestNatsConnect ./sys/nats/") t.Skip("未设置 NATS_URL,跳过。示例: NATS_URL=nats://<host>:4222 go test -v -run TestNatsConnect ./sys/nats/")
} }
t.Logf("→ 连接 %s ...", url) t.Logf("→ 连接 %s ...", url)

88
deploy/README.md

@ -6,89 +6,33 @@
cd deploy cd deploy
./dev-deploy-all.sh # 交互选公司(环境),按 app → console → admin 全量发布 ./dev-deploy-all.sh # 交互选公司(环境),按 app → console → admin 全量发布
./dev-deploy-all.sh along # 全量发到阿龙测试环境 ./dev-deploy-all.sh along # 全量发到阿龙测试环境
./dev-deploy-all.sh yunyan app admin # 只发 app 和 admin 到云雁测试环境 ./dev-deploy-all.sh along app admin # 只发 app 和 admin
./dev-deploy-all.sh along -y -k # 免确认;某个服务失败也继续发后面的 ./dev-deploy-all.sh along -y -k # 免确认;某个服务失败也继续发后面的
./dev-deploy-all.sh along -n # dry-run,只打印会执行什么 ./dev-deploy-all.sh along -n # dry-run,只打印会执行什么
``` ```
公司/环境:`lingpu`(灵谱·香港)、`yunyan`(云雁·广州)、`along`(阿龙),与各服务 环境:目前只有 `along`(阿龙),与各服务 `dev-deploy.sh` 的 `ENVS` 同名,脚本启动时会交叉校验,
`dev-deploy.sh` 的 `ENVS` 同名,脚本启动时会交叉校验,一边加了新环境另一边忘了加会直接报错。 一边加了新环境另一边忘了加会直接报错。
> ⚠️ **2026-09-13:云雁(yunyan)/灵谱(lingpu) 的部署目标已从本仓库的脚本中移除。**
> 移除的只是「往哪台服务器部署」——这些环境的 SSH 私钥路径指向的是另一台开发机,本机没有钥匙,
> 留着只会在菜单里制造误选。**镜像仓库没有动**:`prod-build.sh` 仍可选 `yunyan` / `lingpu`
> 把镜像推到 `registry.voitrans.net` / `registry.lingpu.net`。
> 要恢复部署目标,从 git 历史里取回对应的 `env_profile` / `region_profile` 段即可(含 IP 与私钥路径)。
它只是按顺序调各服务自己的 `deploy/<服务>/dev-deploy.sh <环境>` 并汇总结果,部署规则仍然 它只是按顺序调各服务自己的 `deploy/<服务>/dev-deploy.sh <环境>` 并汇总结果,部署规则仍然
只有一处;单独发某个服务照旧进服务目录跑 `./dev-deploy.sh <环境>`。服务器上还没有真实 `.env` 只有一处;单独发某个服务照旧进服务目录跑 `./dev-deploy.sh <环境>`。服务器上还没有真实 `.env`
时子脚本会推完镜像就停下,汇总表里记为「跳过」而不是「成功」。 时子脚本会推完镜像就停下,汇总表里记为「跳过」而不是「成功」。
> 以下是早期 `deploy.sh <env> [action]` 的说明,与现在的 `dev-deploy.sh` / `prod-build.sh` / ## 各服务的脚本
> `prod-deploy.sh` 流程不完全一致,仅供参考。
**每个服务目录各一个 `deploy.sh`**,服务名取自所在目录,指令为 `<env> [action]`。 每个服务目录下三个脚本:`dev-deploy.sh`(测试环境,固定 tag `dev-latest`)、
每个服务自带单服务 compose + 环境模板;依赖服务(PostgreSQL / Redis / NATS) `prod-build.sh <版本> [公司]`(构建并推送带版本号的镜像)、`prod-deploy.sh`(只做远端拉取重启)。
由目标环境**共享提供**,不在 compose 内搭建,只在 env 里填连接地址。 服务器上的 `.env` 与 `confs/*.yaml` 由人工维护,脚本只下发 `*.example` 模板,绝不覆盖。
## 服务
- `console/` — Go 控制面服务(:8080) - `console/` — Go 控制面服务(:8080)
- `admin/` — Nuxt 管理前端(:3000,通过 `CONSOLE_BACKEND` 连 console) - `admin/` — Nuxt 管理前端(:3000,通过 `CONSOLE_BACKEND` 连 console)
- `app/` — Go 业务后端(多应用共用镜像,远端目录/容器名按应用区分,见 `prod-deploy.sh` 的 `APPS`)
## 目录结构 ⚠️ 部署目标与仓库密码写死在脚本里且**会进 git**(私有仓库内可接受;公开前务必清理
``` `deploy/registry-profiles.sh` 的 `REGISTRY_PASS` 与各 `*-deploy.sh` 的 `DEPLOY_HOST`)。
deploy/
└── <service>/ # console / admin
├── deploy.sh # 本服务部署脚本(服务名 = 目录名)
├── docker-compose.yml # 单服务 compose(变量驱动,无依赖服务)
├── env/
│ ├── local.env.example
│ ├── dev.env.example
│ ├── prod.env.example
│ └── (复制填写后的 local.env / dev.env / prod.env)
└── (console 额外: confs/console.yaml、log/)
```
> 两个服务的 `deploy.sh` 内容相同,靠所在目录名区分服务。
## 用法
进入服务目录运行:
```bash
cd deploy/console
./deploy.sh local up # 本机起 console
./deploy.sh dev # 一键发布 console 到开发服务器(动作默认 deploy)
./deploy.sh prod # 一键发布 console 到生产服务器
./deploy.sh prod logs # 看生产 console 日志
cd deploy/admin
./deploy.sh local up # 本机起 admin
./deploy.sh prod # 发布 admin 到生产服务器
```
参数:
- `env`:`local` | `dev` | `prod`
- `action`:
- `local`:up(默认)| down | restart | logs | ps | pull | build
- `dev` / `prod`:deploy(默认,构建推送+同步+远程拉起)| up(仅远程拉起)| build | down | restart | logs | ps | pull
## 首次配置(dev / prod)
配置分两处:**部署目标/仓库凭据写死在各服务的 `deploy.sh`,运行时配置放 env 文件**。
1. 部署目标 + 私有仓库(改各服务 `deploy.sh` 顶部 `deploy_profile`,[console](console/deploy.sh) / [admin](admin/deploy.sh)):
- 按 `dev` / `prod` 段填 `DEPLOY_HOST`(服务器 IP) 和 `REGISTRY_PASS`(仓库密码)
- 通用项已给默认:`REGISTRY` / `REGISTRY_USER` / `DEPLOY_PORT` / `DEPLOY_USER` / `DEPLOY_KEY`
- `DEPLOY_DIR` 自动 = `/opt/yunyan/<服务名>`,无需填
- 注:两个 `deploy.sh` 各填各的,不共享
2. 运行时配置(每个服务、每个环境一份 env):
```bash
cp console/env/dev.env.example console/env/dev.env
cp admin/env/dev.env.example admin/env/dev.env
```
需要填:
- 依赖服务连接(console):`POSTGRES_DSN` / `REDIS_ADDR`+`REDIS_PASSWORD` / `NATS_URL`
- 网络与端口:`DOCKER_NETWORK` / `ADMIN_PORT` / `TAG`
3. 目标机要求:装 docker + docker compose v2、能访问私有镜像仓库、SSH 公钥已授权、依赖服务在 `DOCKER_NETWORK` 网络内可达。
## 部署顺序
依赖服务(DB/Redis/NATS)共享、由环境预先提供。两个应用服务按序:
1. 先 `console`(admin 依赖它)
2. 再 `admin`(同一目标机、同一 `DOCKER_NETWORK`,用容器名 `yunyan-console` 连 console)
## 注意
- `env/*.env`(真实)含密钥/DSN,已 `.gitignore` 忽略;仓库只保留 `*.example`。
- ⚠️ 部署目标/仓库密码写死在各服务 `deploy.sh`,而它们**会进 git**——`DEPLOY_HOST`、`REGISTRY_PASS` 等会随仓库提交(私有仓库内可接受;公开前务必清理两个 `deploy.sh`)。
- prod 的 `TAG` 用固定版本号便于回滚;`FIELD_ENCRYPT_KEY` / `CONSOLE_TOKEN_KEY` 用强随机值,环境间不复用。
- 镜像构建复用仓库根目录 `build.sh`(`deploy.sh` 在 deploy/build 动作时自动调用,目标=服务名);当前仓库**尚无 build.sh**,build/deploy 前需先补。

13
deploy/admin/README.md

@ -16,7 +16,7 @@
| `env/env.example` | 运行时环境**模板**(只一份,不分环境);真实 `.env` 被 gitignore | | `env/env.example` | 运行时环境**模板**(只一份,不分环境);真实 `.env` 被 gitignore |
| `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 远端拉取重启(固定 tag `dev-latest`) | | `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 远端拉取重启(固定 tag `dev-latest`) |
| `prod-build.sh` | **生产①**:构建带版本号镜像并推送(`:版本` + `:latest`),不部署 | | `prod-build.sh` | **生产①**:构建带版本号镜像并推送(`:版本` + `:latest`),不部署 |
| `prod-deploy.sh` | **生产②**:部署指定版本到指定生产环境(`yunyan`/`lingpu`),支持回滚 | | `prod-deploy.sh` | **生产②**:部署指定版本到生产环境(目前只有 `along`),支持回滚 |
## 用法 ## 用法
@ -45,12 +45,12 @@ docker compose up -d # 本机起 starpivot-admin
```bash ```bash
cd deploy/admin cd deploy/admin
./dev-deploy.sh lingpu # 开发:发布到灵谱测试环境(构建 dev-latest → 推送 → 远端拉取重启) ./dev-deploy.sh along # 开发:发布到阿龙测试环境(构建 dev-latest → 推送 → 远端拉取重启)
./dev-deploy.sh yunyan # 开发:发布到云雁测试环境 ./dev-deploy.sh yunyan # 开发:发布到云雁测试环境
./dev-deploy.sh # 不带参数 → 交互选择测试环境 ./dev-deploy.sh # 不带参数 → 交互选择测试环境
./prod-build.sh 0.1.1 yunyan # 生产①:构建并推送 starpivot-admin:0.1.1 (+latest);第2参数选公司 ./prod-build.sh 0.1.1 yunyan # 生产①:构建并推送 starpivot-admin:0.1.1 (+latest);第2参数选公司
./prod-deploy.sh 0.1.1 lingpu # 生产②:部署 0.1.1 到指定生产环境(yunyan|lingpu|along),省略环境则交互选 ./prod-deploy.sh 0.2.1 along # 生产②:部署 0.2.1 到生产环境(目前只有 along,省略即用它)
./prod-deploy.sh rollback lingpu # 生产回滚到上一版本(与当前版本来回切换) ./prod-deploy.sh rollback along # 生产回滚到上一版本(与当前版本来回切换)
``` ```
> 改了服务器上的 `.env` 后,必须经 `up`(脚本里的 `docker compose up -d`)**重建容器**才生效,`restart` 不会重读环境变量。 > 改了服务器上的 `.env` 后,必须经 `up`(脚本里的 `docker compose up -d`)**重建容器**才生效,`restart` 不会重读环境变量。
@ -58,8 +58,9 @@ cd deploy/admin
## 需要填写 / 提供的东西 ## 需要填写 / 提供的东西
- **部署目标与凭据**:写死在各脚本顶部。`dev-deploy.sh` 的 `env_profile()`(每个测试环境一套 `REGISTRY/DEV_HOST/DEV_KEY/DEV_DIR`,现有 `lingpu`、`yunyan`、`along`;新增环境加一段 case 并写进 `ENVS`)、`prod-deploy.sh` 的 `env_profile()`(每个生产环境一套 `REGISTRY/IMAGE_NAME/DEPLOY_HOST/DEPLOY_KEY/DEPLOY_DIR`,现有 `yunyan`、`lingpu`、`along`;新增环境加一段 case 并写进 `PROD_ENVS`); - **部署目标与凭据**:写死在各脚本顶部。`dev-deploy.sh` 的 `env_profile()`(每个测试环境一套 `REGISTRY/DEV_HOST/DEV_KEY/DEV_DIR`,**目前只有 `along`**;新增环境加一段 case 并写进 `ENVS`)、`prod-deploy.sh` 的 `env_profile()`(每个生产环境一套 `REGISTRY/IMAGE_NAME/DEPLOY_HOST/DEPLOY_KEY/DEPLOY_DIR`,**目前只有 `along`**;新增环境加一段 case 并写进 `PROD_ENVS`);
仓库账号 `admin` / 密码已写死;`DEPLOY_KEY` 默认 `/Users/liwei/Documents/key/yunyan-us.pem`(prod)。 仓库账号 `admin` / 密码已写死;`DEPLOY_KEY` 默认指向阿龙那把钥匙,可用环境变量覆盖:`DEPLOY_KEY=~/Documents/keys/loginscre.pem ./prod-deploy.sh …`。
- 2026-09-13 移除了云雁/灵谱的部署目标(私钥在另一台开发机上,本机连不上);**镜像仓库未动**,`prod-build.sh` 仍可选这两家推镜像。
- ⚠️ 本目录脚本纳入 git,**公开仓库前务必清空** `*_HOST` 与 `REGISTRY_PASS`。 - ⚠️ 本目录脚本纳入 git,**公开仓库前务必清空** `*_HOST` 与 `REGISTRY_PASS`。
- **后端地址**:`.env` 的 `CONSOLE_BACKEND`,须指向同网络的 console 容器,默认 `http://starpivot-console:8080`。 - **后端地址**:`.env` 的 `CONSOLE_BACKEND`,须指向同网络的 console 容器,默认 `http://starpivot-console:8080`。

184
deploy/admin/deploy.sh

@ -1,184 +0,0 @@
#!/usr/bin/env bash
# <服务> 部署脚本(每个服务目录各一份,服务名取自所在目录名)
#
# 用法: ./deploy.sh <env> [action] (在 deploy/<服务>/ 下运行)
# env : local | dev | prod
# action : dev/prod → deploy(默认) | up | build | pull | down | restart | logs | ps
# local → up(默认) | build | pull | down | restart | logs | ps
#
# deploy = 构建推送镜像 + 同步配置 + 远程 pull + up(一键发布,仅 dev/prod)
# up = 远程:仅同步配置 + 远程 pull + up(用已推送镜像);本机:起容器
#
# 例(在 deploy/console/ 下):
# ./deploy.sh prod # 一键发布生产 console
# ./deploy.sh prod logs # 看生产 console 日志
# ./deploy.sh local up # 本机起 console
# ./deploy.sh dev build # 只构建推送 dev 镜像
set -euo pipefail
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 本服务部署目录(deploy/<服务>)
SERVICE="$(basename "$SVC_DIR")" # 服务名 = 目录名(console / admin)
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根(deploy/<服务>/../..)
blue() { printf "\033[34m%s\033[0m\n" "$*"; }
green() { printf "\033[32m%s\033[0m\n" "$*"; }
red() { printf "\033[31m%s\033[0m\n" "$*"; }
# ╔══════════════════════════════════════════════════════════════════════╗
# ║ 部署目标 / 私有仓库配置 —— 写死在这里,改这里即可(按环境) ║
# ║ console、admin 同机不同目录;DEPLOY_DIR 自动 = /opt/yunyan/<服务名> ║
# ║ 注意:本文件纳入 git,下面的密码/IP 会随仓库提交(私有仓库内可接受) ║
# ╚══════════════════════════════════════════════════════════════════════╝
deploy_profile() {
DEPLOY_HOST=""; REGISTRY_PASS="" # 占位,local 用不到;dev/prod 在下面填
# —— 各环境通用默认 ——
REGISTRY=registry.voitrans.net # 私有镜像仓库地址
REGISTRY_USER=liwei # 仓库账号
DEPLOY_PORT=22 # 服务器 SSH 端口
DEPLOY_USER=root # 服务器 SSH 用户
DEPLOY_KEY=~/Documents/灵谱/lingpu-hk.pem # 本机访问服务器的 SSH 私钥路径
case "$ENV_NAME" in
dev)
DEPLOY_HOST="8.217.193.140" # 灵谱香港阿里云(lingpu-hk)
REGISTRY_PASS="li13451234" # 私有仓库密码(liwei 用户)
;;
prod)
DEPLOY_HOST="" # ← 生产服务器 IP(必填)
REGISTRY_PASS="" # ← 私有仓库密码(必填)
;;
local) ;; # 本机不走 SSH,无需填
esac
DEPLOY_DIR="/home/work/starpivot-$SERVICE" # 远程部署目录(与 /home/work 下其余服务一致)
}
usage() {
cat >&2 <<EOF
用法: ./deploy.sh <env> [action] (在 deploy/$SERVICE/ 下运行)
env : local | dev | prod
action : dev/prod → deploy(默认) | up | build | pull | down | restart | logs | ps
local → up(默认) | build | pull | down | restart | logs | ps
例:
./deploy.sh prod # 一键发布生产 $SERVICE
./deploy.sh prod logs # 看生产 $SERVICE 日志
./deploy.sh local up # 本机起 $SERVICE
./deploy.sh dev build # 只构建推送 dev 镜像
EOF
exit 1
}
# ── 参数解析与校验 ──
ENV_NAME="${1:-}"; ACTION="${2:-}"
[ -n "$ENV_NAME" ] || usage
[ -f "$SVC_DIR/docker-compose.yml" ] || { red "✗ $SERVICE 目录缺 docker-compose.yml(deploy.sh 须放在服务目录内)"; exit 1; }
case "$ENV_NAME" in
local|dev|prod) ;;
*) red "✗ 未知环境: ${ENV_NAME}(应为 local|dev|prod)"; usage ;;
esac
# 默认动作:远程发布默认 deploy,本机默认 up
[ -n "$ACTION" ] || { [ "$ENV_NAME" = local ] && ACTION=up || ACTION=deploy; }
# 从 ENV_FILE 安全读取单个变量值(去除可能的外层双引号)
_envget() {
{ grep -E "^$1=" "$ENV_FILE" || true; } | head -1 | cut -d= -f2- | sed -e 's/^"//' -e 's/"$//'
}
# 载入 env/<环境>.env —— 现在只放容器运行时配置(DSN / Redis / NATS / 密钥 / 端口 / TAG / 网络)。
# 部署目标与仓库凭据已写死在 deploy_profile,不再放 env 文件。脚本只需从这里取 compose 仍要的 TAG/网络。
load_env() {
ENV_FILE="$SVC_DIR/env/$ENV_NAME.env"
if [ ! -f "$ENV_FILE" ]; then
red "✗ 缺少 $SERVICE/env/$ENV_NAME.env —— 请先从 $SERVICE/env/$ENV_NAME.env.example 复制并填写"
exit 1
fi
TAG="$(_envget TAG)"; TAG="${TAG:-latest}"
DOCKER_NETWORK="$(_envget DOCKER_NETWORK)"; DOCKER_NETWORK="${DOCKER_NETWORK:-1panel-network}"
}
# 生成 compose 用 .env = 服务运行时 env 文件 + 注入写死的 REGISTRY(镜像地址替换用)
# 先滤掉源文件里可能残留的 REGISTRY 行,保证唯一、不依赖 compose 的覆盖顺序。
gen_env() {
{ grep -v '^REGISTRY=' "$ENV_FILE" || true; } > "$SVC_DIR/.env"
printf 'REGISTRY=%s\n' "$REGISTRY" >> "$SVC_DIR/.env"
}
# 构建并推送本服务镜像(复用仓库根目录 build.sh,目标 = 服务名)。
# local 环境只构建不推送(PUSH=0),dev/prod 构建并推送到私有仓库。
build_push() {
local push=1; [ "$ENV_NAME" = local ] && push=0
blue ">>> 构建$([ "$push" = 1 ] && echo 推送) $SERVICE 镜像 (TAG=${TAG:-latest}, PUSH=$push)"
TAG="${TAG:-latest}" REGISTRY="$REGISTRY" PUSH="$push" "$REPO_ROOT/build.sh" "$SERVICE"
}
ensure_network_local() {
docker network inspect "$DOCKER_NETWORK" >/dev/null 2>&1 || docker network create "$DOCKER_NETWORK"
}
# 本机 docker compose(gen_env 落为同目录 .env,既做变量替换又做容器 env_file)
compose_local() {
gen_env
ensure_network_local
( cd "$SVC_DIR" && docker compose --env-file .env "$@" )
}
# ── 远程 SSH 基础 ──
_ssh() { ssh -p "${DEPLOY_PORT:-22}" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "${DEPLOY_USER:-root}@${DEPLOY_HOST}" "$@"; }
_scp() { scp -P "${DEPLOY_PORT:-22}" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$@"; }
_dest() { echo "${DEPLOY_USER:-root}@${DEPLOY_HOST}:$1"; }
# 远程发布:同步配置 + 远程 login + pull + up
remote_deploy() {
: "${DEPLOY_HOST:?请在 deploy.sh 顶部 deploy_profile 的 $ENV_NAME 段填 DEPLOY_HOST}"
gen_env
blue ">>> 同步 $SERVICE 配置到 ${DEPLOY_USER:-root}@${DEPLOY_HOST}:${DEPLOY_DIR}"
_ssh "mkdir -p '$DEPLOY_DIR'"
_scp "$SVC_DIR/docker-compose.yml" "$(_dest "$DEPLOY_DIR/docker-compose.yml")"
_scp "$SVC_DIR/.env" "$(_dest "$DEPLOY_DIR/.env")"
# 带 confs/ 的服务(console)一并同步配置并预建 log 目录
if [ -d "$SVC_DIR/confs" ]; then
_ssh "mkdir -p '$DEPLOY_DIR/confs' '$DEPLOY_DIR/log'"
_scp -r "$SVC_DIR/confs/." "$(_dest "$DEPLOY_DIR/confs/")"
fi
blue ">>> 远程拉起 $SERVICE"
[ -n "${REGISTRY_PASS:-}" ] && \
_ssh "docker login '$REGISTRY' -u '${REGISTRY_USER:-admin}' -p '$REGISTRY_PASS'"
_ssh "docker network inspect '$DOCKER_NETWORK' >/dev/null 2>&1 || docker network create '$DOCKER_NETWORK'"
_ssh "cd '$DEPLOY_DIR' && docker compose --env-file .env pull && docker compose --env-file .env up -d --force-recreate"
}
# 远程运维(down/restart/logs/ps/pull)
remote_compose() {
: "${DEPLOY_HOST:?请在 deploy.sh 顶部 deploy_profile 的 $ENV_NAME 段填 DEPLOY_HOST}"
_ssh "cd '$DEPLOY_DIR' && docker compose --env-file .env $*"
}
# ── 分发 ──
deploy_profile # 写死的部署目标 / 仓库凭据
load_env # env 文件里的运行时配置(TAG / 网络)
if [ "$ENV_NAME" = local ]; then
case "$ACTION" in
up) compose_local up -d --force-recreate ;;
down) compose_local down ;;
restart) compose_local restart ;;
logs) compose_local logs -f --tail=120 ;;
ps) compose_local ps ;;
pull) compose_local pull ;;
build) build_push ;;
*) red "未知操作: ${ACTION}(local 支持 up|down|restart|logs|ps|pull|build)"; exit 1 ;;
esac
else
case "$ACTION" in
deploy) build_push; remote_deploy ;;
up) remote_deploy ;;
build) build_push ;;
pull) remote_compose pull ;;
down) remote_compose down ;;
restart) remote_compose restart ;;
ps) remote_compose ps ;;
logs) remote_compose logs -f --tail=120 ;;
*) red "未知操作: ${ACTION}(dev/prod 支持 deploy|up|build|pull|down|restart|logs|ps)"; exit 1 ;;
esac
fi
green "✓ $SERVICE $ENV_NAME $ACTION 完成"

27
deploy/admin/dev-deploy.sh

@ -6,13 +6,14 @@
# (照着仓库里的 env/env.example 在服务器上放好真实 .env),本脚本不上传任何真实配置。 # (照着仓库里的 env/env.example 在服务器上放好真实 .env),本脚本不上传任何真实配置。
# #
# 用法(在 deploy/admin/ 下): # 用法(在 deploy/admin/ 下):
# ./dev-deploy.sh lingpu # 发布到灵谱测试环境
# ./dev-deploy.sh yunyan # 发布到云雁测试环境
# ./dev-deploy.sh along # 发布到阿龙测试环境 # ./dev-deploy.sh along # 发布到阿龙测试环境
# ./dev-deploy.sh # 不带参数 → 交互选择环境(无 TTY 时用默认环境 lingpu) # ./dev-deploy.sh # 不带参数 → 交互选择环境(目前只有 along 一个,无 TTY 时也用它)
# #
# 开发版镜像始终使用固定 tag dev-latest,每次构建覆盖同一 tag。 # 开发版镜像始终使用固定 tag dev-latest,每次构建覆盖同一 tag。
set -euo pipefail set -euo pipefail
# ⚠️ 2026-09-13:云雁(yunyan)/灵谱(lingpu) 的部署目标已从本脚本移除——本仓库的部署目标只剩阿龙。
# 移除的是「往哪台服务器部署」,**镜像仓库没动**:prod-build.sh 仍可选 yunyan/lingpu 推镜像。
# 要恢复:git log 里找回对应的 env_profile/region_profile 段即可(含 IP 与各自的 SSH 私钥路径)。
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/admin SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/admin
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根 REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根
@ -34,27 +35,13 @@ IMAGE_NAME="starpivot-admin" # 业务镜像名
# ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║ # ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║
# ║ 新增环境:在 env_profile 加一段 case,并把环境名加进 ENVS 数组。 ║ # ║ 新增环境:在 env_profile 加一段 case,并把环境名加进 ENVS 数组。 ║
# ╚══════════════════════════════════════════════════════════════════════╝ # ╚══════════════════════════════════════════════════════════════════════╝
ENVS=(lingpu yunyan along) # 可选测试环境(交互菜单按此顺序展示) ENVS=(along) # 可选测试环境(目前只剩阿龙;云雁/灵谱已于 2026-09-13 移除,见文件头说明)
DEFAULT_ENV="lingpu" # 无参数且无 TTY(CI/管道)时的默认环境 DEFAULT_ENV="along" # 无参数且无 TTY(CI/管道)时的默认环境
env_profile() { env_profile() {
DEV_PORT=22 # 各环境通用默认 DEV_PORT=22 # 各环境通用默认
DEV_USER=root DEV_USER=root
case "$DEV_ENV" in case "$DEV_ENV" in
lingpu)
ENV_DESC="灵谱测试环境 · 香港阿里云 (lingpu-hk)"
registry_profile lingpu # 镜像仓库:灵谱
DEV_HOST="8.217.193.140" # ← 灵谱测试服务器 IP
DEV_KEY=/Users/liwei/Documents/灵谱/lingpu-hk.pem # 本机访问该服务器的 SSH 私钥
DEV_DIR="/home/work/starpivot/admin" # 远端部署目录(其中 docker-compose.yml/.env 由你自己维护)
;;
yunyan)
ENV_DESC="云雁测试环境 · 广州阿里云"
registry_profile yunyan # 镜像仓库:云雁(registry.voitrans.net,就在这台机上)
DEV_HOST="8.138.86.223" # ← 云雁测试服务器 IP(2026-07 由腾讯云 124.222.232.218 迁来)
DEV_KEY="/Users/liwei1dao/work/密钥/dev-guangzhou.pem" # 本机访问该服务器的 SSH 私钥(2026-08-18 由 ~/.ssh/aliyun_guangzhou.pem 迁到此处)
DEV_DIR="/home/work/starpivot/admin"
;;
along) along)
ENV_DESC="阿龙测试环境 · 阿里云" ENV_DESC="阿龙测试环境 · 阿里云"
registry_profile along # 镜像仓库:就在这台机上(registry.ymaikj.com,账号 admin) registry_profile along # 镜像仓库:就在这台机上(registry.ymaikj.com,账号 admin)
@ -73,8 +60,6 @@ usage() {
用法: ./dev-deploy.sh [环境] (在 deploy/admin/ 下运行) 用法: ./dev-deploy.sh [环境] (在 deploy/admin/ 下运行)
环境 : ${ENVS[*]// /, }(省略则交互选择;无 TTY 时用默认 ${DEFAULT_ENV}) 环境 : ${ENVS[*]// /, }(省略则交互选择;无 TTY 时用默认 ${DEFAULT_ENV})
例: 例:
./dev-deploy.sh lingpu # 发布到灵谱测试环境
./dev-deploy.sh yunyan # 发布到云雁测试环境
./dev-deploy.sh along # 发布到阿龙测试环境 ./dev-deploy.sh along # 发布到阿龙测试环境
EOF EOF
} }

30
deploy/admin/prod-deploy.sh

@ -6,11 +6,14 @@
# (照着仓库里的 env/env.example 在服务器上放好真实 .env),本脚本不上传任何真实配置。 # (照着仓库里的 env/env.example 在服务器上放好真实 .env),本脚本不上传任何真实配置。
# #
# 用法(在 deploy/admin/ 下): # 用法(在 deploy/admin/ 下):
# ./prod-deploy.sh <tag> [环境] # 部署指定版本,如 ./prod-deploy.sh 0.1.1 lingpu # ./prod-deploy.sh <tag> [环境] # 部署指定版本,如 ./prod-deploy.sh 0.1.1 along
# ./prod-deploy.sh rollback [环境] # 回滚到上一次部署版本(与当前版本来回切换) # ./prod-deploy.sh rollback [环境] # 回滚到上一次部署版本(与当前版本来回切换)
# 环境 = yunyan(上海)| lingpu(广州)| along(阿龙 47.116.104.181);省略 → 交互选择,无 TTY 时用默认 yunyan。 # 环境 = along(阿龙 47.116.104.181);省略 → 直接用它。
# ⚠️ 环境必须与 prod-build.sh 构建时所选公司一致,否则拉不到镜像。 # ⚠️ 环境必须与 prod-build.sh 构建时所选公司一致,否则拉不到镜像。
set -euo pipefail set -euo pipefail
# ⚠️ 2026-09-13:云雁(yunyan)/灵谱(lingpu) 的部署目标已从本脚本移除——本仓库的部署目标只剩阿龙。
# 移除的是「往哪台服务器部署」,**镜像仓库没动**:prod-build.sh 仍可选 yunyan/lingpu 推镜像。
# 要恢复:git log 里找回对应的 env_profile/region_profile 段即可(含 IP 与各自的 SSH 私钥路径)。
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@ -32,30 +35,13 @@ DOCKER_NETWORK="1panel-network" # compose 外部网络名(与服务
# 容器名:必须与 docker-compose.yml 的 container_name 一字不差(本脚本会上传覆盖那份 compose)。 # 容器名:必须与 docker-compose.yml 的 container_name 一字不差(本脚本会上传覆盖那份 compose)。
# 仅用于 up 冲突时清理同名遗留容器;不随环境变化。 # 仅用于 up 冲突时清理同名遗留容器;不随环境变化。
CONTAINER_NAME="starpivot-admin" CONTAINER_NAME="starpivot-admin"
PROD_ENVS=(yunyan lingpu along) # 可选生产环境(交互菜单按此顺序展示) PROD_ENVS=(along) # 可选生产环境(目前只剩阿龙;云雁/灵谱已于 2026-09-13 移除,见文件头说明)
DEFAULT_PROD_ENV="yunyan" # 无参数且无 TTY(CI/管道)时的默认环境 DEFAULT_PROD_ENV="along" # 无参数且无 TTY(CI/管道)时的默认环境
env_profile() { env_profile() {
DEPLOY_PORT=22 # 各环境通用默认 DEPLOY_PORT=22 # 各环境通用默认
DEPLOY_USER=root DEPLOY_USER=root
case "$DEPLOY_ENV" in case "$DEPLOY_ENV" in
yunyan)
# 2026-08-05 起 admin 生产目标改为上海机(与业务后端 app 同机)。
# 旧目标:美国机 47.253.88.66,其上 yunyan-admin:0.1.1 容器仍在跑,待新环境验证后再停;
# 需要临时回连那台时,把下面三行换成 47.253.88.66 / yunyan-us.pem / /home/work/yunyan-admin。
ENV_DESC="云雁生产环境 · 上海阿里云"
registry_profile yunyan || return 1 # 镜像仓库:云雁
DEPLOY_HOST="139.224.212.237" # ← 云雁生产服务器 IP(上海)
DEPLOY_KEY="/Users/liwei1dao/work/密钥/release_shanghai.pem"
DEPLOY_DIR="/home/work/starpivot/admin" # 远端部署目录(其中 docker-compose.yml/.env 由你自己维护)
;;
lingpu)
ENV_DESC="灵谱生产环境 · 广州阿里云 (lingpu-gz)"
registry_profile lingpu || return 1 # 镜像仓库:灵谱(registry.lingpu.net,实体在香港机)
DEPLOY_HOST="8.166.114.65" # ← 灵谱生产服务器 IP
DEPLOY_KEY=~/.ssh/lingpu-gz.pem # 私钥放 ~/.ssh:~/Documents 会被 macOS TCC 挡住读不出来
DEPLOY_DIR="/home/work/starpivot/admin"
;;
along) along)
ENV_DESC="阿龙生产环境 · 阿里云" ENV_DESC="阿龙生产环境 · 阿里云"
registry_profile along || return 1 # 镜像仓库:registry.ymaikj.com(实体在阿龙测试机 8.133.166.29,账号 admin) registry_profile along || return 1 # 镜像仓库:registry.ymaikj.com(实体在阿龙测试机 8.133.166.29,账号 admin)
@ -72,7 +58,7 @@ env_host_of() { ( DEPLOY_ENV="$1" env_profile >/dev/null 2>&1; printf '%s' "${DE
# ── 参数:<tag|rollback> [环境] ── # ── 参数:<tag|rollback> [环境] ──
ARG1="${1:-}" ARG1="${1:-}"
[ -n "$ARG1" ] || { red "用法: ./prod-deploy.sh <tag|rollback> [环境](如 0.1.1 lingpu)"; exit 1; } [ -n "$ARG1" ] || { red "用法: ./prod-deploy.sh <tag|rollback> [环境](如 0.1.1 along)"; exit 1; }
if [ "$ARG1" = "rollback" ]; then MODE="rollback"; NEW_TAG=""; else MODE="deploy"; NEW_TAG="$ARG1"; fi if [ "$ARG1" = "rollback" ]; then MODE="rollback"; NEW_TAG=""; else MODE="deploy"; NEW_TAG="$ARG1"; fi
DEPLOY_ENV="${2:-}" DEPLOY_ENV="${2:-}"

16
deploy/app/README.md

@ -36,7 +36,9 @@
└── confs/ 附属文件 # 见下「需要提供的东西」 └── confs/ 附属文件 # 见下「需要提供的东西」
``` ```
dev 部署目录默认 `/home/work/voitrans-a11`,prod 默认 `/opt/yunyan/voitrans`(见脚本)。 dev 部署目录默认 `/home/work/starpivot/app`;prod 按**应用**取自 `prod-deploy.sh` 的 `APPS` 表(如 `ym` → `/home/work/ym-a11`)。
> 2026-09-13:prod 的可选区域只剩 `along`(云雁的 shanghai/japan/singapore 已移除,私钥在另一台开发机上)。
### 二、部署 ### 二、部署
@ -44,7 +46,7 @@ dev 部署目录默认 `/home/work/voitrans-a11`,prod 默认 `/opt/yunyan/voit
```bash ```bash
cd deploy/voitrans cd deploy/voitrans
./dev-deploy.sh lingpu # 发布到灵谱测试环境(构建 dev-latest → 推送 → 远端拉取重启) ./dev-deploy.sh along # 发布到阿龙测试环境(构建 dev-latest → 推送 → 远端拉取重启)
./dev-deploy.sh yunyan # 发布到云雁测试环境 ./dev-deploy.sh yunyan # 发布到云雁测试环境
./dev-deploy.sh # 不带参数 → 交互选择测试环境 ./dev-deploy.sh # 不带参数 → 交互选择测试环境
``` ```
@ -53,11 +55,9 @@ cd deploy/voitrans
```bash ```bash
./prod-build.sh 0.1.1 # 构建并推送 starpivot-app:0.1.1 (+latest) ./prod-build.sh 0.1.1 # 构建并推送 starpivot-app:0.1.1 (+latest)
./prod-deploy.sh shanghai 0.1.1 # 部署 0.1.1 到上海 ./prod-deploy.sh ym along 0.2.1 # 部署应用 ym 的 0.2.1 到阿龙生产
./prod-deploy.sh japan 0.1.1 # 部署到日本
./prod-deploy.sh singapore 0.1.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 0.1.4 # 部署 ym 到阿龙正式机(目录 /home/work/ym-a11,容器 ym-a11;镜像走阿龙仓库,需先 ./prod-build.sh 0.1.4 along)
./prod-deploy.sh shanghai rollback # 上海回滚到上一版本(与当前版本来回切换) ./prod-deploy.sh ym along rollback # 回滚到上一版本(与当前版本来回切换)
./prod-deploy.sh # 不带参数 → 交互引导:选应用 / 区域 / 版本 ./prod-deploy.sh # 不带参数 → 交互引导:选应用 / 区域 / 版本
``` ```
@ -75,8 +75,8 @@ cd deploy/voitrans
## 需要填写 / 提供的东西 ## 需要填写 / 提供的东西
- **部署目标与凭据**:写死在各脚本顶部。 - **部署目标与凭据**:写死在各脚本顶部。
- `dev-deploy.sh`:`env_profile()` 里各测试环境(`lingpu` / `yunyan` / `along`)的 `REGISTRY/DEV_HOST/DEV_KEY/DEV_DIR`。 - `dev-deploy.sh`:`env_profile()` 里各测试环境(**目前只有 `along`**)的 `REGISTRY/DEV_HOST/DEV_KEY/DEV_DIR`。
- `prod-deploy.sh`:`region_profile()` 里各区域(`shanghai` / `japan` / `singapore` / `along`)的 `DEPLOY_HOST`; - `prod-deploy.sh`:`region_profile()` 里各区域(**目前只有 `along`**)的 `DEPLOY_HOST`;
另有 `REGION_COMPANY`(该区域拉哪个公司的仓库,可用环境变量 `COMPANY` 覆盖)。 另有 `REGION_COMPANY`(该区域拉哪个公司的仓库,可用环境变量 `COMPANY` 覆盖)。
远端目录只由 `APPS` 表决定(`ym` → `/home/work/ym-a11`),区域里**不再覆盖目录**;容器名由应用 key 推导为 `<应用>-a11`, 远端目录只由 `APPS` 表决定(`ym` → `/home/work/ym-a11`),区域里**不再覆盖目录**;容器名由应用 key 推导为 `<应用>-a11`,
部署时以 `CONTAINER_NAME` 注入 compose 并写回服务器 `.env`,所以同一台机可以并存多个应用。 部署时以 `CONTAINER_NAME` 注入 compose 并写回服务器 `.env`,所以同一台机可以并存多个应用。

27
deploy/app/dev-deploy.sh

@ -6,13 +6,14 @@
# (照着仓库里的 env/env.example、confs/*.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。 # (照着仓库里的 env/env.example、confs/*.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。
# #
# 用法(在 deploy/app/ 下): # 用法(在 deploy/app/ 下):
# ./dev-deploy.sh lingpu # 发布到灵谱测试环境
# ./dev-deploy.sh yunyan # 发布到云雁测试环境
# ./dev-deploy.sh along # 发布到阿龙测试环境 # ./dev-deploy.sh along # 发布到阿龙测试环境
# ./dev-deploy.sh # 不带参数 → 交互选择环境(无 TTY 时用默认环境 lingpu) # ./dev-deploy.sh # 不带参数 → 交互选择环境(目前只有 along 一个,无 TTY 时也用它)
# #
# 参数只用于选环境;开发版镜像始终使用固定 tag dev-latest,每次构建覆盖同一 tag。 # 参数只用于选环境;开发版镜像始终使用固定 tag dev-latest,每次构建覆盖同一 tag。
set -euo pipefail set -euo pipefail
# ⚠️ 2026-09-13:云雁(yunyan)/灵谱(lingpu) 的部署目标已从本脚本移除——本仓库的部署目标只剩阿龙。
# 移除的是「往哪台服务器部署」,**镜像仓库没动**:prod-build.sh 仍可选 yunyan/lingpu 推镜像。
# 要恢复:git log 里找回对应的 env_profile/region_profile 段即可(含 IP 与各自的 SSH 私钥路径)。
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/app SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/app
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根 REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根
@ -34,27 +35,13 @@ IMAGE_NAME="starpivot-app" # 业务单镜像名
# ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║ # ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║
# ║ 新增环境:在 env_profile 加一段 case,并把环境名加进 ENVS 数组。 ║ # ║ 新增环境:在 env_profile 加一段 case,并把环境名加进 ENVS 数组。 ║
# ╚══════════════════════════════════════════════════════════════════════╝ # ╚══════════════════════════════════════════════════════════════════════╝
ENVS=(lingpu yunyan along) # 可选测试环境(交互菜单按此顺序展示) ENVS=(along) # 可选测试环境(目前只剩阿龙;云雁/灵谱已于 2026-09-13 移除,见文件头说明)
DEFAULT_ENV="lingpu" # 无参数且无 TTY(CI/管道)时的默认环境 DEFAULT_ENV="along" # 无参数且无 TTY(CI/管道)时的默认环境
env_profile() { env_profile() {
DEV_PORT=22 # 各环境通用默认 DEV_PORT=22 # 各环境通用默认
DEV_USER=root DEV_USER=root
case "$DEV_ENV" in case "$DEV_ENV" in
lingpu)
ENV_DESC="灵谱 · 香港阿里云 (lingpu-hk)"
registry_profile lingpu # 镜像仓库:灵谱
DEV_HOST="8.217.193.140" # ← 灵谱测试服务器 IP
DEV_KEY=/Users/liwei/Documents/灵谱/lingpu-hk.pem # 本机访问该服务器的 SSH 私钥
DEV_DIR="/home/work/starpivot/app" # 远端部署目录(其中 docker-compose.yml/.env/confs 由你自己维护)
;;
yunyan)
ENV_DESC="云雁测试环境 · 广州阿里云"
registry_profile yunyan # 镜像仓库:云雁(registry.voitrans.net,就在这台机上)
DEV_HOST="8.138.86.223" # ← 云雁测试服务器 IP(2026-07 由腾讯云 124.222.232.218 迁来)
DEV_KEY="/Users/liwei1dao/work/密钥/dev-guangzhou.pem" # 本机访问该服务器的 SSH 私钥(2026-08-18 由 ~/.ssh/aliyun_guangzhou.pem 迁到此处)
DEV_DIR="/home/work/starpivot/app"
;;
along) along)
ENV_DESC="阿龙测试环境 · 阿里云" ENV_DESC="阿龙测试环境 · 阿里云"
registry_profile along # 镜像仓库:就在这台机上(registry.ymaikj.com,账号 admin) registry_profile along # 镜像仓库:就在这台机上(registry.ymaikj.com,账号 admin)
@ -73,8 +60,6 @@ usage() {
用法: ./dev-deploy.sh [环境] (在 deploy/app/ 下运行) 用法: ./dev-deploy.sh [环境] (在 deploy/app/ 下运行)
环境 : ${ENVS[*]// /, }(省略则交互选择;无 TTY 时用默认 ${DEFAULT_ENV}) 环境 : ${ENVS[*]// /, }(省略则交互选择;无 TTY 时用默认 ${DEFAULT_ENV})
例: 例:
./dev-deploy.sh lingpu # 发布到灵谱测试环境
./dev-deploy.sh yunyan # 发布到云雁测试环境
./dev-deploy.sh along # 发布到阿龙测试环境 ./dev-deploy.sh along # 发布到阿龙测试环境
EOF EOF
} }

4
deploy/app/prod-build.sh

@ -50,5 +50,5 @@ docker push "$IMG_VER"
docker push "$IMG_LATEST" docker push "$IMG_LATEST"
green "✓ 已推送 $IMG_VER 与 $IMG_LATEST" green "✓ 已推送 $IMG_VER 与 $IMG_LATEST"
echo " 部署:./prod-deploy.sh <应用> <shanghai|japan|singapore|along> $VERSION" echo " 部署:./prod-deploy.sh <应用> along $VERSION"
echo " 例:./prod-deploy.sh voitrans shanghai $VERSION" echo " 例:./prod-deploy.sh ym along $VERSION"

46
deploy/app/prod-deploy.sh

@ -13,13 +13,16 @@
# #
# 用法(在 deploy/voitrans/ 下): # 用法(在 deploy/voitrans/ 下):
# ./prod-deploy.sh # 不带参数 → 交互式引导:逐步选「应用 / 区域 / 版本」,部署前确认 # ./prod-deploy.sh # 不带参数 → 交互式引导:逐步选「应用 / 区域 / 版本」,部署前确认
# ./prod-deploy.sh <应用> <区域> <tag> # 直接部署,如 ./prod-deploy.sh voitrans shanghai 0.1.1 # ./prod-deploy.sh <应用> <区域> <tag> # 直接部署,如 ./prod-deploy.sh ym along 0.2.1
# ./prod-deploy.sh <应用> <区域> rollback # 回滚到上一次部署版本(与当前版本来回切换) # ./prod-deploy.sh <应用> <区域> rollback # 回滚到上一次部署版本(与当前版本来回切换)
# ./prod-deploy.sh <区域> <tag> # 省略应用 → 默认首个应用(兼容旧写法) # ./prod-deploy.sh <区域> <tag> # 省略应用 → 默认首个应用(兼容旧写法)
# ./prod-deploy.sh -h # 查看用法 # ./prod-deploy.sh -h # 查看用法
# 应用: 见 APPS(默认 voitrans) 区域: shanghai | japan | singapore | along # 应用: 见 APPS(默认 voitrans) 区域: along
# 镜像仓库跟区域走:shanghai/japan/singapore → 云雁仓库,along → 阿龙仓库;需与 prod-build.sh 所选公司一致。 # 镜像仓库跟区域走:along → 阿龙仓库 registry.ymaikj.com;需与 prod-build.sh 所选公司一致。
set -euo pipefail set -euo pipefail
# ⚠️ 2026-09-13:云雁(yunyan)/灵谱(lingpu) 的部署目标已从本脚本移除——本仓库的部署目标只剩阿龙。
# 移除的是「往哪台服务器部署」,**镜像仓库没动**:prod-build.sh 仍可选 yunyan/lingpu 推镜像。
# 要恢复:git log 里找回对应的 env_profile/region_profile 段即可(含 IP 与各自的 SSH 私钥路径)。
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@ -33,8 +36,8 @@ red() { printf "\033[31m%s\033[0m\n" "$*"; }
yellow(){ printf "\033[33m%s\033[0m\n" "$*"; } yellow(){ printf "\033[33m%s\033[0m\n" "$*"; }
# ── 私有仓库凭据(写死;公开仓库前清空 REGISTRY_PASS)── # ── 私有仓库凭据(写死;公开仓库前清空 REGISTRY_PASS)──
# 镜像仓库按「区域所属公司」自动选(见 region_profile 里的 REGION_COMPANY:三个云雁区域走 yunyan, # 镜像仓库按「区域所属公司」自动选(见 region_profile 里的 REGION_COMPANY:along 走自己的
# 阿龙区域走 along 自己的 registry.ymaikj.com),也可用环境变量强制指定:COMPANY=lingpu ./prod-deploy.sh … # registry.ymaikj.com),也可用环境变量强制指定:COMPANY=lingpu ./prod-deploy.sh …
# 所选公司必须与 prod-build.sh 构建时所选公司一致,否则拉不到镜像。 # 所选公司必须与 prod-build.sh 构建时所选公司一致,否则拉不到镜像。
COMPANY_OVERRIDE="${COMPANY:-}" # 环境变量显式指定的公司;留空 = 跟区域走 COMPANY_OVERRIDE="${COMPANY:-}" # 环境变量显式指定的公司;留空 = 跟区域走
# 共用镜像:所有应用都用这一个。名字走 prod_image_name(),与 prod-build.sh 同源,防漂移。 # 共用镜像:所有应用都用这一个。名字走 prod_image_name(),与 prod-build.sh 同源,防漂移。
@ -79,29 +82,11 @@ DEFAULT_APP="${APPS[0]%%|*}" # 省略应用时的默认(首个)
# ║ 写死在此(本文件纳入 git)。公开仓库前清空 HOST/KEY。 ║ # ║ 写死在此(本文件纳入 git)。公开仓库前清空 HOST/KEY。 ║
# ║ 新增区域:在 region_profile 加一段 case,并把区域名加进 REGIONS 数组。 ║ # ║ 新增区域:在 region_profile 加一段 case,并把区域名加进 REGIONS 数组。 ║
# ╚══════════════════════════════════════════════════════════════════════╝ # ╚══════════════════════════════════════════════════════════════════════╝
REGIONS=(shanghai japan singapore along) # 可选环境/区域(交互菜单按此顺序展示) REGIONS=(along) # 可选环境/区域(目前只剩阿龙;云雁三地已于 2026-09-13 移除,见文件头说明)
region_profile() { # 远端部署目录/容器名由「应用」决定(见 APPS),此处只管各区域服务器连接信息 region_profile() { # 远端部署目录/容器名由「应用」决定(见 APPS),此处只管各区域服务器连接信息
REGION_COMPANY="$DEFAULT_COMPANY" # 该区域镜像仓库所属公司(默认云雁;可被环境变量 COMPANY 覆盖) REGION_COMPANY="$DEFAULT_COMPANY" # 该区域镜像仓库所属公司(可被环境变量 COMPANY 覆盖;along 段下面会改写成 along)
case "$REGION" in case "$REGION" in
shanghai)
DEPLOY_HOST="139.224.212.237" # ← 上海生产服务器 IP(必填)
DEPLOY_PORT=22
DEPLOY_USER=root
DEPLOY_KEY="/Users/liwei1dao/work/密钥/release_shanghai.pem" # ← 上海服务器 SSH 私钥(各区域不同)
;;
japan)
DEPLOY_HOST="8.216.34.101" # ← 日本生产服务器 IP(必填)
DEPLOY_PORT=22
DEPLOY_USER=root
DEPLOY_KEY=/Users/liwei/Documents/key/aliyun_japan.pem # ← 日本服务器 SSH 私钥(各区域不同)
;;
singapore)
DEPLOY_HOST="47.84.198.24" # ← 新加坡生产服务器 IP(必填)
DEPLOY_PORT=22
DEPLOY_USER=root
DEPLOY_KEY=/Users/liwei/Documents/key/alibaba_singapore.pem # ← 新加坡服务器 SSH 私钥(各区域不同)
;;
along) along)
DEPLOY_HOST="47.116.104.181" # ← 阿龙生产服务器 IP DEPLOY_HOST="47.116.104.181" # ← 阿龙生产服务器 IP
DEPLOY_PORT=22 DEPLOY_PORT=22
@ -135,7 +120,7 @@ usage() {
./prod-deploy.sh 0.1.4 # 只给版本 → 交互里选应用、区域 ./prod-deploy.sh 0.1.4 # 只给版本 → 交互里选应用、区域
./prod-deploy.sh ym along # 给应用+区域 → 交互里选版本(会列出仓库里已有的 tag) ./prod-deploy.sh ym along # 给应用+区域 → 交互里选版本(会列出仓库里已有的 tag)
./prod-deploy.sh ym along rollback # ym 阿龙回滚到上一版本 ./prod-deploy.sh ym along rollback # ym 阿龙回滚到上一版本
./prod-deploy.sh shanghai 0.1.1 # 省略应用 → 默认首个应用(${DEFAULT_APP:-?}) ./prod-deploy.sh along 0.2.1 # 省略应用 → 默认首个应用(${DEFAULT_APP:-?})
提示: 不带参数(或参数不全)运行将进入交互式引导,逐步选择应用 / 环境 / 版本。 提示: 不带参数(或参数不全)运行将进入交互式引导,逐步选择应用 / 环境 / 版本。
EOF EOF
@ -148,6 +133,9 @@ EOF
case "${1:-}" in -h|--help|help) usage; exit 0 ;; esac case "${1:-}" in -h|--help|help) usage; exit 0 ;; esac
is_region() { local r; for r in "${REGIONS[@]}"; do [ "$r" = "$1" ] && return 0; done; return 1; } is_region() { local r; for r in "${REGIONS[@]}"; do [ "$r" = "$1" ] && return 0; done; return 1; }
# 2026-09-13 移除的区域名:它们是普通串,会被 is_tag_like 当成 tag 吃掉,
# 报出「版本指定了两次」这种驴唇不对马嘴的错。这里当场点明,省得对着参数解析找半天。
is_removed_region() { case "$1" in shanghai|japan|singapore) return 0 ;; *) return 1 ;; esac; }
is_tag_like() { case "$1" in ''|*[!A-Za-z0-9._-]*) return 1 ;; *) return 0 ;; esac; } is_tag_like() { case "$1" in ''|*[!A-Za-z0-9._-]*) return 1 ;; *) return 0 ;; esac; }
APP_KEY=""; REGION=""; TAG_ARG=""; MODE=""; INTERACTIVE=0 APP_KEY=""; REGION=""; TAG_ARG=""; MODE=""; INTERACTIVE=0
@ -155,6 +143,12 @@ for a in "$@"; do
if is_app "$a"; then if is_app "$a"; then
[ -z "$APP_KEY" ] || { red "✗ 应用指定了两次: $APP_KEY / $a"; exit 1; } [ -z "$APP_KEY" ] || { red "✗ 应用指定了两次: $APP_KEY / $a"; exit 1; }
APP_KEY="$a" APP_KEY="$a"
elif is_removed_region "$a"; then
# ⚠️ 变量名必须写成 ${a} 而不是 $a:紧跟其后的是多字节的「」,在非 UTF-8 locale 下
# bash 会把它的首字节并进变量名,报出 `a?: unbound variable`(set -u)。
red "✗ 区域「${a}」已于 2026-09-13 从本脚本移除(云雁三地的 SSH 私钥在另一台开发机上,本机连不上)。"
red " 现可用区域: ${REGIONS[*]}。需要恢复请从 git 历史取回 region_profile 对应段。"
exit 1
elif is_region "$a"; then elif is_region "$a"; then
[ -z "$REGION" ] || { red "✗ 区域指定了两次: $REGION / $a"; exit 1; } [ -z "$REGION" ] || { red "✗ 区域指定了两次: $REGION / $a"; exit 1; }
REGION="$a" REGION="$a"

10
deploy/console/README.md

@ -18,7 +18,7 @@
| `env/env.example` | 运行时环境**模板**(只一份,不分环境);真实 `.env` 被 gitignore | | `env/env.example` | 运行时环境**模板**(只一份,不分环境);真实 `.env` 被 gitignore |
| `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 远端拉取重启(固定 tag `dev-latest`) | | `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 远端拉取重启(固定 tag `dev-latest`) |
| `prod-build.sh` | **生产①**:构建带版本号镜像并推送(`:版本` + `:latest`),不部署 | | `prod-build.sh` | **生产①**:构建带版本号镜像并推送(`:版本` + `:latest`),不部署 |
| `prod-deploy.sh` | **生产②**:部署指定版本到指定生产环境(`yunyan`/`lingpu`),支持回滚 | | `prod-deploy.sh` | **生产②**:部署指定版本到生产环境(目前只有 `along`),支持回滚 |
## 用法 ## 用法
@ -51,12 +51,12 @@ docker compose --env-file .env up -d # 本机起 starpivot-console
```bash ```bash
cd deploy/console cd deploy/console
./dev-deploy.sh lingpu # 开发:发布到灵谱测试环境(构建 dev-latest → 推送 → 远端拉取重启) ./dev-deploy.sh along # 开发:发布到阿龙测试环境(构建 dev-latest → 推送 → 远端拉取重启)
./dev-deploy.sh yunyan # 开发:发布到云雁测试环境 ./dev-deploy.sh yunyan # 开发:发布到云雁测试环境
./dev-deploy.sh # 不带参数 → 交互选择测试环境 ./dev-deploy.sh # 不带参数 → 交互选择测试环境
./prod-build.sh 0.1.1 yunyan # 生产①:构建并推送 starpivot-console:0.1.1 (+latest);第2参数选公司 ./prod-build.sh 0.1.1 yunyan # 生产①:构建并推送 starpivot-console:0.1.1 (+latest);第2参数选公司
./prod-deploy.sh 0.1.1 lingpu # 生产②:部署 0.1.1 到指定生产环境(yunyan|lingpu|along),省略环境则交互选 ./prod-deploy.sh 0.2.1 along # 生产②:部署 0.2.1 到生产环境(目前只有 along,省略即用它)
./prod-deploy.sh rollback lingpu # 生产回滚到上一版本(与当前版本来回切换) ./prod-deploy.sh rollback along # 生产回滚到上一版本(与当前版本来回切换)
``` ```
> 改了服务器上的 `.env` 后,必须经 `up`(脚本里的 `docker compose up -d`)**重建容器**才生效,`restart` 不会重读环境变量。 > 改了服务器上的 `.env` 后,必须经 `up`(脚本里的 `docker compose up -d`)**重建容器**才生效,`restart` 不会重读环境变量。
@ -64,7 +64,7 @@ cd deploy/console
## 需要填写 / 提供的东西 ## 需要填写 / 提供的东西
- **部署目标与凭据**:写死在各脚本顶部。`dev-deploy.sh` 的 `env_profile()`(每个测试环境一套 `REGISTRY/DEV_HOST/DEV_KEY/DEV_DIR`,现有 `lingpu`、`yunyan`、`along`;新增环境加一段 case 并写进 `ENVS`)、`prod-deploy.sh` 的 `env_profile()`(每个生产环境一套 `REGISTRY/IMAGE_NAME/DEPLOY_HOST/DEPLOY_KEY/DEPLOY_DIR`,现有 `yunyan`、`lingpu`、`along`;新增环境加一段 case 并写进 `PROD_ENVS`); - **部署目标与凭据**:写死在各脚本顶部。`dev-deploy.sh` 的 `env_profile()`(每个测试环境一套 `REGISTRY/DEV_HOST/DEV_KEY/DEV_DIR`,**目前只有 `along`**;新增环境加一段 case 并写进 `ENVS`)、`prod-deploy.sh` 的 `env_profile()`(每个生产环境一套 `REGISTRY/IMAGE_NAME/DEPLOY_HOST/DEPLOY_KEY/DEPLOY_DIR`,**目前只有 `along`**;新增环境加一段 case 并写进 `PROD_ENVS`);
仓库账号 `admin` / 密码已写死;`DEPLOY_KEY` 默认 `~/.ssh/id_rsa`。 仓库账号 `admin` / 密码已写死;`DEPLOY_KEY` 默认 `~/.ssh/id_rsa`。
- ⚠️ 本目录脚本纳入 git,**公开仓库前务必清空** `*_HOST` 与 `REGISTRY_PASS`。 - ⚠️ 本目录脚本纳入 git,**公开仓库前务必清空** `*_HOST` 与 `REGISTRY_PASS`。
- **依赖端点**:填服务器 `.env` 的 `POSTGRES_DSN / REDIS_* / NATS_URL`,以及 `CONSOLE_TOKEN_KEY`、 - **依赖端点**:填服务器 `.env` 的 `POSTGRES_DSN / REDIS_* / NATS_URL`,以及 `CONSOLE_TOKEN_KEY`、

27
deploy/console/dev-deploy.sh

@ -6,13 +6,14 @@
# (照着仓库里的 env/env.example、confs/console.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。 # (照着仓库里的 env/env.example、confs/console.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。
# #
# 用法(在 deploy/console/ 下): # 用法(在 deploy/console/ 下):
# ./dev-deploy.sh lingpu # 发布到灵谱测试环境
# ./dev-deploy.sh yunyan # 发布到云雁测试环境
# ./dev-deploy.sh along # 发布到阿龙测试环境 # ./dev-deploy.sh along # 发布到阿龙测试环境
# ./dev-deploy.sh # 不带参数 → 交互选择环境(无 TTY 时用默认环境 lingpu) # ./dev-deploy.sh # 不带参数 → 交互选择环境(目前只有 along 一个,无 TTY 时也用它)
# #
# 开发版镜像始终使用固定 tag dev-latest,每次构建覆盖同一 tag。 # 开发版镜像始终使用固定 tag dev-latest,每次构建覆盖同一 tag。
set -euo pipefail set -euo pipefail
# ⚠️ 2026-09-13:云雁(yunyan)/灵谱(lingpu) 的部署目标已从本脚本移除——本仓库的部署目标只剩阿龙。
# 移除的是「往哪台服务器部署」,**镜像仓库没动**:prod-build.sh 仍可选 yunyan/lingpu 推镜像。
# 要恢复:git log 里找回对应的 env_profile/region_profile 段即可(含 IP 与各自的 SSH 私钥路径)。
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/console SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/console
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根 REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根
@ -34,27 +35,13 @@ IMAGE_NAME="starpivot-console" # 业务镜像名
# ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║ # ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║
# ║ 新增环境:在 env_profile 加一段 case,并把环境名加进 ENVS 数组。 ║ # ║ 新增环境:在 env_profile 加一段 case,并把环境名加进 ENVS 数组。 ║
# ╚══════════════════════════════════════════════════════════════════════╝ # ╚══════════════════════════════════════════════════════════════════════╝
ENVS=(lingpu yunyan along) # 可选测试环境(交互菜单按此顺序展示) ENVS=(along) # 可选测试环境(目前只剩阿龙;云雁/灵谱已于 2026-09-13 移除,见文件头说明)
DEFAULT_ENV="lingpu" # 无参数且无 TTY(CI/管道)时的默认环境 DEFAULT_ENV="along" # 无参数且无 TTY(CI/管道)时的默认环境
env_profile() { env_profile() {
DEV_PORT=22 # 各环境通用默认 DEV_PORT=22 # 各环境通用默认
DEV_USER=root DEV_USER=root
case "$DEV_ENV" in case "$DEV_ENV" in
lingpu)
ENV_DESC="灵谱 · 香港阿里云 (lingpu-hk)"
registry_profile lingpu # 镜像仓库:灵谱
DEV_HOST="8.217.193.140" # ← 灵谱测试服务器 IP
DEV_KEY=/Users/liwei/Documents/灵谱/lingpu-hk.pem # 本机访问该服务器的 SSH 私钥
DEV_DIR="/home/work/starpivot/console" # 远端部署目录(其中 docker-compose.yml/.env/confs 由你自己维护)
;;
yunyan)
ENV_DESC="云雁测试环境 · 广州阿里云"
registry_profile yunyan # 镜像仓库:云雁(registry.voitrans.net,就在这台机上)
DEV_HOST="8.138.86.223" # ← 云雁测试服务器 IP(2026-07 由腾讯云 124.222.232.218 迁来)
DEV_KEY="/Users/liwei1dao/work/密钥/dev-guangzhou.pem" # 本机访问该服务器的 SSH 私钥(2026-08-18 由 ~/.ssh/aliyun_guangzhou.pem 迁到此处)
DEV_DIR="/home/work/starpivot/console"
;;
along) along)
ENV_DESC="阿龙测试环境 · 阿里云" ENV_DESC="阿龙测试环境 · 阿里云"
registry_profile along # 镜像仓库:就在这台机上(registry.ymaikj.com,账号 admin) registry_profile along # 镜像仓库:就在这台机上(registry.ymaikj.com,账号 admin)
@ -73,8 +60,6 @@ usage() {
用法: ./dev-deploy.sh [环境] (在 deploy/console/ 下运行) 用法: ./dev-deploy.sh [环境] (在 deploy/console/ 下运行)
环境 : ${ENVS[*]// /, }(省略则交互选择;无 TTY 时用默认 ${DEFAULT_ENV}) 环境 : ${ENVS[*]// /, }(省略则交互选择;无 TTY 时用默认 ${DEFAULT_ENV})
例: 例:
./dev-deploy.sh lingpu # 发布到灵谱测试环境
./dev-deploy.sh yunyan # 发布到云雁测试环境
./dev-deploy.sh along # 发布到阿龙测试环境 ./dev-deploy.sh along # 发布到阿龙测试环境
EOF EOF
} }

30
deploy/console/prod-deploy.sh

@ -6,11 +6,14 @@
# (照着仓库里的 env/env.example、confs/console.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。 # (照着仓库里的 env/env.example、confs/console.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。
# #
# 用法(在 deploy/console/ 下): # 用法(在 deploy/console/ 下):
# ./prod-deploy.sh <tag> [环境] # 部署指定版本,如 ./prod-deploy.sh 0.1.1 lingpu # ./prod-deploy.sh <tag> [环境] # 部署指定版本,如 ./prod-deploy.sh 0.1.1 along
# ./prod-deploy.sh rollback [环境] # 回滚到上一次部署版本(与当前版本来回切换) # ./prod-deploy.sh rollback [环境] # 回滚到上一次部署版本(与当前版本来回切换)
# 环境 = yunyan(上海)| lingpu(广州)| along(阿龙 47.116.104.181);省略 → 交互选择,无 TTY 时用默认 yunyan。 # 环境 = along(阿龙 47.116.104.181);省略 → 直接用它。
# ⚠️ 环境必须与 prod-build.sh 构建时所选公司一致,否则拉不到镜像。 # ⚠️ 环境必须与 prod-build.sh 构建时所选公司一致,否则拉不到镜像。
set -euo pipefail set -euo pipefail
# ⚠️ 2026-09-13:云雁(yunyan)/灵谱(lingpu) 的部署目标已从本脚本移除——本仓库的部署目标只剩阿龙。
# 移除的是「往哪台服务器部署」,**镜像仓库没动**:prod-build.sh 仍可选 yunyan/lingpu 推镜像。
# 要恢复:git log 里找回对应的 env_profile/region_profile 段即可(含 IP 与各自的 SSH 私钥路径)。
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@ -32,30 +35,13 @@ DOCKER_NETWORK="1panel-network" # compose 外部网络名(与服务
# 容器名:必须与 docker-compose.yml 的 container_name 一字不差(本脚本会上传覆盖那份 compose)。 # 容器名:必须与 docker-compose.yml 的 container_name 一字不差(本脚本会上传覆盖那份 compose)。
# 仅用于 up 冲突时清理同名遗留容器;不随环境变化。 # 仅用于 up 冲突时清理同名遗留容器;不随环境变化。
CONTAINER_NAME="starpivot-console" CONTAINER_NAME="starpivot-console"
PROD_ENVS=(yunyan lingpu along) # 可选生产环境(交互菜单按此顺序展示) PROD_ENVS=(along) # 可选生产环境(目前只剩阿龙;云雁/灵谱已于 2026-09-13 移除,见文件头说明)
DEFAULT_PROD_ENV="yunyan" # 无参数且无 TTY(CI/管道)时的默认环境 DEFAULT_PROD_ENV="along" # 无参数且无 TTY(CI/管道)时的默认环境
env_profile() { env_profile() {
DEPLOY_PORT=22 # 各环境通用默认 DEPLOY_PORT=22 # 各环境通用默认
DEPLOY_USER=root DEPLOY_USER=root
case "$DEPLOY_ENV" in case "$DEPLOY_ENV" in
yunyan)
# 2026-08-05 起 console 生产目标改为上海机(与业务后端 app 同机)。
# 旧目标:美国机 47.253.88.66,其上 console-a11:0.1.1 容器仍在跑,待新环境验证后再停;
# 需要临时回连那台时,把下面三行换成 47.253.88.66 / yunyan-us.pem / /home/work/console-a11。
ENV_DESC="云雁生产环境 · 上海阿里云"
registry_profile yunyan || return 1 # 镜像仓库:云雁
DEPLOY_HOST="139.224.212.237" # ← 云雁生产服务器 IP(上海)
DEPLOY_KEY="/Users/liwei1dao/work/密钥/release_shanghai.pem"
DEPLOY_DIR="/home/work/starpivot/console" # 远端部署目录(其中 docker-compose.yml/.env/confs 由你自己维护)
;;
lingpu)
ENV_DESC="灵谱生产环境 · 广州阿里云 (lingpu-gz)"
registry_profile lingpu || return 1 # 镜像仓库:灵谱(registry.lingpu.net,实体在香港机)
DEPLOY_HOST="8.166.114.65" # ← 灵谱生产服务器 IP
DEPLOY_KEY=~/.ssh/lingpu-gz.pem # 私钥放 ~/.ssh:~/Documents 会被 macOS TCC 挡住读不出来
DEPLOY_DIR="/home/work/starpivot/console"
;;
along) along)
ENV_DESC="阿龙生产环境 · 阿里云" ENV_DESC="阿龙生产环境 · 阿里云"
registry_profile along || return 1 # 镜像仓库:registry.ymaikj.com(实体在阿龙测试机 8.133.166.29,账号 admin) registry_profile along || return 1 # 镜像仓库:registry.ymaikj.com(实体在阿龙测试机 8.133.166.29,账号 admin)
@ -72,7 +58,7 @@ env_host_of() { ( DEPLOY_ENV="$1" env_profile >/dev/null 2>&1; printf '%s' "${DE
# ── 参数:<tag|rollback> [环境] ── # ── 参数:<tag|rollback> [环境] ──
ARG1="${1:-}" ARG1="${1:-}"
[ -n "$ARG1" ] || { red "用法: ./prod-deploy.sh <tag|rollback> [环境](如 0.1.1 lingpu)"; exit 1; } [ -n "$ARG1" ] || { red "用法: ./prod-deploy.sh <tag|rollback> [环境](如 0.1.1 along)"; exit 1; }
if [ "$ARG1" = "rollback" ]; then MODE="rollback"; NEW_TAG=""; else MODE="deploy"; NEW_TAG="$ARG1"; fi if [ "$ARG1" = "rollback" ]; then MODE="rollback"; NEW_TAG=""; else MODE="deploy"; NEW_TAG="$ARG1"; fi
DEPLOY_ENV="${2:-}" DEPLOY_ENV="${2:-}"

9
deploy/dev-deploy-all.sh

@ -8,7 +8,6 @@
# 用法(在 deploy/ 下): # 用法(在 deploy/ 下):
# ./dev-deploy-all.sh # 交互选公司(环境),发布全部三个服务 # ./dev-deploy-all.sh # 交互选公司(环境),发布全部三个服务
# ./dev-deploy-all.sh along # 发布全部服务到阿龙测试环境 # ./dev-deploy-all.sh along # 发布全部服务到阿龙测试环境
# ./dev-deploy-all.sh yunyan app admin # 只发 app 和 admin 到云雁测试环境
# ./dev-deploy-all.sh along -y -k # 不确认、失败也继续发后面的服务 # ./dev-deploy-all.sh along -y -k # 不确认、失败也继续发后面的服务
# #
# 发布顺序固定 app → console → admin:admin 的页面要连 console,console 又依赖业务库, # 发布顺序固定 app → console → admin:admin 的页面要连 console,console 又依赖业务库,
@ -17,6 +16,9 @@
# ⚠️ macOS 自带 bash 3.2 不认多字节字符边界,`"$rc)"` 会被当成变量名 `rc)` 而在 set -u 下 # ⚠️ macOS 自带 bash 3.2 不认多字节字符边界,`"$rc)"` 会被当成变量名 `rc)` 而在 set -u 下
# 报 unbound variable。所以本文件里凡是变量后面紧跟中文标点的,一律写成 ${rc}。 # 报 unbound variable。所以本文件里凡是变量后面紧跟中文标点的,一律写成 ${rc}。
set -euo pipefail set -euo pipefail
# ⚠️ 2026-09-13:云雁(yunyan)/灵谱(lingpu) 的部署目标已从本脚本移除——本仓库的部署目标只剩阿龙。
# 移除的是「往哪台服务器部署」,**镜像仓库没动**:prod-build.sh 仍可选 yunyan/lingpu 推镜像。
# 要恢复:git log 里找回对应的 env_profile/region_profile 段即可(含 IP 与各自的 SSH 私钥路径)。
DEPLOY_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 仓库的 deploy/ DEPLOY_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 仓库的 deploy/
source "$DEPLOY_ROOT/registry-profiles.sh" # 公司 → 仓库地址/账号 source "$DEPLOY_ROOT/registry-profiles.sh" # 公司 → 仓库地址/账号
@ -28,8 +30,8 @@ red() { printf "\033[31m%s\033[0m\n" "$*"; }
# 可选公司(测试环境)。名字与各服务 dev-deploy.sh 的 ENVS 一致,脚本启动时会交叉校验, # 可选公司(测试环境)。名字与各服务 dev-deploy.sh 的 ENVS 一致,脚本启动时会交叉校验,
# 一边加了新环境另一边忘了加时会直接报错,不会发到一半才炸。 # 一边加了新环境另一边忘了加时会直接报错,不会发到一半才炸。
ENVS=(lingpu yunyan along) ENVS=(along)
DEFAULT_ENV="lingpu" # 无参数且无 TTY(CI/管道)时用它 DEFAULT_ENV="along" # 无参数且无 TTY(CI/管道)时用它
ALL_SVCS=(app console admin) # 默认发布顺序 ALL_SVCS=(app console admin) # 默认发布顺序
usage() { usage() {
@ -47,7 +49,6 @@ usage() {
例: 例:
./dev-deploy-all.sh along # 全量发到阿龙测试环境 ./dev-deploy-all.sh along # 全量发到阿龙测试环境
./dev-deploy-all.sh yunyan console # 只把 console 发到云雁测试环境
EOF EOF
} }

5
deploy/registry-profiles.sh

@ -21,13 +21,13 @@ registry_profile() {
case "${1:-}" in case "${1:-}" in
yunyan) yunyan)
COMPANY_DESC="云雁" COMPANY_DESC="云雁"
REGISTRY="registry.voitrans.net" # 仓库实体在广州机 8.138.86.223(1Panel OpenResty 反代 → docker-registry:5000) REGISTRY="registry.voitrans.net" # 云雁自有仓库(1Panel OpenResty 反代 → docker-registry:5000)
REGISTRY_USER="liwei" REGISTRY_USER="liwei"
REGISTRY_PASS="li13451234" REGISTRY_PASS="li13451234"
;; ;;
lingpu) lingpu)
COMPANY_DESC="灵谱" COMPANY_DESC="灵谱"
REGISTRY="registry.lingpu.net" # 仓库实体在灵谱香港机 8.217.193.140 REGISTRY="registry.lingpu.net" # 灵谱自有仓库
REGISTRY_USER="liwei" REGISTRY_USER="liwei"
REGISTRY_PASS="li13451234" REGISTRY_PASS="li13451234"
;; ;;
@ -50,7 +50,6 @@ registry_profile() {
# #
# admin/console:各公司都用 starpivot-* 新命名,与 docker-compose.yml 里写死的 image/container_name # admin/console:各公司都用 starpivot-* 新命名,与 docker-compose.yml 里写死的 image/container_name
# 一致。云雁的 admin/console 生产目标已于 2026-08-05 迁到上海机(那台没有历史容器), # 一致。云雁的 admin/console 生产目标已于 2026-08-05 迁到上海机(那台没有历史容器),
# 旧名 yunyan-admin / console-a11 只留在待下线的美国机 47.253.88.66 上,不再由脚本部署。
# app:2026-08-10 起统一为 starpivot-app。旧名 voitrans-a11 已废弃——仓库里的旧 tag 不再更新, # app:2026-08-10 起统一为 starpivot-app。旧名 voitrans-a11 已废弃——仓库里的旧 tag 不再更新,
# 首次用新名部署需先 ./prod-build.sh <version> 推一版(旧 tag 不会自动搬过来)。 # 首次用新名部署需先 ./prod-build.sh <version> 推一版(旧 tag 不会自动搬过来)。
# #

2
docs/运维-容器断网与免费证书排查.md

@ -1,6 +1,6 @@
# 运维排查记录:容器无法访问外网 + NPM 免费证书签发 # 运维排查记录:容器无法访问外网 + NPM 免费证书签发
> 场景:阿里云 ECS(lingpu-hk 8.217.193.140),Nginx Proxy Manager(NPM)容器化部署, > 场景:阿里云 ECS,Nginx Proxy Manager(NPM)容器化部署,
> 给 starpivot-dev.lingpu.net 申请 Let's Encrypt 证书(阿里云 DNS challenge)。 > 给 starpivot-dev.lingpu.net 申请 Let's Encrypt 证书(阿里云 DNS challenge)。
> 记录于 2026-07-22。 > 记录于 2026-07-22。

Loading…
Cancel
Save