26 changed files with 1012 additions and 12 deletions
@ -0,0 +1,63 @@ |
|||
# voitrans 业务单镜像:gateway + home + api + mcp + timer 五个集群(RPCX/ETCD)服务打进一个镜像 |
|||
# 构建上下文:apps/services/ |
|||
# 用 shell 脚本(entrypoint)管理多进程:任一服务退出即整个容器退出,交给 docker restart 策略拉起。 |
|||
# 运行期配置 confs/ 由宿主机 deploy/voitrans/ 目录挂入当 /app(见 docker-compose.yml)。 |
|||
|
|||
# ============ Stage 1: Builder ============ |
|||
FROM golang:1.25-alpine AS builder |
|||
|
|||
ENV GOPROXY=https://goproxy.cn,direct \ |
|||
CGO_ENABLED=0 \ |
|||
GOOS=linux \ |
|||
GOARCH=amd64 |
|||
|
|||
WORKDIR /src |
|||
|
|||
# 预热依赖缓存 |
|||
COPY go.mod go.sum ./ |
|||
RUN --mount=type=cache,target=/go/pkg/mod \ |
|||
go mod download |
|||
|
|||
COPY . . |
|||
|
|||
# 编译 5 个服务(统一 .a11 后缀,与镜像名 voitrans-a11 对齐) |
|||
RUN --mount=type=cache,target=/go/pkg/mod \ |
|||
--mount=type=cache,target=/root/.cache/go-build \ |
|||
mkdir -p /out && \ |
|||
go build -trimpath -ldflags "-s -w" -o /out/gateway.a11 ./services/gateway/ && \ |
|||
go build -trimpath -ldflags "-s -w" -o /out/home.a11 ./services/home/ && \ |
|||
go build -trimpath -ldflags "-s -w" -o /out/api.a11 ./services/api/ && \ |
|||
go build -trimpath -ldflags "-s -w" -o /out/mcp.a11 ./services/mcp/ && \ |
|||
go build -trimpath -ldflags "-s -w" -o /out/timer.a11 ./services/timer/ |
|||
|
|||
# ============ Stage 2: Runtime ============ |
|||
FROM alpine:3.20 |
|||
|
|||
# nc 用于 compose 健康检查探测端口 |
|||
RUN apk add --no-cache tzdata ca-certificates && \ |
|||
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ |
|||
echo "Asia/Shanghai" > /etc/timezone |
|||
|
|||
# 二进制与启动脚本放 /opt/voitrans,避免与宿主机挂载的 /app 冲突 |
|||
COPY --from=builder /out/gateway.a11 /opt/voitrans/a11/gateway.a11 |
|||
COPY --from=builder /out/home.a11 /opt/voitrans/a11/home.a11 |
|||
COPY --from=builder /out/api.a11 /opt/voitrans/a11/api.a11 |
|||
COPY --from=builder /out/mcp.a11 /opt/voitrans/a11/mcp.a11 |
|||
COPY --from=builder /out/timer.a11 /opt/voitrans/a11/timer.a11 |
|||
COPY entrypoint.voitrans.sh /opt/voitrans/entrypoint.sh |
|||
|
|||
RUN chmod +x /opt/voitrans/entrypoint.sh /opt/voitrans/a11/*.a11 && \ |
|||
mkdir -p /app |
|||
|
|||
# 工作目录:宿主机把 deploy/voitrans 目录挂为 /app,内含 confs/ log/ 及各服务运行期资源 |
|||
WORKDIR /app |
|||
|
|||
# 端口声明(容器内部,对外经反代/compose 暴露) |
|||
# gateway: 7001 RPCX / 7100 HTTP |
|||
# home: 7002 RPCX |
|||
# timer: 7003 RPCX |
|||
# mcp: 7004 RPCX / 7300 HTTP(MCP) |
|||
# api: 7005 RPCX / 8080 Console |
|||
EXPOSE 7001 7002 7003 7004 7005 7100 7300 8080 |
|||
|
|||
ENTRYPOINT ["/opt/voitrans/entrypoint.sh"] |
|||
@ -0,0 +1,54 @@ |
|||
#!/bin/sh |
|||
# voitrans 容器入口:在一个容器内拉起 5 个集群服务,shell 监控进程。 |
|||
# |
|||
# 运行时布局(宿主机挂 deploy/voitrans 目录为 /app,二进制在镜像内 /opt/voitrans): |
|||
# /app/ ← 宿主机 deploy/voitrans 目录 |
|||
# ├── confs/*.yaml ← 各服务配置(gateway/home/api/mcp/timer) |
|||
# ├── log/ ← 日志输出 |
|||
# └── ... ← 其他运行期资源(密钥/词库等,如有) |
|||
# /opt/voitrans/a11/*.a11 ← 镜像内二进制 |
|||
# |
|||
# cwd=/app,所以配置里的相对路径 ./confs/... ./log/... 均直接生效。 |
|||
# 任一服务进程退出 → 杀掉其余 → 容器退出码 1 → docker 按 restart 策略整体拉起。 |
|||
|
|||
set -e |
|||
cd /app |
|||
A11=/opt/voitrans/a11 |
|||
|
|||
echo "[entrypoint] starting voitrans services..." |
|||
|
|||
# gateway 是 HTTP 入口 + 服务发现枢纽,先起;home/api/timer 随后;mcp 依赖 gateway 发现,最后起。 |
|||
${A11}/gateway.a11 -conf ./confs/gateway.yaml & PID_GW=$! |
|||
${A11}/home.a11 -conf ./confs/home.yaml & PID_HOME=$! |
|||
${A11}/api.a11 -conf ./confs/api.yaml & PID_API=$! |
|||
${A11}/timer.a11 -conf ./confs/timer.yaml & PID_TIMER=$! |
|||
|
|||
# mcp 依赖 gateway 的服务发现,稍等再起 |
|||
sleep 3 |
|||
${A11}/mcp.a11 -conf ./confs/mcp.yaml & PID_MCP=$! |
|||
|
|||
echo "[entrypoint] gateway=$PID_GW home=$PID_HOME api=$PID_API timer=$PID_TIMER mcp=$PID_MCP" |
|||
|
|||
ALL_PIDS="$PID_GW $PID_HOME $PID_API $PID_TIMER $PID_MCP" |
|||
|
|||
shutdown() { |
|||
echo "[entrypoint] received shutdown signal, stopping services..." |
|||
kill -TERM $ALL_PIDS 2>/dev/null || true |
|||
wait |
|||
echo "[entrypoint] all services stopped, exiting" |
|||
exit 0 |
|||
} |
|||
trap shutdown TERM INT |
|||
|
|||
# 守护:任一进程消失即整体退出(交给 docker restart) |
|||
while :; do |
|||
for pid in $ALL_PIDS; do |
|||
if ! kill -0 "$pid" 2>/dev/null; then |
|||
echo "[entrypoint] FATAL: pid $pid exited, shutting down container" |
|||
kill -TERM $ALL_PIDS 2>/dev/null || true |
|||
wait |
|||
exit 1 |
|||
fi |
|||
done |
|||
sleep 2 |
|||
done |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,6 +1,8 @@ |
|||
# 真实环境变量(含密钥 / DSN / SSH),勿提交;仓库只保留 *.example 模板 |
|||
*/env/*.env |
|||
*/.env |
|||
*/.env.infra |
|||
|
|||
# 运行时日志 |
|||
console/log/ |
|||
voitrans/log/ |
|||
|
|||
@ -0,0 +1,66 @@ |
|||
# voitrans 业务服务部署 |
|||
|
|||
把 **gateway + home + api + mcp + timer** 五个集群(RPCX/ETCD)服务打成**一个镜像** `voitrans-a11`, |
|||
推送到私有仓库 `docker-registry.ideapsound.com`,再到目标机拉取重启。容器内用 shell(entrypoint) 管理 5 个进程, |
|||
任一进程退出即整体重启。 |
|||
|
|||
## 文件 |
|||
|
|||
| 文件 | 说明 | |
|||
|---|---| |
|||
| `../../apps/services/Dockerfile.voitrans` | 镜像构建(编译 5 个二进制 + entrypoint),构建上下文 = `apps/services/` | |
|||
| `../../apps/services/entrypoint.voitrans.sh` | 容器入口,拉起并守护 5 个服务进程 | |
|||
| `docker-compose.yml` | 运行镜像,把本目录挂为容器 `/app`,`.env` 注入容器 | |
|||
| `confs/*.yaml` | 5 个服务的运行配置(含真实密钥,随仓库提交);基础设施端点用 `${VAR}` 由 `.env` 注入 | |
|||
| `env/<env>.env.example` | 运行时配置模板(真实 `.env` 被 gitignore,需复制后填写) | |
|||
| `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 更新开发环境(一条命令) | |
|||
| `prod-build.sh` | **生产①**:构建带版本号镜像并推送(`:版本` + `:latest`),不部署 | |
|||
| `prod-deploy.sh` | **生产②**:选区域 + 版本 tag 部署,支持回滚 | |
|||
|
|||
## 用法 |
|||
|
|||
首次:复制并填写 env(真实文件已 gitignore) |
|||
|
|||
```bash |
|||
cd deploy/voitrans |
|||
cp env/dev.env.example env/dev.env |
|||
cp env/shanghai.env.example env/shanghai.env # 生产区域同理 japan / singapore |
|||
# 编辑 env/*.env,填 DSN / Redis / ETCD / NATS / 统计应用名等(CHANGE_ME 处) |
|||
``` |
|||
|
|||
开发环境(一键发布) |
|||
|
|||
```bash |
|||
./dev-deploy.sh # 构建并发布 dev-latest |
|||
./dev-deploy.sh dev_meet-latest # 指定 tag |
|||
``` |
|||
|
|||
生产环境(构建与部署分离) |
|||
|
|||
```bash |
|||
./prod-build.sh 0.1.1 # 构建并推送 voitrans-a11:0.1.1 (+latest) |
|||
./prod-deploy.sh shanghai 0.1.1 # 部署 0.1.1 到上海 |
|||
./prod-deploy.sh japan 0.1.1 # 部署到日本 |
|||
./prod-deploy.sh singapore 0.1.1 # 部署到新加坡 |
|||
./prod-deploy.sh shanghai rollback # 上海回滚到上一版本(与当前版本来回切换) |
|||
``` |
|||
|
|||
## 需要填写 / 提供的东西 |
|||
|
|||
- **部署目标与凭据**:写死在各脚本顶部。 |
|||
- `dev-deploy.sh`:`DEV_HOST`(开发服务器 IP)。 |
|||
- `prod-deploy.sh`:`region_profile()` 里各区域的 `DEPLOY_HOST`。 |
|||
- 仓库账号 `admin` / 密码已写死;`DEPLOY_KEY` 默认 `~/.ssh/id_rsa`。 |
|||
- ⚠️ 本目录脚本纳入 git,**公开仓库前务必清空** `*_HOST` 与 `REGISTRY_PASS`。 |
|||
- **基础设施端点**:填 `env/<env>.env` 的 `ETCD_SERVERS / DB_ADMIN_DSN / DB_DEVICE_DSN / REDIS_* / NATS_URL`, |
|||
以及统计上报 `ANALYZE_APP_NAME`(须与 console 注册表应用名一致)/ `ANALYZE_REGION`、集群隔离 `CLUSTER_TAG`。 |
|||
- **confs 引用的附属文件**(按需放入本目录,会随 `confs/`/工作目录挂入容器 `/app`): |
|||
- `home.yaml`:`./smart-bluetooth-447104-2b269474bd72.json`(Google)、firebase/google 登录 json、 |
|||
`./apiclient_key.pem`(微信支付)、`confs/ip2region_v4.xdb` 与 `v6.xdb`(IP 库)、 |
|||
`wordfilter/*.txt`(敏感词,不需要则删掉 `home.yaml` 的 `wordfilter` 段)。 |
|||
- 缺这些文件会导致对应服务启动失败(容器反复重启),用不到的集成请删掉对应配置段。 |
|||
|
|||
## 端口(容器内,对外经反代/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`)网络内,用容器名连接。 |
|||
@ -0,0 +1,51 @@ |
|||
tag: "${CLUSTER_TAG:-voitrans-a11}" #服务集群标签,按 .env 的 CLUSTER_TAG 隔离不同部署 |
|||
type: "api" |
|||
version: 1.0 |
|||
exte: #RPCX 相关配置 |
|||
Port: 7005 |
|||
Addr: 127.0.0.1:7005 |
|||
ETCDServers: ["${ETCD_SERVERS:-etcd:2379}"] |
|||
UpdateInterval: 5 |
|||
|
|||
sys: |
|||
log: |
|||
FileName: "./log/api.log" |
|||
Loglevel: 5 |
|||
MaxAgeTime: 7 |
|||
IsDebug: ${LOG_DEBUG:-false} |
|||
mysql: #业务库 |
|||
MySQLDsn: ${DB_ADMIN_DSN:-root:li13451234@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local} |
|||
postgres: #公共库 |
|||
Dsn: ${DB_DEVICE_DSN:-postgresql://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable} |
|||
ReadDsn: ${DB_DEVICE_READ_DSN:-} |
|||
MaxPoolSize: 5 |
|||
redis: |
|||
Addr: ["${REDIS_ADDR:-redis:6379}"] |
|||
Password: ${REDIS_PASSWORD:-li13451234} |
|||
DB: ${REDIS_DB:-1} |
|||
KeyPrefix: ${REDIS_KEY_PREFIX:-Voitrans} |
|||
cos: |
|||
SecretID: AKID9F8qNiqlTzIMFwQ9rQxNcgYCdsukNmzA |
|||
SecretKey: OYVGo8HEzWpvpfHgtfHfqpcDbnlY7Tdc |
|||
AppId: "1253517901" |
|||
Region: ap-guangzhou |
|||
BucketName: "deepsound-1253517901" |
|||
BucketURL: "https://deepsound-1253517901.cos.ap-guangzhou.myqcloud.com" |
|||
translate: #Azure Translator,按需填写 |
|||
Key: "${AZURE_TRANSLATE_KEY:-}" |
|||
Region: "eastasia" |
|||
Endpoint: "https://api.cognitive.microsofttranslator.com" |
|||
email: |
|||
EmailType: 1 |
|||
FromEmail: liwei@yunqiinnovation.com |
|||
FromName: DeepSound |
|||
Password: eNdXWrjSQbdYtwbt |
|||
|
|||
modules: |
|||
api: |
|||
TokenKey: "${GATEWAY_TOKEN_KEY:-asd123adqeqwe}" |
|||
AdninAccount: ${API_ADMIN_ACCOUNT:-admin} |
|||
AdninPassword: "${API_ADMIN_PASSWORD:-123}" |
|||
ConsolePort: 8080 |
|||
SiteName: "${API_SITE_NAME:-Voitrans}" #后台站点名称(不同环境可区分) |
|||
CurrencyType: ${API_CURRENCY_TYPE:-0} #0=人民币(元) 1=美元(USD) |
|||
@ -0,0 +1,31 @@ |
|||
tag: "${CLUSTER_TAG:-voitrans-a11}" #服务集群标签:同标签且 ETCD 一致才能互相发现,按 .env 的 CLUSTER_TAG 隔离不同部署 |
|||
type: "gateway" #服务类型 |
|||
version: 1.0 |
|||
exte: #RPCX 相关配置(同一容器内 5 服务用 127.0.0.1 互通;ETCD 为独立容器,经 .env 注入容器名) |
|||
Port: 7001 |
|||
Addr: 127.0.0.1:7001 |
|||
ETCDServers: ["${ETCD_SERVERS:-etcd:2379}"] |
|||
UpdateInterval: 5 |
|||
|
|||
sys: |
|||
log: |
|||
FileName: "./log/gateway.log" |
|||
Loglevel: 5 |
|||
MaxAgeTime: 7 |
|||
IsDebug: ${LOG_DEBUG:-false} |
|||
# gateway 不接存储 |
|||
|
|||
modules: |
|||
gateway: |
|||
ListenPort: 7100 |
|||
TokenKey: "${GATEWAY_TOKEN_KEY:-asd123adqeqwe}" |
|||
WhiteList: |
|||
- user_sgin |
|||
- api_getuser |
|||
- user_getappconfig |
|||
- user_getchannelapp |
|||
- user_verification |
|||
- public_info |
|||
- pay_wechatnotify |
|||
- pay_alipaynotify |
|||
Debug: ${LOG_DEBUG:-false} |
|||
@ -0,0 +1,206 @@ |
|||
tag: "${CLUSTER_TAG:-voitrans-a11}" #服务集群标签,按 .env 的 CLUSTER_TAG 隔离不同部署 |
|||
type: "home" |
|||
version: 1.0 |
|||
exte: #RPCX 相关配置 |
|||
Port: 7002 |
|||
Addr: 127.0.0.1:7002 |
|||
ETCDServers: ["${ETCD_SERVERS:-etcd:2379}"] |
|||
UpdateInterval: 5 |
|||
|
|||
sys: |
|||
log: |
|||
FileName: "./log/home.log" |
|||
Loglevel: 5 |
|||
MaxAgeTime: 7 |
|||
IsDebug: ${LOG_DEBUG:-false} |
|||
nats: #统计快照管道(推送到 console 统计消费端);连不上不阻断业务 |
|||
URL: "${NATS_URL:-nats://nats:4222}" |
|||
Name: "home" |
|||
MaxReconnects: -1 |
|||
ReconnectWait: 2 |
|||
# 敏感词过滤:需把词库 txt 放入 deploy/voitrans/wordfilter/。无需要时整段删除即可。 |
|||
wordfilter: |
|||
WorldFile: |
|||
- ./wordfilter/暴恐词库.txt |
|||
- ./wordfilter/补充词库.txt |
|||
- ./wordfilter/反动词库.txt |
|||
- ./wordfilter/非法网址.txt |
|||
- ./wordfilter/广告类型.txt |
|||
- ./wordfilter/老版本词库.txt |
|||
- ./wordfilter/零时-Tencent.txt |
|||
- ./wordfilter/民生词库.txt |
|||
- ./wordfilter/色情词库.txt |
|||
- ./wordfilter/色情类型.txt |
|||
- ./wordfilter/涉枪涉爆.txt |
|||
- ./wordfilter/贪腐词库.txt |
|||
- ./wordfilter/网易前端过滤敏感词库.txt |
|||
- ./wordfilter/新思想启蒙.txt |
|||
- ./wordfilter/政治类型.txt |
|||
- ./wordfilter/COVID-19词库.txt |
|||
- ./wordfilter/GFW补充词库.txt |
|||
mysql: #业务库(用户/设备/统计/任务等 per-app 业务数据) |
|||
MySQLDsn: ${DB_ADMIN_DSN:-root:li13451234@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local} |
|||
postgres: #公共库(厂家/产品/设备码/公码等跨 app 共享数据) |
|||
Dsn: ${DB_DEVICE_DSN:-postgresql://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable} |
|||
ReadDsn: ${DB_DEVICE_READ_DSN:-} #只读副本,填了就开读写分离;不填=单库 |
|||
MaxPoolSize: 5 |
|||
redis: |
|||
Addr: ["${REDIS_ADDR:-redis:6379}"] |
|||
Password: ${REDIS_PASSWORD:-li13451234} |
|||
DB: ${REDIS_DB:-1} |
|||
KeyPrefix: ${REDIS_KEY_PREFIX:-Voitrans} #key 应用分组前缀 |
|||
sms: #短信服务 |
|||
AppId: "1400971698" |
|||
SecretId: "AKIDmiaJsJr6C8sPkt22xgFdswnTyHNG1c3J" |
|||
SecretKey: "BByYxDH9oIafXCmALJ5f4GGQWcaMwPta" |
|||
SignName: "深圳市创想云启" |
|||
Template1: "2397800" |
|||
Template2: "2385564" |
|||
google: #需放置 ./smart-bluetooth-447104-2b269474bd72.json |
|||
JsonPath: ./smart-bluetooth-447104-2b269474bd72.json |
|||
deepseek: |
|||
Appkey: sk-3adfd188a3134e718bbf704f525aff17 |
|||
email: |
|||
EmailType: 1 |
|||
FromEmail: liwei@yunqiinnovation.com |
|||
FromName: DeepSound |
|||
Password: eNdXWrjSQbdYtwbt |
|||
firebase_auth: #需放置对应 json |
|||
ApiKeyFile: ./deepsound-e0e3d-firebase-adminsdk-fbsvc-269043a0f8.json |
|||
google_auth: #需放置对应 json |
|||
ApiKeyFile: ./deepsound-e0e3d-firebase-adminsdk-fbsvc-269043a0f8.json |
|||
facebook_auth: |
|||
AppID: "3829022037361855" |
|||
AppSecret: b7c9b0e5d2eafc9376152a5bc9cccc0a |
|||
wechat_auth: |
|||
AppID: wx4b5ec8957ad5e425 |
|||
AppSecret: 9ceb39df312b3ba95a229b2aa0d15496 |
|||
musicobj: |
|||
ApiBaseUrl: https://hw.music.voitrans.net |
|||
cos: |
|||
SecretID: AKID9F8qNiqlTzIMFwQ9rQxNcgYCdsukNmzA |
|||
SecretKey: OYVGo8HEzWpvpfHgtfHfqpcDbnlY7Tdc |
|||
AppId: "1253517901" |
|||
Region: ap-guangzhou |
|||
BucketName: "deepsound-1253517901" |
|||
BucketURL: "https://deepsound-1253517901.cos.ap-guangzhou.myqcloud.com" |
|||
wechatpay: #微信支付,需放置 ./apiclient_key.pem |
|||
AppId: wx4b5ec8957ad5e425 |
|||
MchId: "1719983673" |
|||
ApiV3Key: "rV7cE1xZ9bN3mK5jH8gT0wQ2sF4dG6yU" |
|||
PrivateKeyPath: ./apiclient_key.pem |
|||
CertSerialNo: "2D27CEB52B98DDEB1CC65E4D96A041E7408F58D0" |
|||
alipay: #支付宝支付 |
|||
AppID: "2021005135671159" |
|||
PrivateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCCNjHeCrB24HHGBoU3Vj/TxTxveW4pU4fktVSd5ZpACsU1vYO9VsBIiIDSOvIOTlsiXTS09940qc/6sgggyPard423Jxb/v+W9D0ZgKa/iMwdTJ2aTks1CCXyanIl3DSkyJrPfFlm2N/ZbPBvgup20QISQtnde1MRtPCMMyodDvvjkVzRYFfitzNS5fM85Ym7uKpfao32tYem0LKnMe8HUwp1AY41pzYDjzvSzWfYzOuLtvO/GnP3m8EAmGeY9WLkCU5BGNS8UJz6WcCZUqgkuAYoYjt0+qcPGIctU0RYs9KVcK1UGJs22GK6oxNHalbT2TluI8exaSDvNObMQmGJtAgMBAAECggEAAxt//SiMJYodrPgi3bs1MryWQKLJOjtAFvks18fO8pBPPIUVS8NyO0jmqh22ARllm2BWDauMpf0XdYgiIYYqPNVRkHliVyAhP23J5CmahO9q0VUzYXozY8wOKTJrJWgu/39k84H5ChwzyOGaWLabZN3VTA7OsnipZ2H0BC4hJEC1R/mJIybul7WveP1RlnYd5L9EwGqj/k0UQKeBfEltH/FBiiWlnukruxW9TjYxkydOsLB7rRYdnBCnVbdy65gi/XX/fIxVmNiHauzfO3yksktdS54VPmQHnrl0Xc38xThbbxGmMxRXuS1bZQwuxkz1iJsh4K0eKvCKFl5EnVWsgQKBgQD9bzuD7Pp1aMXHxS4REYk85TsT0peGbNFfBfKrDb42oirccELiIhKM6TDl7JGHQA3kY+wN4I8VONJFlxfjZ9neXbhZanLqNr3Z8OEjejJ7cN2+WWERIwGboaAEf5qqKFuk+hUKdoXnhFMwTnFJfUQr0EV9+aUHVKEYBK/2IFHCtQKBgQCDh6JhvPAD0b/RlzTJUnRPB2+ZCM0LZQ3VCUYVsdLrOBh+IMrv74oSDgTJYglJaOINmQKVCcVyeLahII6skOcwJrxzVOnbQowNAITWRd0BzNfqB/Sx11s5Xpomi39Uk8yeRHJmmGDudQguvJrtcQEH6QkdGZz2CSNe41LoN0Rb2QKBgFu76YUFQFtDQ2PeIaCYAl8CcnHoZmSH+vk0Q/RakyFfbbhBAlSsFJ7gwYxYJw0XyG+FI6qaP/BkkugRmtn7e0ccEWB8QYA5tFqw8C1RS/iWkA0YeSQk0a4twKggMDGrThDXXMpewHVe1mXD3wnEx+o8BwtkU7HlljIFcFPcuvuJAoGAMKuxB8+9VPx5qZn1O4YaaeALiKp8DXpgavOajo6VyNqiHZIO5khSZCnx1X9EhnicJYH4NaQ+/wPyOJ9ujCa2ldfPpjHGs47lei41+YAyrubZAs3c2y4goJMEIb0+6dIjRnF3j8VVIQgHdhvujGxjgETYTX9b2KM7BnLCDBHfZskCgYEA9Mz4RMZbmyqsZ1gtkioYmyn1MSaaVu/fMJzVwLyHi0TicXpG67kLM7KRBZq5tdySQUhq7ZVkQ+jwHxyuO9iJsKD5ye1KTgyRhh1URMe6/YLRf5170805XIfSFn0jLmQ6rWwaftcWaDp8OjMR/wJDHe6vJcqttle7IuJ6w481AQ8= |
|||
PublicKey: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuLaTAzkBwcfy9Vg6Kj8NSNPQ5IeYyT+UKvOoPYiBw85SU7b8/gc1Lhgvo0DVfhM0MI67qf1GY69l3f6/f8mk6CcnhqC6iY0AcQnXqzQ4RBM/9s8/GCaqOohcv8LvLtbz+RlxIPu4dnTkrgEan7z+XZz7vUgHN7vQOcnPj/stfnxJeOANNLgCOYm/XDJZa8yV7lFux750d5+HE3m1jXXHAPTt0Pp+iHIzUZZkIEPaaz46tvoNi0cUf/+BJyq0DLIeWvTv2HpquVV3ZiiFYyjG3FnxWx+BFz/JCYfnPM31gQBt+EgwO3Oc9Kb5JetWrgmjgeoHwL6JIX3mCapZlJlxlwIDAQAB" |
|||
paypal: #沙盒;正式环境换 ClientID/Secret/BaseURL/WebhookID |
|||
ClientID: AUjApGHJm31MT101xA6koKAnZBZvo6ZxzRYAyXRXFQjlBYoj2i7Hg33clMIaIqyziFhmY0TX_EN_hgDL |
|||
Secret: EKlnsMTODfMFfOsnkfyifDRexw3dPk9Ldzv5b-a3Yu-Gq0cYFF7roQsrSaBv572kW9n_u25lHBDabemq |
|||
BaseURL: https://api-m.sandbox.paypal.com |
|||
WebhookID: 0PR35225Y36017238 |
|||
appleiap: #苹果内购 |
|||
AppStoreIssuerID: 96c4b3eb-5e5c-4719-8b07-db9cc4dc1f89 |
|||
AppStoreKeyID: VS538D3ZR5 |
|||
AppStorePrivateKeyPEM: | |
|||
-----BEGIN PRIVATE KEY----- |
|||
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgTadGz/swzKnpENyr |
|||
7JW6aH32ggaFCRKSakkoVPBc0PWgCgYIKoZIzj0DAQehRANCAASafAEhHQegdDNn |
|||
TE8bPW50cqrX+cFACKK5ZHoRbIYdp2BE3Ji8NSK7mE9avnLahGhyIfyTeSc7ZLyY |
|||
4WlcCJCm |
|||
-----END PRIVATE KEY----- |
|||
AppStoreBundleID: com.saitong.voitrans |
|||
UseSandbox: true |
|||
googleiap: |
|||
ServiceAccountJSON: | |
|||
{ |
|||
"type": "service_account", |
|||
"project_id": "deepsound-451512", |
|||
"private_key_id": "56adf297cf60cfa27cd31c7b35db8678164fd311", |
|||
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCteoHZrBT5F82D\nbbCzDf7jlrtPLag5hZUiA50Ex4HjSQrRMi1z5a/Px6sP4/wMEj5K+huyvkRv1cGN\nFbSSMBo2HMnoQIVKHy0HTZ3g/oJxlHMeJDLz300pAfTAJx8qvauV5KD/Z0wWqrAE\nrdwiU6VZbFIoIb2XfIuB3U0v8FsaLX0+yUG+kuRWi+kKCOOPUNneS+Ppu6NGR+98\nyA94uVzBXkIdUieSpY4/6WznzeKR55UWcjsZ4EVqiZ1xoP0nduUces4Ph8kQkIEw\nX/albSUeX4yfHdKnEmQ+W1y9TfW5PSBKno452vkiI1ZivK6I2LrvCxINe8saCOko\ng8B5lTqzAgMBAAECgf8Om16Mg7frCIfTpaGtACtZqR6ONtgsOKFydKKLGc+dGsJA\nMGsxj+yTIHW4p+t0pvgmArBSXJaDDQrNSCMInefP+/Hn1U4Ofeba73U/Q70NYXKI\n3kqIdyh52bBQRMIiLIDwyME3atCLidO6H1tNT7245kmS3pnC0kgJUe5KU/KtJQPl\nHhO59v3HyBSNW5fBjj9KAoJlgLK7IAtDynupIkFik4Gt2nqI9F9wbmgwAtQx1HhT\n4mbvDEv+k/GmvZLd60lt/kM625HNnuYKm4NYzdTpk1ryERgvCpAwQj84CvWncqom\nb2RBY7SFbE//Twe8Cr7gIKmTWivyDMdp/JiPeIECgYEA2W/fcxTD143TfxEb5402\ne85XYpB8bWew4R7W888HE0BKQbGYQFi4Z3np5ogw9T3VqYd9ct5AeRq2u6ZM82OJ\n0sHxDybTzTDAPx7/ii6cz43zut2bXoEI2YyfguW+FsnVtH6ZM94DlnaUuvClbpHE\n7yWqdicT73yxQ3+T3HNfTiECgYEAzD7SYv3D4vGvgwon9rJ7+kw+KBHYeloXFu5q\n3Y6TnRLpvgxmNxEm/SYD/bRhWbsXONyPvoQiVl0v/BpKxIf4cS5OD2SikmAxfYDP\nSA7CVkrsmWwAqJiMi+yXAlq3+kmTJAjbSj1zkJjhE9BSTfeKIoo5OyLu0XsKbK+t\nyw1WJlMCgYEA2M9Ye6PE8/aTYSxqKqUAIstJDxsLNvwhvxfFNi7E20lLwd2X9vGA\nTo2d0SUTzDXwCq7zQ8qhdyBP/SSBrl8eWmzQNU99+igWSdpa7Co2ai8dTMMT3VPE\nw0gY9aintoli8isaEEHcSffLreuuyWXIfL2OD2o/0pj/GFLp2POhoIECgYEAy8P1\nE0uG7VgfuQUd37IrnAgKjuFW7qIInRkZRpbiz/c5eSBXraKQGkid/LPD18ien1z0\nj6a1tyHbKJF2fGIGy6vdtt+4jX4ogcRLh+Epovu21k14zrr6pPJRqcil+a8DfBTV\n6lgqflz3WFQieKR4jIMIG+Z7CkTNnqcaJRTWGP8CgYB5HpozVNaJDuokXobxx4dG\nTo2wUvC67EW8TtpI/GD6xS6h5n9ZAXr4e08pSnG5M3784N4mDFy83Z8nEpSPISC7\nCy+QEREzTRx6LBRMe79eqo79Y8CuHaZKnubB8xgc87zWEjufXxfe5gZX8hccTFUv\nhnIdLDXB7hgvboB/GzjJ8Q==\n-----END PRIVATE KEY-----\n", |
|||
"client_email": "deeapsound@deepsound-451512.iam.gserviceaccount.com", |
|||
"client_id": "104070580817776670469", |
|||
"auth_uri": "https://accounts.google.com/o/oauth2/auth", |
|||
"token_uri": "https://oauth2.googleapis.com/token", |
|||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", |
|||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/deeapsound%40deepsound-451512.iam.gserviceaccount.com", |
|||
"universe_domain": "googleapis.com" |
|||
} |
|||
doubao: #allhelp 模块 AI 总结用 |
|||
Apikey: "55242c1c-204e-4e2a-ae75-faa846180682" |
|||
Model: "doubao-1-5-pro-32k-250115" |
|||
ipinfo: #需放置 confs/ip2region_v4.xdb 与 v6.xdb |
|||
V4XdbPath: "./confs/ip2region_v4.xdb" |
|||
V6XdbPath: "./confs/ip2region_v6.xdb" |
|||
CachePolicy: "vindex" |
|||
Searchers: 10 |
|||
ali_filetrans: |
|||
ApiKey: "sk-4598b2ed68e0484691b90da8c234b0f8" |
|||
|
|||
modules: |
|||
analyze: #平台统计模块(推送快照到 console) |
|||
AppName: "${ANALYZE_APP_NAME:-voitrans-a11}" #须与 console 注册表中的应用名完全一致,否则不落库 |
|||
Region: ${ANALYZE_REGION:-1} #本部署所属区域,取值见 pb.Region |
|||
SyncInterval: 600 |
|||
Subject: "stats.event" #须与 console 端 Stat.Subject 一致 |
|||
Timezone: "Asia/Shanghai" |
|||
user: |
|||
Debug: ${LOG_DEBUG:-false} |
|||
AvatarUrl: "https://api.ideapsound.com/avatar/" |
|||
TokenKey: "${GATEWAY_TOKEN_KEY:-asd123adqeqwe}" |
|||
CBCKey: gDSiGlKJIGuu84ZNe+j4FQ== |
|||
allhelp: |
|||
max_summary_process: 5 |
|||
echomeet: #会议记录:四套服务商各自的转写/翻译/总结配置 |
|||
Debug: ${LOG_DEBUG:-false} |
|||
DefaultService: "Auto" |
|||
ByteDance: |
|||
Callback: "" |
|||
Transcribe: |
|||
AppID: "8595121680" |
|||
Token: "ERAUYbJQBOv6M0ooFnUwo-tTWs08HWZn" |
|||
Translate: |
|||
AccessKey: "AKLTZjJmODdkODQwN2RjNDcyMWI4NWM2ZWRmNDkxY2RiYzI" |
|||
SecretKey: "T0Roa09EZGlNalZpTjJObE5HVmlZMkV3WVRkbVpUTTBORGMzTW1FNFlqYw==" |
|||
Region: "cn-beijing" |
|||
Summary: |
|||
Apikey: "55242c1c-204e-4e2a-ae75-faa846180682" |
|||
Model: "doubao-1-5-pro-32k-250115" |
|||
Ali: |
|||
Callback: "" |
|||
Transcribe: |
|||
ApiKey: "sk-4598b2ed68e0484691b90da8c234b0f8" |
|||
Translate: |
|||
AccessKeyId: "LTAI5t9dLKEg8zHLbfZJcJZn" |
|||
AccessKeySecret: "8NicfnXpHSeQa6ARvCNzXsNqZe8wr1" |
|||
Region: "cn-hangzhou" |
|||
Summary: |
|||
BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1" |
|||
Token: "sk-4598b2ed68e0484691b90da8c234b0f8" |
|||
Model: "qwen-plus" |
|||
Google: #需放置 ./smart-bluetooth-447104-2b269474bd72.json |
|||
Transcribe: |
|||
JsonPath: ./smart-bluetooth-447104-2b269474bd72.json |
|||
Translate: |
|||
JsonPath: ./smart-bluetooth-447104-2b269474bd72.json |
|||
Summary: |
|||
Apikey: "AQ.Ab8RN6Iek4x3Pd5NZcuLrXzrapFor_gAoO9W5H-Fg2p8F2M64g" |
|||
Model: "gemini-2.5-flash" |
|||
Microsoft: #未配置则该服务商不可用 |
|||
Transcribe: |
|||
Key: "" |
|||
Region: "eastasia" |
|||
Translate: |
|||
Key: "" |
|||
Region: "eastasia" |
|||
Endpoint: "https://api.cognitive.microsofttranslator.com" |
|||
Summary: |
|||
BaseURL: "" |
|||
Token: "" |
|||
Model: "" |
|||
pay: |
|||
Debug: ${LOG_DEBUG:-false} |
|||
WxAppId: "wx5bbb7be9324f3e2d" |
|||
WxAppSecret: "4cfde33bc22b6cc6b1f8b225dd97d255" |
|||
WxMchId: "1652202222" |
|||
WxPayPayKey: "rV7cE1xZ9bN3mK5jH8gT0wQ2sF4dG6yU" |
|||
@ -0,0 +1,59 @@ |
|||
tag: "${CLUSTER_TAG:-voitrans-a11}" #服务集群标签,按 .env 的 CLUSTER_TAG 隔离不同部署 |
|||
type: "mcp" |
|||
version: 1.0 |
|||
exte: #RPCX 相关配置 |
|||
Port: 7004 |
|||
Addr: 127.0.0.1:7004 |
|||
ETCDServers: ["${ETCD_SERVERS:-etcd:2379}"] |
|||
UpdateInterval: 5 |
|||
|
|||
sys: |
|||
log: |
|||
FileName: "./log/mcp.log" |
|||
Loglevel: 5 |
|||
MaxAgeTime: 7 |
|||
IsDebug: ${LOG_DEBUG:-false} |
|||
mysql: #mcp 仅用业务库读写 allhelp_task / user 表 |
|||
MySQLDsn: ${DB_ADMIN_DSN:-root:li13451234@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local} |
|||
amap: |
|||
Appkey: c653e53f94ee88a43cc979a08deae883 |
|||
juhe: |
|||
SimpleWeather_ApiKey: efd8f9f391d73d7337344d26ee93d173 |
|||
Finance_ApiKey: 50a101c9c5ff7c967598cea4bcc8ebbd |
|||
Toutiao_ApiKey: 035a1d052001f6f87358a159e7c44575 |
|||
spotify: |
|||
ClientID: "c111d7aa47ef4fb8a60c705f117200b0" |
|||
ClientSecret: "27d9a850f1fe4d749103eec4d5a2a106" |
|||
RedirectURI: "http://localhost:8080/callback" |
|||
musicobj: |
|||
ApiBaseUrl: https://hw.music.ideapsound.com |
|||
bravesearch: |
|||
ApiKey: BSAyKXEKw6ftwPHD5HaTX4Ybr3Bfd6b |
|||
|
|||
modules: |
|||
mcp: |
|||
Port: 7300 |
|||
Addr: "${MCP_ADDR:-http://localhost:7300}" |
|||
TLSCertFile: "" |
|||
TLSKeyFile: "" |
|||
Groups: |
|||
GLOBAL: |
|||
- hefeng_weather |
|||
- music_search |
|||
- music_play |
|||
- add_user_task |
|||
- get_user_tasks |
|||
- cancel_user_task |
|||
OVERSEAS: |
|||
- google_map_nearbysearch |
|||
- google_map_geocode |
|||
- google_map_regeocode |
|||
- google_map_route_navigation |
|||
- tavily_web_search |
|||
CHINA: |
|||
- gaode_map_around_search |
|||
- gaode_map_geocode |
|||
- gaode_map_regeocode |
|||
- gaode_map_route_navigation |
|||
- gaode_map_search_place |
|||
- bocha_web_search |
|||
@ -0,0 +1,27 @@ |
|||
tag: "${CLUSTER_TAG:-voitrans-a11}" #服务集群标签,须与其余服务一致才能同集群 |
|||
type: "timer" |
|||
version: 1.0 |
|||
exte: #RPCX 相关配置(timer 也接 ETCD 参与集群) |
|||
Port: 7003 |
|||
Addr: 127.0.0.1:7003 |
|||
ETCDServers: ["${ETCD_SERVERS:-etcd:2379}"] |
|||
UpdateInterval: 5 |
|||
|
|||
sys: |
|||
log: |
|||
FileName: "./log/timer.log" |
|||
Loglevel: 5 |
|||
MaxAgeTime: 7 |
|||
IsDebug: ${LOG_DEBUG:-false} |
|||
cron: {} #定时驱动(无额外配置项) |
|||
redis: #timer 仅用 Redis |
|||
Addr: ["${REDIS_ADDR:-redis:6379}"] |
|||
Password: ${REDIS_PASSWORD:-li13451234} |
|||
DB: ${REDIS_DB:-1} |
|||
KeyPrefix: ${REDIS_KEY_PREFIX:-Voitrans} |
|||
|
|||
modules: |
|||
timer: |
|||
Doubao_Apikey: "${TIMER_DOUBAO_APIKEY:-}" |
|||
Doubao_BotId: "${TIMER_DOUBAO_BOTID:-}" |
|||
Doubao_Role: "${TIMER_DOUBAO_ROLE:-}" |
|||
@ -0,0 +1,79 @@ |
|||
#!/usr/bin/env bash |
|||
# voitrans 开发环境:一键「构建开发镜像 → 推送 → 更新开发环境」。 |
|||
# |
|||
# 业务五服务(gateway/home/api/mcp/timer)打成单镜像 voitrans-a11,推到私有仓库后远端拉取重启。 |
|||
# |
|||
# 用法(在 deploy/voitrans/ 下): |
|||
# ./dev-deploy.sh # 构建并发布 voitrans-a11:dev-latest |
|||
# |
|||
# 开发版镜像始终使用固定 tag dev-latest(不接受参数覆盖),每次构建覆盖同一 tag。 |
|||
set -euo pipefail |
|||
|
|||
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/voitrans |
|||
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根 |
|||
BUILD_CONTEXT="$REPO_ROOT/apps/services" # Go 构建上下文 |
|||
DOCKERFILE="$BUILD_CONTEXT/Dockerfile.voitrans" |
|||
|
|||
blue() { printf "\033[34m%s\033[0m\n" "$*"; } |
|||
green() { printf "\033[32m%s\033[0m\n" "$*"; } |
|||
red() { printf "\033[31m%s\033[0m\n" "$*"; } |
|||
|
|||
# ╔══════════════════════════════════════════════════════════════════════╗ |
|||
# ║ 开发部署目标 / 私有仓库凭据 —— 写死在此(本文件纳入 git,私有仓库内可接受) ║ |
|||
# ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║ |
|||
# ╚══════════════════════════════════════════════════════════════════════╝ |
|||
IMAGE_NAME="voitrans-a11" # 业务单镜像名 |
|||
REGISTRY="docker-registry.ideapsound.com" # 私有镜像仓库 |
|||
REGISTRY_USER="admin" # 仓库账号 |
|||
REGISTRY_PASS="li13451234" # 仓库密码 |
|||
|
|||
DEV_HOST="124.222.232.218" # ← 开发服务器 IP(必填) |
|||
DEV_PORT=22 |
|||
DEV_USER=root |
|||
DEV_KEY=/Users/liwei/Documents/key/earphone.pem # 本机访问开发服务器的 SSH 私钥 |
|||
DEV_DIR="/home/work/voitrans-a11" # 远端部署目录 |
|||
|
|||
PLATFORM="${PLATFORM:-linux/amd64}" |
|||
TAG="dev-latest" # 开发版固定 tag,每次构建覆盖同一 dev-latest |
|||
ENV_FILE="$SVC_DIR/env/dev.env" |
|||
FULL_IMAGE="$REGISTRY/$IMAGE_NAME:$TAG" |
|||
|
|||
[ -f "$DOCKERFILE" ] || { red "✗ 缺少 $DOCKERFILE"; exit 1; } |
|||
[ -f "$ENV_FILE" ] || { red "✗ 缺少 env/dev.env(先 cp env/dev.env.example env/dev.env 并填写)"; exit 1; } |
|||
: "${DEV_HOST:?请在 dev-deploy.sh 顶部填 DEV_HOST(开发服务器 IP)}" |
|||
|
|||
# 生成 compose/容器用 .env = dev.env(去掉 TAG/REGISTRY) + 注入 REGISTRY + 本次 TAG |
|||
gen_env() { |
|||
{ grep -vE '^(REGISTRY|TAG)=' "$ENV_FILE" || true; } > "$SVC_DIR/.env" |
|||
{ |
|||
printf 'REGISTRY=%s\n' "$REGISTRY" |
|||
printf 'TAG=%s\n' "$TAG" |
|||
} >> "$SVC_DIR/.env" |
|||
} |
|||
|
|||
# ── 远程 SSH 基础 ── |
|||
_ssh() { ssh -p "$DEV_PORT" -i "${DEV_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$DEV_USER@$DEV_HOST" "$@"; } |
|||
_scp() { scp -P "$DEV_PORT" -i "${DEV_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$@"; } |
|||
_dest() { echo "$DEV_USER@$DEV_HOST:$1"; } |
|||
|
|||
# 1) 构建并推送镜像 |
|||
blue ">>> 构建镜像 $FULL_IMAGE (context=$BUILD_CONTEXT)" |
|||
echo "$REGISTRY_PASS" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin |
|||
docker build --network=host --platform "$PLATFORM" -f "$DOCKERFILE" -t "$FULL_IMAGE" "$BUILD_CONTEXT" |
|||
docker push "$FULL_IMAGE" |
|||
|
|||
# 2) 同步配置并远端拉起 |
|||
: "${DEV_HOST:?请在 dev-deploy.sh 顶部填 DEV_HOST}" |
|||
gen_env |
|||
blue ">>> 同步配置到 $DEV_USER@$DEV_HOST:$DEV_DIR" |
|||
_ssh "mkdir -p '$DEV_DIR/confs' '$DEV_DIR/log'" |
|||
_scp "$SVC_DIR/docker-compose.yml" "$(_dest "$DEV_DIR/docker-compose.yml")" |
|||
_scp "$SVC_DIR/.env" "$(_dest "$DEV_DIR/.env")" |
|||
_scp -r "$SVC_DIR/confs/." "$(_dest "$DEV_DIR/confs/")" |
|||
|
|||
blue ">>> 远端拉取并重启" |
|||
_ssh "echo '$REGISTRY_PASS' | docker login '$REGISTRY' -u '$REGISTRY_USER' --password-stdin >/dev/null" |
|||
_ssh "docker network inspect 1panel-network >/dev/null 2>&1 || docker network create 1panel-network" |
|||
_ssh "cd '$DEV_DIR' && docker compose --env-file .env pull && docker compose --env-file .env up -d && docker image prune -f" |
|||
|
|||
green "✓ voitrans dev 已发布: $FULL_IMAGE → $DEV_HOST:$DEV_DIR" |
|||
@ -0,0 +1,57 @@ |
|||
# voitrans 业务单镜像部署 |
|||
# |
|||
# voitrans-a11: gateway + home + api + mcp + timer 五个集群服务(shell 脚本管理多进程)。 |
|||
# 镜像版本通过 TAG 控制;接入外部网络(默认 1panel-network),与 etcd/mysql/redis/nats 通过容器名互通。 |
|||
# 不直接 docker compose 调用,统一经 dev-deploy.sh / prod-deploy.sh(它们把 env/<env>.env 落为 .env)。 |
|||
|
|||
services: |
|||
voitrans-a11: |
|||
image: ${REGISTRY:-docker-registry.ideapsound.com}/voitrans-a11:${TAG:-latest} |
|||
container_name: voitrans-a11 |
|||
restart: unless-stopped |
|||
platform: ${PLATFORM:-linux/amd64} |
|||
|
|||
networks: |
|||
- appnet |
|||
|
|||
# 把整个部署目录挂为 /app(WORKDIR):内含 confs/ log/ 及各服务运行期资源(密钥/词库等,如有) |
|||
volumes: |
|||
- .:/app |
|||
|
|||
# 把 .env 整体注入容器,供各 yaml 里 ${VAR} 在启动时展开(compose 默认只做文件内插值,需显式 env_file) |
|||
env_file: |
|||
- .env |
|||
|
|||
environment: |
|||
TZ: Asia/Shanghai |
|||
|
|||
stop_grace_period: 30s |
|||
|
|||
# 端口(容器内部,对外经反代/NPM 暴露;如需直接发布改用下方 ports): |
|||
# 7001 gateway RPCX / 7100 gateway HTTP |
|||
# 7002 home RPCX / 7003 timer RPCX |
|||
# 7004 mcp RPCX / 7300 mcp HTTP |
|||
# 7005 api RPCX / 8080 api Console |
|||
expose: |
|||
- "7100" |
|||
- "7300" |
|||
- "8080" |
|||
# 如需宿主机直接访问,取消注释(左=宿主端口,右=容器端口): |
|||
# ports: |
|||
# - "7100:7100" # gateway HTTP |
|||
# - "8080:8080" # api Console |
|||
# - "7300:7300" # mcp HTTP |
|||
|
|||
# 健康检查:探测 gateway HTTP 端口 |
|||
healthcheck: |
|||
test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 7100 || exit 1"] |
|||
interval: 30s |
|||
timeout: 5s |
|||
retries: 3 |
|||
start_period: 30s |
|||
|
|||
# 外部共享网络:依赖的 etcd/mysql/redis/nats 在此网络(用容器名连接),目标机需预先存在 |
|||
networks: |
|||
appnet: |
|||
name: ${DOCKER_NETWORK:-1panel-network} |
|||
external: true |
|||
@ -0,0 +1,45 @@ |
|||
# voitrans · dev 开发环境(容器运行时配置) |
|||
# 仓库地址 REGISTRY 由 dev-deploy.sh 写死注入,这里不填。 |
|||
# 依赖的 etcd/mysql/redis/nats 在 DOCKER_NETWORK 网络内,用容器名连接。 |
|||
|
|||
# ── 镜像 / 容器 ── |
|||
TAG=dev-latest |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network |
|||
|
|||
# ── 集群 / 服务发现 ── |
|||
CLUSTER_TAG=voitrans-a11-dev # 集群标签:隔离 dev 与生产,避免互相发现 |
|||
ETCD_SERVERS=etcd:2379 # ETCD 容器名:端口 |
|||
|
|||
# ── 存储(容器名连接;密码特殊字符在 DSN 里需 URL 编码 & → %26)── |
|||
DB_ADMIN_DSN=root:li13451234@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local |
|||
DB_DEVICE_DSN=postgresql://postgres:postgres@postgres:5432/postgres?sslmode=disable |
|||
DB_DEVICE_READ_DSN= |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD=li13451234 |
|||
REDIS_DB=1 |
|||
REDIS_KEY_PREFIX=VoitransDev |
|||
|
|||
# ── NATS(统计快照推送到 console,须与 console 端一致)── |
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
# ── 统计上报(须与 console 注册表中的应用名/区域一致)── |
|||
ANALYZE_APP_NAME=voitrans-a11-dev |
|||
ANALYZE_REGION=1 |
|||
|
|||
# ── 业务杂项 ── |
|||
LOG_DEBUG=true |
|||
GATEWAY_TOKEN_KEY=dev-change-me |
|||
API_ADMIN_ACCOUNT=admin |
|||
API_ADMIN_PASSWORD=123 |
|||
API_SITE_NAME=Voitrans(开发) |
|||
API_CURRENCY_TYPE=0 |
|||
MCP_ADDR=http://localhost:7300 |
|||
|
|||
# ── 可选 ── |
|||
AZURE_TRANSLATE_KEY= |
|||
TIMER_DOUBAO_APIKEY= |
|||
TIMER_DOUBAO_BOTID= |
|||
TIMER_DOUBAO_ROLE= |
|||
|
|||
# 部署目标(DEV_HOST/USER/KEY/DIR)与仓库凭据已写死在 dev-deploy.sh 顶部,不在此文件填。 |
|||
@ -0,0 +1,35 @@ |
|||
# voitrans · 生产-日本 容器运行时配置(REGISTRY 由 prod-deploy.sh 注入) |
|||
|
|||
TAG=latest |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network |
|||
|
|||
CLUSTER_TAG=voitrans-a11-jp |
|||
ETCD_SERVERS=etcd:2379 |
|||
|
|||
# ── 存储(按日本生产实际填写)── |
|||
DB_ADMIN_DSN=root:CHANGE_ME@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local |
|||
DB_DEVICE_DSN=postgresql://postgres:CHANGE_ME@postgres:5432/postgres?sslmode=require |
|||
DB_DEVICE_READ_DSN= |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD=CHANGE_ME |
|||
REDIS_DB=1 |
|||
REDIS_KEY_PREFIX=VoitransJP |
|||
|
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
ANALYZE_APP_NAME=voitrans-a11-jp |
|||
ANALYZE_REGION=2 # 区域编码见 pb.Region(海外) |
|||
|
|||
LOG_DEBUG=false |
|||
GATEWAY_TOKEN_KEY=CHANGE_ME |
|||
API_ADMIN_ACCOUNT=admin |
|||
API_ADMIN_PASSWORD=CHANGE_ME |
|||
API_SITE_NAME=Voitrans |
|||
API_CURRENCY_TYPE=1 # 海外计费美元 |
|||
MCP_ADDR=http://localhost:7300 |
|||
|
|||
AZURE_TRANSLATE_KEY= |
|||
TIMER_DOUBAO_APIKEY= |
|||
TIMER_DOUBAO_BOTID= |
|||
TIMER_DOUBAO_ROLE= |
|||
@ -0,0 +1,36 @@ |
|||
# voitrans · 生产-上海 容器运行时配置(REGISTRY 由 prod-deploy.sh 注入) |
|||
# 依赖的 etcd/mysql/redis/nats 用容器名或区域内网地址连接,按实际填写。 |
|||
|
|||
TAG=latest # 实际部署版本由 prod-deploy.sh <区域> <tag> 覆盖 |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network |
|||
|
|||
CLUSTER_TAG=voitrans-a11-sh # 各区域独立集群标签,避免跨区域互相发现 |
|||
ETCD_SERVERS=etcd:2379 |
|||
|
|||
# ── 存储(按上海生产实际填写)── |
|||
DB_ADMIN_DSN=root:CHANGE_ME@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local |
|||
DB_DEVICE_DSN=postgresql://postgres:CHANGE_ME@postgres:5432/postgres?sslmode=require |
|||
DB_DEVICE_READ_DSN= |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD=CHANGE_ME |
|||
REDIS_DB=1 |
|||
REDIS_KEY_PREFIX=VoitransSH |
|||
|
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
ANALYZE_APP_NAME=voitrans-a11-sh # 须与 console 注册表应用名一致 |
|||
ANALYZE_REGION=1 # 区域编码见 pb.Region(国内) |
|||
|
|||
LOG_DEBUG=false |
|||
GATEWAY_TOKEN_KEY=CHANGE_ME |
|||
API_ADMIN_ACCOUNT=admin |
|||
API_ADMIN_PASSWORD=CHANGE_ME |
|||
API_SITE_NAME=Voitrans |
|||
API_CURRENCY_TYPE=0 |
|||
MCP_ADDR=http://localhost:7300 |
|||
|
|||
AZURE_TRANSLATE_KEY= |
|||
TIMER_DOUBAO_APIKEY= |
|||
TIMER_DOUBAO_BOTID= |
|||
TIMER_DOUBAO_ROLE= |
|||
@ -0,0 +1,35 @@ |
|||
# voitrans · 生产-新加坡 容器运行时配置(REGISTRY 由 prod-deploy.sh 注入) |
|||
|
|||
TAG=latest |
|||
PLATFORM=linux/amd64 |
|||
DOCKER_NETWORK=1panel-network |
|||
|
|||
CLUSTER_TAG=voitrans-a11-sg |
|||
ETCD_SERVERS=etcd:2379 |
|||
|
|||
# ── 存储(按新加坡生产实际填写)── |
|||
DB_ADMIN_DSN=root:CHANGE_ME@tcp(mysql:3306)/voitrans?charset=utf8mb4&parseTime=True&loc=Local |
|||
DB_DEVICE_DSN=postgresql://postgres:CHANGE_ME@postgres:5432/postgres?sslmode=require |
|||
DB_DEVICE_READ_DSN= |
|||
REDIS_ADDR=redis:6379 |
|||
REDIS_PASSWORD=CHANGE_ME |
|||
REDIS_DB=1 |
|||
REDIS_KEY_PREFIX=VoitransSG |
|||
|
|||
NATS_URL=nats://nats:4222 |
|||
|
|||
ANALYZE_APP_NAME=voitrans-a11-sg |
|||
ANALYZE_REGION=2 # 区域编码见 pb.Region(海外) |
|||
|
|||
LOG_DEBUG=false |
|||
GATEWAY_TOKEN_KEY=CHANGE_ME |
|||
API_ADMIN_ACCOUNT=admin |
|||
API_ADMIN_PASSWORD=CHANGE_ME |
|||
API_SITE_NAME=Voitrans |
|||
API_CURRENCY_TYPE=1 |
|||
MCP_ADDR=http://localhost:7300 |
|||
|
|||
AZURE_TRANSLATE_KEY= |
|||
TIMER_DOUBAO_APIKEY= |
|||
TIMER_DOUBAO_BOTID= |
|||
TIMER_DOUBAO_ROLE= |
|||
@ -0,0 +1,44 @@ |
|||
#!/usr/bin/env bash |
|||
# voitrans 生产环境:仅「构建带版本号的镜像并推送」,不部署。 |
|||
# |
|||
# 业务五服务(gateway/home/api/mcp/timer)打成单镜像 voitrans-a11。 |
|||
# 同时推送 :<version> 与 :latest 两个 tag。部署用 ./prod-deploy.sh <区域> <version>。 |
|||
# |
|||
# 用法(在 deploy/voitrans/ 下): |
|||
# ./prod-build.sh <version> # version 必须形如 X.Y.Z,如 0.1.1 |
|||
set -euo pipefail |
|||
|
|||
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|||
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" |
|||
BUILD_CONTEXT="$REPO_ROOT/apps/services" |
|||
DOCKERFILE="$BUILD_CONTEXT/Dockerfile.voitrans" |
|||
|
|||
blue() { printf "\033[34m%s\033[0m\n" "$*"; } |
|||
green() { printf "\033[32m%s\033[0m\n" "$*"; } |
|||
red() { printf "\033[31m%s\033[0m\n" "$*"; } |
|||
|
|||
# ── 私有仓库凭据(写死,公开仓库前清空 REGISTRY_PASS)── |
|||
IMAGE_NAME="voitrans-a11" |
|||
REGISTRY="docker-registry.ideapsound.com" |
|||
REGISTRY_USER="admin" |
|||
REGISTRY_PASS="li13451234" |
|||
PLATFORM="${PLATFORM:-linux/amd64}" |
|||
|
|||
VERSION="${1:-}" |
|||
[ -n "$VERSION" ] || { red "用法: ./prod-build.sh <version>(如 0.1.1)"; exit 1; } |
|||
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then |
|||
red "✗ version 必须形如 0.1.1,当前: $VERSION"; exit 1 |
|||
fi |
|||
[ -f "$DOCKERFILE" ] || { red "✗ 缺少 $DOCKERFILE"; exit 1; } |
|||
|
|||
IMG_VER="$REGISTRY/$IMAGE_NAME:$VERSION" |
|||
IMG_LATEST="$REGISTRY/$IMAGE_NAME:latest" |
|||
|
|||
blue ">>> 构建生产镜像 $IMG_VER (+latest) context=$BUILD_CONTEXT" |
|||
echo "$REGISTRY_PASS" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin |
|||
docker build --network=host --platform "$PLATFORM" -f "$DOCKERFILE" -t "$IMG_VER" -t "$IMG_LATEST" "$BUILD_CONTEXT" |
|||
docker push "$IMG_VER" |
|||
docker push "$IMG_LATEST" |
|||
|
|||
green "✓ 已推送 $IMG_VER 与 $IMG_LATEST" |
|||
echo " 部署:./prod-deploy.sh <shanghai|japan|singapore> $VERSION" |
|||
@ -0,0 +1,105 @@ |
|||
#!/usr/bin/env bash |
|||
# voitrans 生产环境:部署到指定区域(选择区域 + 镜像版本 tag),支持回滚。 |
|||
# |
|||
# 镜像由 ./prod-build.sh 预先构建推送;本脚本只做「同步配置 + 远端拉取 + 重启」。 |
|||
# 每个区域一套服务器配置(见下方 region_profile)。各区域共用同一镜像 voitrans-a11。 |
|||
# |
|||
# 用法(在 deploy/voitrans/ 下): |
|||
# ./prod-deploy.sh <区域> <tag> # 部署指定版本,如 ./prod-deploy.sh shanghai 0.1.1 |
|||
# ./prod-deploy.sh <区域> rollback # 回滚到上一次部署版本(与当前版本来回切换) |
|||
# 区域: shanghai | japan | singapore |
|||
set -euo pipefail |
|||
|
|||
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|||
|
|||
blue() { printf "\033[34m%s\033[0m\n" "$*"; } |
|||
green() { printf "\033[32m%s\033[0m\n" "$*"; } |
|||
red() { printf "\033[31m%s\033[0m\n" "$*"; } |
|||
|
|||
# ── 私有仓库凭据(写死;公开仓库前清空 REGISTRY_PASS)── |
|||
IMAGE_NAME="voitrans-a11" |
|||
REGISTRY="docker-registry.ideapsound.com" |
|||
REGISTRY_USER="admin" |
|||
REGISTRY_PASS="li13451234" |
|||
|
|||
# ╔══════════════════════════════════════════════════════════════════════╗ |
|||
# ║ 各区域部署目标 —— 写死在此(本文件纳入 git)。公开仓库前清空 HOST。 ║ |
|||
# ║ 新增区域:在此加一段 case + 在 env/ 下加 <区域>.env ║ |
|||
# ╚══════════════════════════════════════════════════════════════════════╝ |
|||
region_profile() { |
|||
DEPLOY_PORT=22 |
|||
DEPLOY_USER=root |
|||
DEPLOY_KEY=~/.ssh/id_rsa |
|||
DEPLOY_DIR="/opt/yunyan/voitrans" # 远端部署目录(各区域机器各一份) |
|||
case "$REGION" in |
|||
shanghai) DEPLOY_HOST="" ;; # ← 上海生产服务器 IP(必填) |
|||
japan) DEPLOY_HOST="" ;; # ← 日本生产服务器 IP(必填) |
|||
singapore) DEPLOY_HOST="" ;; # ← 新加坡生产服务器 IP(必填) |
|||
*) red "✗ 未知区域: $REGION(应为 shanghai|japan|singapore)"; exit 1 ;; |
|||
esac |
|||
} |
|||
|
|||
# ── 参数 ── |
|||
REGION="${1:-}"; ARG2="${2:-}" |
|||
[ -n "$REGION" ] && [ -n "$ARG2" ] || { red "用法: ./prod-deploy.sh <shanghai|japan|singapore> <tag|rollback>"; exit 1; } |
|||
if [ "$ARG2" = "rollback" ]; then MODE="rollback"; NEW_TAG=""; else MODE="deploy"; NEW_TAG="$ARG2"; fi |
|||
|
|||
region_profile |
|||
: "${DEPLOY_HOST:?请在 prod-deploy.sh 的 region_profile($REGION) 段填 DEPLOY_HOST}" |
|||
|
|||
ENV_FILE="$SVC_DIR/env/$REGION.env" |
|||
[ -f "$ENV_FILE" ] || { red "✗ 缺少 env/$REGION.env"; exit 1; } |
|||
[ -f "$SVC_DIR/docker-compose.yml" ] || { red "✗ 缺少 docker-compose.yml"; exit 1; } |
|||
|
|||
# 取 DOCKER_NETWORK(compose 外部网络名) |
|||
DOCKER_NETWORK="$({ grep -E '^DOCKER_NETWORK=' "$ENV_FILE" || true; } | head -1 | cut -d= -f2-)" |
|||
DOCKER_NETWORK="${DOCKER_NETWORK:-1panel-network}" |
|||
|
|||
# 生成 .env.infra = <区域>.env(去掉 TAG/PREV_TAG/REGISTRY) + 注入 REGISTRY。TAG/PREV_TAG 由远端管理。 |
|||
gen_env_infra() { |
|||
{ grep -vE '^(REGISTRY|TAG|PREV_TAG)=' "$ENV_FILE" || true; } > "$SVC_DIR/.env.infra" |
|||
printf 'REGISTRY=%s\n' "$REGISTRY" >> "$SVC_DIR/.env.infra" |
|||
} |
|||
|
|||
_ssh() { ssh -p "$DEPLOY_PORT" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST" "$@"; } |
|||
_scp() { scp -P "$DEPLOY_PORT" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$@"; } |
|||
_dest() { echo "$DEPLOY_USER@$DEPLOY_HOST:$1"; } |
|||
|
|||
gen_env_infra |
|||
blue ">>> [$REGION] 同步配置到 $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_DIR ($MODE${NEW_TAG:+ $NEW_TAG})" |
|||
_ssh "mkdir -p '$DEPLOY_DIR/confs' '$DEPLOY_DIR/log'" |
|||
_scp "$SVC_DIR/docker-compose.yml" "$(_dest "$DEPLOY_DIR/docker-compose.yml")" |
|||
_scp "$SVC_DIR/.env.infra" "$(_dest "$DEPLOY_DIR/.env.infra")" |
|||
_scp -r "$SVC_DIR/confs/." "$(_dest "$DEPLOY_DIR/confs/")" |
|||
|
|||
blue ">>> [$REGION] 远端计算版本、拉取并重启" |
|||
_ssh bash -s -- "$DEPLOY_DIR" "$MODE" "$NEW_TAG" "$REGISTRY/$IMAGE_NAME" "$REGISTRY" "$REGISTRY_USER" "$REGISTRY_PASS" "$DOCKER_NETWORK" <<'REMOTE' |
|||
set -e |
|||
DIR="$1"; MODE="$2"; NEW_TAG="$3"; IMG_PREFIX="$4"; REG="$5"; REG_USER="$6"; REG_PASS="$7"; NET="$8" |
|||
|
|||
getline() { [ -f "$DIR/.env" ] && { grep -E "^$1=" "$DIR/.env" 2>/dev/null | tail -1 | cut -d= -f2- || true; } | tr -d '\r'; } |
|||
CUR_TAG="$(getline TAG)" |
|||
CUR_PREV="$(getline PREV_TAG)" |
|||
|
|||
if [ "$MODE" = "rollback" ]; then |
|||
[ -n "$CUR_PREV" ] || { echo "没有 PREV_TAG,无法回滚"; exit 1; } |
|||
FINAL_TAG="$CUR_PREV"; FINAL_PREV="$CUR_TAG" # 与当前版本来回切换 |
|||
else |
|||
FINAL_TAG="$NEW_TAG" |
|||
if [ -n "$CUR_TAG" ] && [ "$CUR_TAG" != "$NEW_TAG" ]; then FINAL_PREV="$CUR_TAG"; else FINAL_PREV="$CUR_PREV"; fi |
|||
fi |
|||
|
|||
# 写新 .env = 同步来的基础设施配置 + PREV_TAG + TAG |
|||
cp "$DIR/.env.infra" "$DIR/.env" |
|||
[ -n "$FINAL_PREV" ] && echo "PREV_TAG=$FINAL_PREV" >> "$DIR/.env" |
|||
echo "TAG=$FINAL_TAG" >> "$DIR/.env" |
|||
|
|||
echo "$REG_PASS" | docker login "$REG" -u "$REG_USER" --password-stdin >/dev/null |
|||
docker network inspect "$NET" >/dev/null 2>&1 || docker network create "$NET" |
|||
docker pull "$IMG_PREFIX:$FINAL_TAG" |
|||
cd "$DIR" && docker compose --env-file .env up -d |
|||
docker image prune -f |
|||
echo "已部署版本 TAG=$FINAL_TAG (PREV_TAG=${FINAL_PREV:-无})" |
|||
REMOTE |
|||
|
|||
green "✓ voitrans [$REGION] $MODE 完成" |
|||
Loading…
Reference in new issue