8.9 KiB
Docker 部署
前置条件
| 依赖 | 说明 |
|---|---|
| Docker + Compose | 本机或服务器已安装 |
| Upstash Redis | .env 中 REDIS_URL=rediss://... |
| LINUX DO OAuth | Client ID/Secret + 公网回调地址 |
| 平台 LLM(主站直连) | LLM_API_KEY / LLM_BASE_URL / LLM_MODEL |
| 工具网关(用户自定义 API + 搜索) | TOOLS_BASE_URL / TOOLS_API_KEY;镜像见 huggingface/wechat-ai-tools |
快速启动
cd /path/to/WeChat-AI
# 配置环境变量(勿提交 .env)
# 生产务必修改:
# PUBLIC_BASE_URL=https://你的域名
# LINUXDO_REDIRECT_URI=https://你的域名/api/v1/auth/callback
# REDIS_URL / LLM_* / LINUXDO_CLIENT_*
# 用户自定义 API + 搜索:TOOLS_BASE_URL / TOOLS_API_KEY
# docker compose --profile tools up -d --build
# TOOLS_BASE_URL=http://wechat-ai-tools:7860
# 推荐:升版 + 打 OTA 包 + 构建(无需 Cookie)
pnpm docker:up
# 自定义镜像名:
pnpm docker:build -- -- docker build -t your-dockerhub-user/wechat-ai:latest .
# 包在 dist/release/<版本>/files.json
# 浏览器登录 /admin → 部署节点 →「上传通道包」→ 再点节点「更新」
docker compose logs -f wechat-ai
单独构建 tools 镜像
docker build -t wechat-ai-tools:latest -f huggingface/wechat-ai-tools/Dockerfile huggingface/wechat-ai-tools
docker run --rm -p 7860:7860 -e TOOLS_API_KEY=secret -e ALLOW_REQUEST_UPSTREAM=true wechat-ai-tools:latest
详见 docs/ai-gateway.md、huggingface/wechat-ai-tools/README.md。
版本与通道:
pnpm docker:build默认:升版 → 本地 pack → Docker。
通道发布只走网页:/admin→ 上传files.json(无 CLI Cookie)。
直接跑docker build不会升版、也不会打通道包。
| 地址 | 说明 |
|---|---|
/ |
功能介绍落地页(OG 分享图 /og.jpg) |
/app |
用户中心(LINUX DO 登录、加机器人) |
/admin |
管理后台(仪表盘 / Token) |
/health |
健康检查 |
仅用 Dockerfile
# 升版 + docker build -t wechat-ai .
pnpm docker:build -- --raw
# 或自定义:node scripts/docker-build.mjs -- docker build -t wechat-ai:0.2.1 .
docker run -d --name wechat-ai --restart unless-stopped \
--env-file .env \
-e WECHAT_AI_HOST=0.0.0.0 \
-e WECHAT_AI_PORT=8787 \
-p 8787:8787 \
wechat-ai
Bot token 与表情包均存 Redis(与 REDIS_URL 同库),容器重建不丢;无需本地数据卷。
生产环境检查清单
- LINUX DO 应用回调与
.env完全一致:
https://你的域名/api/v1/auth/callback PUBLIC_BASE_URL=https://你的域名(无尾斜杠)- HTTPS 时设置
COOKIE_SECURE=true - Redis 使用 Upstash
rediss://,服务器能访问外网 - Bot token 已写入 Redis(与
REDIS_URL同库),重建容器不会丢登录
常用命令
docker compose ps
docker compose logs -f
docker compose restart
docker compose down # 停服务(Bot token 在 Redis,不受影响)
docker compose down -v # 同 down(本服务无持久化 volume)
pnpm docker:up # 升版+本地 pack+compose up --build
pnpm docker:build -- -- docker build -t your-dockerhub-user/wechat-ai:latest .
# 然后 /admin → 上传通道包 (files.json) → 更新节点
pnpm docker:build -- --no-channel # 只升版构建,不 pack
OTA 增量更新(多节点日常热修)
业务源码小改可不必每台 docker build:本地 pack → 管理后台上传通道包 → 对落后节点点「更新」。
# 构建顺带打通道包
pnpm docker:build -- -- docker build -t your-dockerhub-user/wechat-ai:latest .
# 或仅 pack:
pnpm release:pack
# 浏览器 /admin → 部署节点 →「上传通道包」选 dist/release/<ver>/files.json
# →「更新全部落后」
| 项 | 说明 |
|---|---|
| 差量 | 按文件 sha256 比对,只下发变更文件 |
| 重启 | 节点 process.exit(0),依赖 restart: unless-stopped 拉起同一容器(可写层保留补丁) |
| 版本 | 心跳 version:.wa-version(OTA 写入)→ APP_VERSION → 根 package.json |
| 仍需镜像 | Node 基础镜像、系统包、Dockerfile、OTA_ALLOW_INSTALL=false 时的依赖大变 |
环境变量:OTA_ENABLED(默认 true)、OTA_ALLOW_INSTALL、APP_VERSION(无 OTA 戳时可选)、OTA_STAGING_DIR。
注意: OTA 只改文件、不改环境变量;版本靠 /app/.wa-version 上报,无需、也不应靠 APP_VERSION 跟版。
docker compose up --build / 重建容器会丢掉仅靠 OTA 写入的补丁;长期仍以镜像为 source of truth。
反代示例
生产推荐把域名挂在 Cloudflare(橙云代理 + Cache Rules),见 docs/cloudflare.md(Business 缓存规则、忽略 Cookie、Purge 清单)。
Caddy(无 CF 时)
your.domain.com {
reverse_proxy 127.0.0.1:8787
}
Nginx(无 CF 时,或 CF → Nginx → Node)
server {
listen 443 ssl;
server_name your.domain.com;
# ssl_certificate ...;
location / {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Cloudflare 还原真实 IP 时可用:
# proxy_set_header CF-Connecting-IP $http_cf_connecting_ip;
}
}
源站已输出 Cache-Control / Cloudflare-CDN-Cache-Control、HTML ETag、公开表情 /cdn/s/:id?v=。反代层不必再写 proxy_cache,除非你不用 Cloudflare。
镜像说明
- 基础镜像:
node:22-bookworm-slim - 包管理:pnpm monorepo
- 启动:
pnpm db:seed(幂等)→pnpm --filter @wechat-ai/api start(API + Worker 同进程) - 非 root 用户
appuser运行 - 健康检查:
GET /health
Worker 规模(单镜像)
默认仍是 一个容器跑全部:HTTP + iLink 长轮询 + AI 回复。
| 环境变量 | 默认 | 说明 |
|---|---|---|
MAX_BOTS_PER_WORKER |
500 |
本进程最多同时 long-poll 的 bot 数 |
REPLY_CONCURRENCY |
16 |
同时进行的 LLM/发送任务数 |
LEASE_TTL_SEC |
45 |
租约 TTL(同镜像多副本时防重复 poll) |
机器人很多时优先调高 MAX_BOTS_PER_WORKER 与系统 ulimit -n(注意内存与出站连接数)。
默认 单副本一体部署 即可;同镜像多副本已支持(Redis 租约分片 poll)。
多节点同构部署(10+ 台)
每台服务器跑同一镜像(API + Worker),共用一个 Upstash Redis;用户只访问主域名。
应用 env(全站一致)
PUBLIC_BASE_URL=https://你的主域名
LINUXDO_REDIRECT_URI=https://你的主域名/api/v1/auth/callback
REDIS_URL=rediss://...
COOKIE_SECURE=true
WORKER_ENABLED=true
每节点不同
WORKER_ID=node-01 # 必填且唯一
NODE_LABEL=cn-east-1a # 可选,管理后台展示
NODE_REGION=cn-east # 可选
不要给每台设不同的 PUBLIC_BASE_URL。源站直连地址(IP:8787)只写在 Cloudflare Worker 的 ORIGINS,见 cloudflare-worker/README.md。
部署步骤摘要
- 各机:
docker run ... --env-file .env -e WORKER_ID=node-0N -p 8787:8787 wechat-ai - 配置并部署
cloudflare-worker,ORIGINS=http://ip1:8787,http://ip2:8787,... - 主域名绑到 Worker
- 打开
/admin→ 节点:应看到各WORKER_ID心跳与租约 bot 数
| 探活 | 路径 |
|---|---|
| Docker / 轻量 | GET /health |
| LB 就绪(含 Redis) | GET /health/ready |
管理 API:GET /api/v1/admin/nodes(Cookie 管理员)。
扫码加机器人会话状态在 Redis,HTTP 无需粘性会话。
租约自动再平衡(rebalance)
多节点默认 开启:租约偏多的进程会周期性 主动释放 多余 lease(不 pause bot),空闲节点下一轮 claim 捡走,使各节点 bot 数接近均分。
| 环境变量 | 默认 | 说明 |
|---|---|---|
REBALANCE_ENABLED |
true |
设为 false 关闭(租约粘在首占节点) |
REBALANCE_INTERVAL_SEC |
60 |
同一进程两次 shed 最小间隔 |
REBALANCE_SLACK |
2 |
允许高出均分多少个再释放 |
REBALANCE_MAX_PER_TICK |
50 |
每次最多释放数(避免瞬间空窗过大) |
日志关键字:[worker] rebalance shed N bot(s)。约 ceil(超额 / 50) 分钟内收敛。
强制下线节点
管理后台 节点 页 → 强制下线:
- 写入 Redis fence(
wa:worker:{id}:fence) - 释放该 WORKER_ID 下全部 bot 租约
- 从
wa:workers:reg移除
目标进程下一轮 reconcile 发现 fence 后停止认领;其他节点 claim 这些 bot。
解除下线 后该节点可重新加入。
这不会 docker stop;若要从 LB 摘流量,还要从 Cloudflare Worker ORIGINS 去掉该源站。