Files
WeChat-AI/docs/docker.md
T

8.9 KiB
Raw Blame History

Docker 部署

前置条件

依赖 说明
Docker + Compose 本机或服务器已安装
Upstash Redis .envREDIS_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.mdhuggingface/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 同库),容器重建不丢;无需本地数据卷。

生产环境检查清单

  1. LINUX DO 应用回调与 .env 完全一致:
    https://你的域名/api/v1/auth/callback
  2. PUBLIC_BASE_URL = https://你的域名(无尾斜杠)
  3. HTTPS 时设置 COOKIE_SECURE=true
  4. Redis 使用 Upstash rediss://,服务器能访问外网
  5. 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-versionOTA 写入)→ APP_VERSION → 根 package.json
仍需镜像 Node 基础镜像、系统包、Dockerfile、OTA_ALLOW_INSTALL=false 时的依赖大变

环境变量:OTA_ENABLED(默认 true)、OTA_ALLOW_INSTALLAPP_VERSION(无 OTA 戳时可选)、OTA_STAGING_DIR
注意: OTA 只改文件、不改环境变量;版本靠 /app/.wa-version 上报,无需、也不应靠 APP_VERSION 跟版。
docker compose up --build / 重建容器会丢掉仅靠 OTA 写入的补丁;长期仍以镜像为 source of truth。

反代示例

生产推荐把域名挂在 Cloudflare(橙云代理 + Cache Rules),见 docs/cloudflare.mdBusiness 缓存规则、忽略 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 startAPI + 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

部署步骤摘要

  1. 各机:docker run ... --env-file .env -e WORKER_ID=node-0N -p 8787:8787 wechat-ai
  2. 配置并部署 cloudflare-workerORIGINS=http://ip1:8787,http://ip2:8787,...
  3. 主域名绑到 Worker
  4. 打开 /admin节点:应看到各 WORKER_ID 心跳与租约 bot 数
探活 路径
Docker / 轻量 GET /health
LB 就绪(含 Redis GET /health/ready

管理 APIGET /api/v1/admin/nodesCookie 管理员)。

扫码加机器人会话状态在 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) 分钟内收敛。

强制下线节点

管理后台 节点 页 → 强制下线

  1. 写入 Redis fencewa:worker:{id}:fence
  2. 释放该 WORKER_ID 下全部 bot 租约
  3. wa:workers:reg 移除

目标进程下一轮 reconcile 发现 fence 后停止认领;其他节点 claim 这些 bot。
解除下线 后该节点可重新加入。

不会 docker stop;若要从 LB 摘流量,还要从 Cloudflare Worker ORIGINS 去掉该源站。