From e3f5fff36dc03743a5d83075246b12eeea662968 Mon Sep 17 00:00:00 2001 From: SMNET Studio Date: Mon, 10 Aug 2026 17:51:24 +0800 Subject: [PATCH] chore: replace private docker image reference with placeholder --- Dockerfile | 2 +- docs/docker.md | 251 +++++++++++++++++---------------------- scripts/docker-build.mjs | 6 +- 3 files changed, 111 insertions(+), 148 deletions(-) diff --git a/Dockerfile b/Dockerfile index c3da39b..2ccf70f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # WeChat-AI (iLink + Redis + LINUX DO OAuth) # # Prefer host wrappers (bump + OTA pack + docker): -# pnpm docker:build -- -- docker build -t e51l6pwpe/wxai:latest . +# pnpm docker:build -- -- docker build -t your-dockerhub-user/wechat-ai:latest . # pnpm docker:up # Publish channel: /admin → 上传通道包 (dist/release//files.json) # Plain `docker build` does NOT bump or pack. diff --git a/docs/docker.md b/docs/docker.md index 43386b3..2eecb28 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -1,68 +1,60 @@ -# Docker 部署 +# 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` | +| 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` | -## 快速启动 - +## 快速启?? ```bash cd /path/to/WeChat-AI -# 配置环境变量(勿提交 .env) -# 生产务必修改: -# PUBLIC_BASE_URL=https://你的域名 -# LINUXDO_REDIRECT_URI=https://你的域名/api/v1/auth/callback +# ?置????(勿?交 .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 +# ?户???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 e51l6pwpe/wxai:latest . - -# 包在 dist/release/<版本>/files.json -# 浏览器登录 /admin → 部署节点 →「上传通道包」→ 再点节点「更新」 +# ??:???+ ??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 镜像 +### ?独?建 tools ?? ```bash 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`。 +详? `docs/ai-gateway.md`?`huggingface/wechat-ai-tools/README.md`?? - -> **版本与通道:** `pnpm docker:build` 默认:升版 → **本地 pack** → Docker。 -> 通道发布只走网页:`/admin` → 上传 `files.json`(无 CLI Cookie)。 -> 直接跑 `docker build` **不会**升版、也**不会**打通道包。 - -| 地址 | 说明 | +> **?本与通??* `pnpm docker:build` 默认:?????**?地 pack** ??Docker?? +> ?????走网页:`/admin` ??上? `files.json`(? CLI Cookie)? +> ?接?`docker build` **不?**????**不?**?通??? +| ?? | 说? | |------|------| -| `/` | 功能介绍落地页(OG 分享图 `/og.jpg`) | -| `/app` | 用户中心(LINUX DO 登录、加机器人) | -| `/admin` | 管理后台(仪表盘 / Token) | -| `/health` | 健康检查 | +| `/` | ?能介??地页?OG ?享??`/og.jpg`?| +| `/app` | ?户中?(LINUX DO ?????器人? | +| `/admin` | 管??台(仪表? / Token?| +| `/health` | ?康检??| ## 仅用 Dockerfile ```bash -# 升版 + docker build -t wechat-ai . +# ?? + docker build -t wechat-ai . pnpm docker:build -- --raw -# 或自定义:node scripts/docker-build.mjs -- docker build -t wechat-ai:0.2.1 . +# ?自定?: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 \ @@ -72,61 +64,49 @@ docker run -d --name wechat-ai --restart unless-stopped \ 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` 同库),重建容器不会丢登录 - -## 常用命令 +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` ??)??建容器不?丢登? +## 常用?令 ```bash 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 e51l6pwpe/wxai:latest . -# 然后 /admin → 上传通道包 (files.json) → 更新节点 -pnpm docker:build -- --no-channel # 只升版构建,不 pack +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 → 管理后台上传通道包 → 对落后节点点「更新」。 - +## OTA 增??新(??点?常?修? +业务源?小改??必???`docker build`:本??pack ??管??台上???????对落???点?更?」? ```bash -# 构建顺带打通道包 -pnpm docker:build -- -- docker build -t e51l6pwpe/wxai:latest . -# 或仅 pack: -pnpm release:pack +# ?建顺带?通???pnpm docker:build -- -- docker build -t your-dockerhub-user/wechat-ai:latest . +# ?? pack?pnpm release:pack -# 浏览器 /admin → 部署节点 →「上传通道包」选 dist/release//files.json -# →「更新全部落后」 -``` +# 浏???/admin ???署?点 ?「?传通??」?dist/release//files.json +# ?「更?全?落??``` -| 项 | 说明 | +| ?| 说? | |----|------| -| 差量 | 按文件 sha256 比对,只下发变更文件 | -| 重启 | 节点 `process.exit(0)`,依赖 `restart: unless-stopped` 拉起**同一容器**(可写层保留补丁) | -| 版本 | 心跳 `version`:`.wa-version`(OTA 写入)→ `APP_VERSION` → 根 `package.json` | -| 仍需镜像 | Node 基础镜像、系统包、Dockerfile、`OTA_ALLOW_INSTALL=false` 时的依赖大变 | +| 差? | ???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。 +????:`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 时) +?产????????**Cloudflare**(?云代??+ Cache Rules)??**`docs/cloudflare.md`**(Business 缓?规??忽??Cookie?Purge 清?)? +### Caddy(? CF ?? ```caddy your.domain.com { @@ -134,8 +114,7 @@ your.domain.com { } ``` -### Nginx(无 CF 时,或 CF → Nginx → Node) - +### Nginx(? CF ????CF ??Nginx ??Node? ```nginx server { listen 443 ssl; @@ -149,96 +128,80 @@ server { 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 时可用: + # 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。 -## 镜像说明 +源?已???`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` +- ????:`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 回复。 - -| 环境变量 | 默认 | 说明 | +## 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` | `500` | ??程?多???long-poll ??bot ??| +| `REPLY_CONCURRENCY` | `16` | ?时进???LLM/?送任?数 | +| `LEASE_TTL_SEC` | `45` | 租约 TTL(???多副?时???poll?| -机器人很多时优先调高 `MAX_BOTS_PER_WORKER` 与系统 `ulimit -n`(注意内存与出站连接数)。 -默认 **单副本一体部署** 即可;同镜像多副本已支持(Redis 租约分片 poll)。 +?器人?多时优?调? `MAX_BOTS_PER_WORKER` 与系?`ulimit -n`(注??存???连接???? +默认 **?副??体部?* ?可;???多副?已??(Redis 租约?? poll)? +## 多????部署?10+ ?? -## 多节点同构部署(10+ 台) - -每台服务器跑**同一镜像**(API + Worker),共用一个 Upstash Redis;用户只访问**主域名**。 - -### 应用 env(全站一致) +每台?务??**????**(API + Worker)??用一?Upstash Redis;用?只访问**主???*?? +### 应用 env(全站??? ```env -PUBLIC_BASE_URL=https://你的主域名 -LINUXDO_REDIRECT_URI=https://你的主域名/api/v1/auth/callback +PUBLIC_BASE_URL=https://你?主???LINUXDO_REDIRECT_URI=https://你?主???api/v1/auth/callback REDIS_URL=rediss://... COOKIE_SECURE=true WORKER_ENABLED=true ``` -### 每节点不同 - +### 每????? ```env WORKER_ID=node-01 # 必填且唯一 -NODE_LABEL=cn-east-1a # 可选,管理后台展示 -NODE_REGION=cn-east # 可选 -``` +NODE_LABEL=cn-east-1a # ?选?管??台展示 +NODE_REGION=cn-east # ??``` -**不要**给每台设不同的 `PUBLIC_BASE_URL`。源站直连地址(IP:8787)只写在 Cloudflare Worker 的 `ORIGINS`,见 `cloudflare-worker/README.md`。 +**不?**给??设不???`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-worker`,`ORIGINS=http://ip1:8787,http://ip2:8787,...` +3. 主?????Worker +4. ?? `/admin` ??**?点**:??到??`WORKER_ID` 心跳与??bot ?? -1. 各机:`docker run ... --env-file .env -e WORKER_ID=node-0N -p 8787:8787 wechat-ai` -2. 配置并部署 `cloudflare-worker`,`ORIGINS=http://ip1:8787,http://ip2:8787,...` -3. 主域名绑到 Worker -4. 打开 `/admin` → **节点**:应看到各 `WORKER_ID` 心跳与租约 bot 数 - -| 探活 | 路径 | +| ?活 | 路? | |------|------| -| Docker / 轻量 | `GET /health` | -| LB 就绪(含 Redis) | `GET /health/ready` | +| Docker / 轻? | `GET /health` | +| LB 就绪(含 Redis?| `GET /health/ready` | -管理 API:`GET /api/v1/admin/nodes`(Cookie 管理员)。 - -扫码加机器人会话状态在 Redis,HTTP 无需粘性会话。 - -### 租约自动再平衡(rebalance) - -多节点默认 **开启**:租约偏多的进程会周期性 **主动释放** 多余 lease(不 pause bot),空闲节点下一轮 `claim` 捡走,使各节点 bot 数接近均分。 - -| 环境变量 | 默认 | 说明 | +管? 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` | 每次最多释放数(避免瞬间空窗过大) | +| `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)` 分钟内收敛。 +???键字?`[worker] rebalance shed N bot(s)`?约 `ceil(超? / 50)` ???收?? +### 强制下线?点 -### 强制下线节点 +管??台 **?点** ???**强制下线**? +1. ?入 Redis fence(`wa:worker:{id}:fence`? +2. ?放?WORKER_ID 下全??bot 租约 +3. ?`wa:workers:reg` 移除 -管理后台 **节点** 页 → **强制下线**: +??进?下??reconcile ?现 fence ??止认领????点 claim 这? bot?? +**?下线** ?该?点?????? -1. 写入 Redis fence(`wa:worker:{id}:fence`) -2. 释放该 WORKER_ID 下全部 bot 租约 -3. 从 `wa:workers:reg` 移除 - -目标进程下一轮 reconcile 发现 fence 后停止认领;其他节点 claim 这些 bot。 -**解除下线** 后该节点可重新加入。 - -这**不会** `docker stop`;若要从 LB 摘流量,还要从 Cloudflare Worker `ORIGINS` 去掉该源站。 \ No newline at end of file +?*不?** `docker stop`;若要? LB ????还??Cloudflare Worker `ORIGINS` ??该?站 \ No newline at end of file diff --git a/scripts/docker-build.mjs b/scripts/docker-build.mjs index 53c668b..4f4d571 100644 --- a/scripts/docker-build.mjs +++ b/scripts/docker-build.mjs @@ -6,7 +6,7 @@ * * pnpm docker:build * pnpm docker:up - * node scripts/docker-build.mjs -- docker build -t e51l6pwpe/wxai:latest . + * node scripts/docker-build.mjs -- docker build -t your-dockerhub-user/wechat-ai:latest . */ import { spawnSync } from "node:child_process"; import path from "node:path"; @@ -39,8 +39,8 @@ Mode: -- custom command after bump/pack Examples: - pnpm docker:build -- --bump minor -- docker build -t e51l6pwpe/wxai:latest . - pnpm docker:build -- -- docker build -t e51l6pwpe/wxai:latest . + pnpm docker:build -- --bump minor -- docker build -t your-dockerhub-user/wechat-ai:latest . + pnpm docker:build -- -- docker build -t your-dockerhub-user/wechat-ai:latest . pnpm docker:build -- --no-channel -- docker build -t wechat-ai . Note: everything after the FIRST \`--\` goes to pnpm; script flags such as