docs(cloud-image): add cloud-agnostic image packaging scripts

Add scripts and docs for packaging WeKnora into cloud images (AMI,
custom images, snapshots) so users can distribute one-click deployable
templates on any cloud provider.

- scripts/cloud-image/: cloud-agnostic prepare/cleanup/firstboot scripts
  plus systemd units. Downloads only the 4 runtime files needed by the
  compose stack (~100KB) instead of cloning the full repo, and pins to
  any git ref via WEKNORA_REF for reproducible builds.
- firstboot.sh randomizes DB/Redis/JWT/AES secrets on first boot,
  writes credentials to /root/weknora-credentials.txt and self-removes.
- docs/cloud-image/: per-platform packaging guides. Includes a guide
  for Tencent Cloud Lighthouse / CVM covering image creation, sharing,
  and marketplace listing.

Default-on services match the unprofiled compose stack (frontend, app,
docreader, postgres, redis); optional services (qdrant, milvus,
neo4j, langfuse, etc.) remain opt-in via compose profiles to keep the
image size small.
This commit is contained in:
wizardchen
2026-05-11 11:56:03 +08:00
committed by lyingbug
parent 7c0964bb95
commit afd7d1fdf8
8 changed files with 652 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
# WeKnora 云镜像打包指南
把 WeKnora 打包成可分发的云镜像AMI / 自定义镜像 / Snapshot用户基于镜像创建实例后开机即用、自动随机化密钥、零私密泄漏。
## 通用工具
云无关的脚本和详细说明:[`scripts/cloud-image/README.md`](../../scripts/cloud-image/README.md)
包含 `prepare.sh` / `cleanup.sh` / `firstboot.sh` 三个脚本和两个 systemd 单元已在多种发行版Ubuntu / Debian / CentOS / TencentOS上验证。
## 各平台具体操作
| 平台 | 文档 | 状态 |
|---|---|---|
| 腾讯云轻量应用服务器 / CVM | [tencent-lighthouse.md](./tencent-lighthouse.md) | ✅ |
| AWS EC2 (AMI) | _欢迎贡献_ | ⏳ |
| 阿里云 ECS | _欢迎贡献_ | ⏳ |
| 火山引擎 ECS | _欢迎贡献_ | ⏳ |
| 华为云 ECS | _欢迎贡献_ | ⏳ |
| 本地 KVM / Proxmox | _欢迎贡献_ | ⏳ |
> 各平台文档结构尽量保持一致:实例规格建议 → 制作镜像操作 → 共享/公开方式 → 该平台独有的注意事项。

View File

@@ -0,0 +1,100 @@
# 腾讯云轻量应用服务器 / CVM 镜像制作指南
> **前置阅读**:通用脚本与流程见 [`scripts/cloud-image/README.md`](../../scripts/cloud-image/README.md)。本文只补充腾讯云平台专属的操作步骤。
## 实例规格建议
| 项 | 建议值 |
|---|---|
| 实例类型 | 轻量应用服务器Lighthouse / 云服务器 CVM 标准型 |
| CPU / 内存 | 至少 4 核 8G推荐 4 核 16G跑全功能 RAG + Agent |
| 系统盘 | 至少 80G SSD |
| 镜像 | Ubuntu Server 22.04 LTS / TencentOS Server 3.1 |
| 地域 | 选你后续主要用户所在地域(同地域才能跨账号共享) |
## 完整流程
1. 控制台买一台符合上述规格的实例
2. SSH 进去,按 [scripts/cloud-image/README.md](../../scripts/cloud-image/README.md) 执行 `prepare.sh`
3. 浏览器访问 `http://<公网IP>` 验证功能
4. 执行 `cleanup.sh`(会自动 `poweroff`
5. 控制台「**制作镜像**」(见下)
6. 用新镜像创建一台测试实例,验证 firstboot 工作正常
7. 「**共享镜像**」给其他账号 / 申请「**镜像市场**」上架(见下)
## 制作镜像
**轻量应用服务器**
1. 控制台 → 轻量应用服务器 → 选中已关机的实例
2. 「更多」→「**制作镜像**」
3. 镜像名建议带版本号:`weknora-v0.5.0-ubuntu2204`
4. 等待 530 分钟(取决于系统盘大小)
**云服务器 CVM**
1. 控制台 → 云服务器 → 选中已关机的实例
2. 「更多」→「制作镜像」→ 选「整机镜像」
3. 同样建议带版本号
> 同一账号下,自定义镜像有数量配额(默认 20 个),可在控制台查看。
## 验证镜像
强烈建议用新镜像创建一台测试实例,至少验证:
- [ ] 能 SSH 进去(用控制台的默认密码 / 你导入的 key
- [ ] `systemctl status weknora-firstboot` 显示已成功执行(或已 disable + 文件被删)
- [ ] `cat /root/weknora-credentials.txt` 里有随机密码
- [ ] 浏览器打开公网 IP 能访问 WeKnora能注册管理员
- [ ] `docker compose -f /opt/WeKnora/docker-compose.yml ps` 全部 healthy
- [ ] `cat /opt/WeKnora/.cloud-image-meta` 显示正确的版本
## 共享给其他用户
按「覆盖范围」递增有 3 种方式:
### 方式 A跨账号共享私下分享
控制台 → 自定义镜像 → 选中镜像 → 「**共享**」→ 输入对方腾讯云账号 IDUIN
- 对方在自己的「共享镜像」列表能看到,可直接基于它创建实例
- **限制**必须同地域对方账号必须已开通对应产品Lighthouse / CVM
- 适合小范围、合作伙伴、内测用户
### 方式 B跨地域使用
Lighthouse 的镜像可以「共享给 CVM」转成 CVM 自定义镜像后即可:
- 跨地域复制
- 导出为 `qcow2` 文件下载到本地(更通用,可用于 KVM / 其他云)
### 方式 C通过腾讯云市场上架镜像商品公开一键部署
这是真正「任何用户都能在云市场搜到并一键购买/部署」的形态。流程比较重,适合长期运营。
参考文档(请以官方为准):
- [云市场 - 镜像服务上架流程](https://cloud.tencent.com/document/product/306/3019)
- [云市场 - 镜像商品制作说明](https://cloud.tencent.com/document/product/306/30128)
- [Lighthouse - 应用镜像使用说明](https://cloud.tencent.com/document/product/1207/72665)
- [Lighthouse - 管理镜像(操作指南)](https://cloud.tencent.com/document/product/1207/63263)
大致步骤:
1. 登录 [腾讯云市场服务商管理控制台](https://console.cloud.tencent.com/serviceprovider),注册成为服务商
2. 「商品管理 → 商品列表 → 新建商品」,接入类型选「**镜像**」
3. 选择已制作好的自定义镜像(即上面方式 A/B 制作的那份)
4. **完成主机安全(专业版)检测**——这是镜像上架的硬性前置条件,需要自付 CVM + 主机安全专业版费用,建议用按量付费跑完即释放
5. 填写商品名称、版本、亮点、详情、使用指南(必传 PDF/Word/PPT/ZIP/RAR≤2MB、售后说明
6. 选择售卖方式(按量计费 / 按周期计费),按量计费目前仅支持 0 元规格
7. 提交审核(云市场运营人员审核约 7 个工作日)
8. 审核通过后,用户在云市场或购买 CVM 时即可选到你的镜像
> WeKnora 是腾讯系开源项目(`Tencent/WeKnora`),如果想推动官方上架,建议在 [WeKnora GitHub Issues](https://github.com/Tencent/WeKnora/issues) 联系维护团队,而不是个人单独申请。
## 注意事项
- 腾讯云的 cloud-init 兼容性良好,`cleanup.sh` 里的 `cloud-init clean` 能正常工作
- Lighthouse 的镜像默认大小限制为系统盘大小,请提前预估
- 若使用域名 + HTTPS建议在 firstboot 之外通过 [acme.sh](https://acme.sh) / certbot 单独配置,不要把证书烤进镜像

View File

@@ -0,0 +1,225 @@
# WeKnora 云镜像打包脚本Cloud-Agnostic
> **本文档面向「想把 WeKnora 打包成云镜像AMI / 自定义镜像 / Snapshot分发给其他人」的用户。**
> **如果你只是想自己用 WeKnora请直接看主仓 [README](../../README.md)`docker compose up -d` 即可。**
## 这套脚本能做什么
帮你把任意一台「能跑 Docker 的 Linux 实例」变成一份**可分发的云镜像模板**
- 别人基于这份镜像创建新实例后,**首次开机会自动**
- 生成全新的随机密钥DB / Redis / JWT / AES
- 启动 WeKnora 全部默认容器
- 把生成的凭证写到 `/root/weknora-credentials.txt`
- 自删除一次性初始化脚本
- 实现「**开机即用、零私密泄漏、每实例独立密钥**」
适用平台(任意 systemd + Docker 的 Linux 都行):
- 腾讯云轻量应用服务器Lighthouse / 云服务器 CVM
- AWS EC2 AMI
- 阿里云 ECS 自定义镜像
- 火山引擎 / 华为云 / Vultr Snapshot
- 本地 KVM / Proxmox 模板
各平台具体的「制作镜像 / 共享 / 上架」操作步骤,请参考 [`docs/cloud-image/`](../../docs/cloud-image/) 下对应文档。
---
## 目录结构
```
scripts/cloud-image/
├── README.md # 本文档
├── prepare.sh # 步骤一: 装 Docker + 拉运行时 + 装 firstboot
├── cleanup.sh # 步骤二: 制作镜像前清理(执行后将锁定 SSH)
├── firstboot.sh # 新实例首次开机自动执行(用户无感)
└── systemd/
├── weknora.service # 开机自启 docker compose
└── weknora-firstboot.service # 首次启动 init(执行后自删)
```
## 不需要 clone 整个 WeKnora 仓库
WeKnora 所有容器都从 Docker Hub 拉镜像(`wechatopenai/weknora-*`Go / Python / 前端源码都不需要带到宿主机。
`docker-compose.yml` 实际从宿主机挂载到容器的只有:
```
- ./config/config.yaml (单文件)
- ./skills/preloaded/ (目录)
```
所以镜像里需要的运行时文件**总共不到 100KB**
| 文件 | 大小 | 用途 |
|---|---|---|
| `docker-compose.yml` | 12K | 容器编排 |
| `.env` | 12K | 环境变量 |
| `config/config.yaml` | 8K | 后端业务配置 |
| `skills/preloaded/` | 56K | Agent 预置技能 |
`prepare.sh``curl + tar` 只下载这 4 项,不 `git clone`
## 镜像里启动哪些容器
WeKnora `docker-compose.yml` 大量服务是 **profile 限定**,本镜像只默认启动核心 5 个。
**默认启动5 个常驻容器,开机自启):**
| 容器 | 角色 |
|---|---|
| `frontend` | Vue UI / NGINX 反代 |
| `app` | WeKnora Go 后端 |
| `docreader` | Python 文档解析 (gRPC) |
| `postgres` (ParadeDB) | 主库 + pgvector 向量检索 + BM25 |
| `redis` | 流式输出 / 缓存 / 异步队列 |
> ParadeDB 自带 pgvector默认场景下不需额外起向量库。
**额外预拉但不常驻:**
- `sandbox` 镜像Agent Skills 由 app 按需 `docker run`。提前 `pull` 避免新实例首次执行 Skill 卡在下载。
**Profile 限定,不预装(用户需要时自己 `pull`**
| profile | 用途 |
|---|---|
| `minio` | 对象存储替代本地文件 |
| `qdrant` / `milvus` / `weaviate` / `doris` | 替代 pgvector |
| `neo4j` | GraphRAG 知识图谱 |
| `jaeger` | OpenTelemetry trace UI |
| `langfuse` | 自建 Langfuse 可观测平台 |
| `dex` | OIDC 登录 |
启用方式:
```bash
cd /opt/WeKnora
docker compose --profile neo4j up -d # 启用 GraphRAG
docker compose --profile langfuse up -d # 启用自建 Langfuse
docker compose --profile qdrant up -d # 切换到 Qdrant
```
---
## 完整流程(云无关)
```
1) 在目标云上买/装一台干净 Linux 实例(建议 4C8G+Ubuntu 22.04
2) SSH 进去, 拷入本目录, 执行 prepare.sh
3) 浏览器验证功能
4) 执行 cleanup.sh (清掉私密 + SSH key, 自动关机)
5) 在云控制台「制作镜像 / 创建快照 / 创建 AMI」
6) 用新镜像创建测试实例, 验证 firstboot 工作正常
7) 共享 / 公开镜像(参考各平台文档)
```
### 步骤一:在干净实例上部署
要求systemd + 联网 + sudo 权限。推荐 Ubuntu 22.04 / Debian 12 / CentOS Stream 9。
**1. 拷入脚本(任选一种,都不用 clone 整个 WeKnora 仓库):**
```bash
# 方式 A: sparse checkout (~60KB)
mkdir -p /opt/weknora-tools && cd /opt/weknora-tools
git init -q && git remote add origin https://github.com/Tencent/WeKnora.git
git config core.sparseCheckout true
echo "scripts/cloud-image/" >> .git/info/sparse-checkout
git pull -q --depth=1 origin main
# 方式 B: 直接 curl
mkdir -p /opt/weknora-tools/scripts/cloud-image/systemd && cd /opt/weknora-tools
base=https://raw.githubusercontent.com/Tencent/WeKnora/main/scripts/cloud-image
for f in prepare.sh cleanup.sh firstboot.sh README.md; do
curl -fsSL "$base/$f" -o "scripts/cloud-image/$f"
done
for f in weknora.service weknora-firstboot.service; do
curl -fsSL "$base/systemd/$f" -o "scripts/cloud-image/systemd/$f"
done
chmod +x scripts/cloud-image/*.sh
```
**2. 执行部署:**
```bash
sudo bash /opt/weknora-tools/scripts/cloud-image/prepare.sh
# 想 pin 特定版本(推荐, 保证镜像可复现)
sudo WEKNORA_REF=v0.5.0 bash /opt/weknora-tools/scripts/cloud-image/prepare.sh
```
`prepare.sh` 会:
1. 安装 Docker / Docker Compose plugin已装则跳过
2.`curl + tar` 下载 4 个运行时文件到 `/opt/WeKnora`
3. 拉取并启动默认 5 个容器 + 预拉 sandbox 镜像
4. 安装 `weknora.service`(开机自启)+ `weknora-firstboot.service`(首启 init
完成后访问 `http://<公网IP>`
### 步骤二:验证
至少验证:
- 能注册管理员、能登录
- 能创建一个知识库
- 能上传一个文档并完成解析
- 能进行一次问答
```bash
sudo docker compose -f /opt/WeKnora/docker-compose.yml ps
curl -f http://localhost:8080/health
```
### 步骤三:清理并制作镜像
> **重要**`cleanup.sh` 会删除所有 SSH 公钥、清空日志、清空数据库与 docker volume。执行后**不要再 SSH 进来**,直接去云控制台关机制作镜像。
```bash
sudo bash /opt/weknora-tools/scripts/cloud-image/cleanup.sh
```
执行完会自动 `poweroff`。然后到对应云控制台按其文档制作镜像。
### 新实例首次开机行为
用户用你的镜像创建实例后,第一次开机时 `weknora-firstboot.service` 会:
1. 生成随机的 `DB_PASSWORD` / `REDIS_PASSWORD` / `JWT_SECRET` / `SYSTEM_AES_KEY` / `TENANT_AES_KEY`
2. 写回 `/opt/WeKnora/.env`
3. `docker compose up -d` 启动全部服务
4. 把生成的凭证写到 `/root/weknora-credentials.txt`(仅 root 可读)
5. 把自己 disable + 删除自己(确保只跑一次)
之后每次开机都由 `weknora.service` 接管。
> **注意**`firstboot.sh` 默认**不**禁用注册(`DISABLE_REGISTRATION=false`),第一个注册的人会成为管理员。
> 凭证文件里有「请尽快注册以防被抢注」的醒目提示。需要更严格控制可在 `firstboot.sh` 里把这一行 `replace DISABLE_REGISTRATION true`。
---
## 升级镜像版本
镜像里没有 git 仓库,升级直接重跑 `prepare.sh`(会覆盖 4 个运行时文件,**不动 `.env` 和 docker volume 数据**
```bash
sudo WEKNORA_REF=v0.6.0 bash /opt/weknora-tools/scripts/cloud-image/prepare.sh
sudo bash /opt/weknora-tools/scripts/cloud-image/cleanup.sh # 制作新镜像前
```
## 安全注意事项
- 镜像里**不要**预置任何 LLM API Key、Langfuse Key、个人 SSH key
- 数据库 / Redis / MinIO 端口默认仅对 docker 网络可见,不要在云防火墙里对外开放
- `/root/weknora-credentials.txt``umask 077` 创建,仅 root 可读
- 每次重制镜像前必须执行 `cleanup.sh`,避免泄漏上一份测试数据 / SSH key / machine-id
## 各云平台具体操作
| 平台 | 文档 |
|---|---|
| 腾讯云轻量应用服务器 / CVM | [`docs/cloud-image/tencent-lighthouse.md`](../../docs/cloud-image/tencent-lighthouse.md) |
| AWS EC2 AMI | (欢迎贡献) |
| 阿里云 ECS | (欢迎贡献) |

76
scripts/cloud-image/cleanup.sh Executable file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# cleanup.sh - 在制作云镜像前清理私密数据。
# 警告: 本脚本会删除 SSH 公钥、清空数据库与日志,最后会自动关机。
# 执行后请直接在云控制台「制作镜像 / 创建快照 / 创建 AMI」不要再 SSH 进来。
set -euo pipefail
WEKNORA_DIR="${WEKNORA_DIR:-/opt/WeKnora}"
if [[ "${EUID}" -ne 0 ]]; then
echo "[cleanup] 请使用 sudo 或 root 运行" >&2
exit 1
fi
read -r -p "[cleanup] 该操作不可逆,确认继续? 输入 YES 继续: " ans
if [[ "${ans}" != "YES" ]]; then
echo "[cleanup] 已取消"
exit 0
fi
echo "[cleanup] 1/8 停止 WeKnora 容器"
if [[ -d "${WEKNORA_DIR}" ]]; then
cd "${WEKNORA_DIR}"
docker compose down -v --remove-orphans || true
fi
echo "[cleanup] 2/8 清空 WeKnora 业务数据"
if [[ -d "${WEKNORA_DIR}" ]]; then
rm -rf "${WEKNORA_DIR}/data"/* "${WEKNORA_DIR}/logs"/* 2>/dev/null || true
rm -f "${WEKNORA_DIR}/.env"
cp "${WEKNORA_DIR}/.env.example" "${WEKNORA_DIR}/.env"
fi
echo "[cleanup] 3/8 清理残留 docker 卷与构建缓存"
docker volume ls -q | grep -Ei 'weknora|postgres|redis|minio|qdrant|elasticsearch|milvus|weaviate' \
| xargs -r docker volume rm -f || true
docker system prune -af --volumes || true
echo "[cleanup] 4/8 清空系统日志"
journalctl --rotate || true
journalctl --vacuum-time=1s || true
find /var/log -type f \( -name '*.log' -o -name '*.gz' -o -name '*.[0-9]' \) -print0 \
| xargs -0 -r truncate -s 0 || true
find /var/log -type f \( -name '*.gz' -o -name '*.[0-9]' \) -print0 \
| xargs -0 -r rm -f || true
echo "[cleanup] 5/8 清理 SSH 历史与授权 key执行后将无法 SSH 进来)"
rm -f /root/.ssh/authorized_keys /root/.ssh/known_hosts /root/.bash_history
for d in /home/*; do
[[ -d "$d" ]] || continue
rm -f "$d/.ssh/authorized_keys" "$d/.ssh/known_hosts" "$d/.bash_history"
done
find / -xdev -type f \( -name 'id_rsa*' -o -name '*.pem' -o -name '*.key' \) \
-not -path '/etc/ssl/*' -not -path '/usr/*' -not -path '/var/lib/docker/*' 2>/dev/null \
| tee /tmp/cleanup-secrets-found.txt || true
echo "[cleanup] ↑ 上面是疑似遗留的密钥文件,必要时人工再核对"
echo "[cleanup] 6/8 重置 cloud-init / machine-id让新实例拿到新 ID"
cloud-init clean --logs --seed 2>/dev/null || true
truncate -s 0 /etc/machine-id || true
rm -f /var/lib/dbus/machine-id || true
echo "[cleanup] 7/8 清理 apt / tmp"
if command -v apt-get >/dev/null 2>&1; then
apt-get clean
rm -rf /var/lib/apt/lists/*
fi
rm -rf /tmp/* /var/tmp/* /root/.cache /home/*/.cache 2>/dev/null || true
echo "[cleanup] 8/8 同步磁盘并关机"
history -c || true
sync
echo
echo " 即将关机。关机完成后请到云控制台执行「制作镜像 / 创建快照 / 创建 AMI」。"
echo
sleep 3
poweroff

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env bash
# firstboot.sh - 由 weknora-firstboot.service 在新实例首次开机时自动执行。
# 任务: 生成随机密钥写入 .env -> 启动容器 -> 输出凭证 -> 自删除。
set -euo pipefail
WEKNORA_DIR="${WEKNORA_DIR:-/opt/WeKnora}"
ENV_FILE="${WEKNORA_DIR}/.env"
CRED_FILE="/root/weknora-credentials.txt"
LOG_FILE="/var/log/weknora-firstboot.log"
exec >>"${LOG_FILE}" 2>&1
echo "==== firstboot started at $(date -Iseconds) ===="
if [[ ! -f "${ENV_FILE}" ]]; then
echo "ERROR: ${ENV_FILE} not found"
exit 1
fi
# 生成 32 字节强随机字符串(用于 AES-256 key, 必须刚好 32 字节)
gen32() { LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32; }
# 通用密码: 24 字符, 不含 / + = (避免出现在 URL / sed 替换时出问题)
genpw() { LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 24; }
DB_PWD=$(genpw)
REDIS_PWD=$(genpw)
JWT=$(genpw)$(genpw)
SYS_AES=$(gen32)
TENANT_AES=$(gen32)
# 用 | 作 sed 分隔符避免冲突; 仅替换以 KEY= 开头的行
replace() {
local key="$1" val="$2"
if grep -qE "^${key}=" "${ENV_FILE}"; then
sed -i "s|^${key}=.*|${key}=${val}|" "${ENV_FILE}"
else
echo "${key}=${val}" >>"${ENV_FILE}"
fi
}
replace DB_PASSWORD "${DB_PWD}"
replace REDIS_PASSWORD "${REDIS_PWD}"
replace JWT_SECRET "${JWT}"
replace SYSTEM_AES_KEY "${SYS_AES}"
replace TENANT_AES_KEY "${TENANT_AES}"
replace GIN_MODE "release"
echo "env updated, starting docker compose..."
cd "${WEKNORA_DIR}"
/usr/bin/docker compose up -d
# 尝试拿公网 IP, 失败就用内网
PUB_IP=$(curl -fsS --max-time 5 https://ifconfig.me 2>/dev/null \
|| curl -fsS --max-time 5 https://api.ipify.org 2>/dev/null \
|| hostname -I | awk '{print $1}')
umask 077
cat >"${CRED_FILE}" <<INFO
========================================
WeKnora 实例初始化完成
生成时间: $(date -Iseconds)
========================================
访问地址 : http://${PUB_IP}
注册后如需关闭后续注册, 编辑 ${ENV_FILE}:
DISABLE_REGISTRATION=true
然后执行: cd ${WEKNORA_DIR} && docker compose up -d
下列随机凭证已写入 ${ENV_FILE}, 请妥善保存(仅 root 可读):
DB_PASSWORD = ${DB_PWD}
REDIS_PASSWORD = ${REDIS_PWD}
JWT_SECRET = ${JWT}
SYSTEM_AES_KEY = ${SYS_AES}
TENANT_AES_KEY = ${TENANT_AES}
安全建议:
- 切勿直接对外暴露 5432 / 6379 / 9000 等基础设施端口
- 仅用 80 / 443 对外服务, 必要时配置反向代理 + HTTPS
INFO
echo "credentials written to ${CRED_FILE}"
# 自删除: 关掉 unit + 删脚本 + 删 unit 文件
systemctl disable weknora-firstboot.service || true
rm -f /etc/systemd/system/weknora-firstboot.service
rm -f /usr/local/sbin/weknora-firstboot.sh
systemctl daemon-reload || true
echo "==== firstboot finished at $(date -Iseconds) ===="

107
scripts/cloud-image/prepare.sh Executable file
View File

@@ -0,0 +1,107 @@
#!/usr/bin/env bash
# prepare.sh - 在干净的 Linux 实例上部署 WeKnora 运行时, 用于制作云镜像模板。
# 不需要 clone 整个 WeKnora 仓库, 只下载 4 个运行时文件 (~100KB)。
# 兼容: Ubuntu / Debian / CentOS / Rocky / TencentOS 等带 systemd + Docker 的发行版。
# 使用方式: sudo bash prepare.sh
# 可调环境变量:
# WEKNORA_REF 要拉取的 git ref (tag / branch / commit), 默认 main
# WEKNORA_DIR 部署目录, 默认 /opt/WeKnora
# WEKNORA_REPO 仓库地址, 默认 https://github.com/Tencent/WeKnora
set -euo pipefail
WEKNORA_REF="${WEKNORA_REF:-main}"
WEKNORA_DIR="${WEKNORA_DIR:-/opt/WeKnora}"
WEKNORA_REPO="${WEKNORA_REPO:-https://github.com/Tencent/WeKnora}"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [[ "${EUID}" -ne 0 ]]; then
echo "[prepare] 请使用 sudo 或 root 运行" >&2
exit 1
fi
echo "[prepare] 1/6 安装 Docker 与依赖"
if ! command -v docker >/dev/null 2>&1; then
curl -fsSL https://get.docker.com | bash
fi
systemctl enable --now docker
if ! docker compose version >/dev/null 2>&1; then
if command -v apt-get >/dev/null 2>&1; then
apt-get update -y
apt-get install -y docker-compose-plugin curl tar
elif command -v yum >/dev/null 2>&1; then
yum install -y docker-compose-plugin curl tar
fi
fi
echo "[prepare] 2/6 拉取 WeKnora 运行时文件 (ref=${WEKNORA_REF})"
# 只下载实际需要的 4 个文件, 不 clone 整个仓库 (~MB 级 -> ~KB 级)
mkdir -p "${WEKNORA_DIR}/config" "${WEKNORA_DIR}/skills"
tmp=$(mktemp -d)
trap 'rm -rf "${tmp}"' EXIT
curl -fsSL "${WEKNORA_REPO}/archive/${WEKNORA_REF}.tar.gz" -o "${tmp}/repo.tar.gz"
# 仅解压需要的路径, 显著加速且省空间
tar -xzf "${tmp}/repo.tar.gz" -C "${tmp}" \
--wildcards \
'*/docker-compose.yml' \
'*/.env.example' \
'*/config/config.yaml' \
'*/skills/preloaded'
src=$(find "${tmp}" -maxdepth 1 -mindepth 1 -type d -name 'WeKnora-*' | head -1)
if [[ -z "${src}" ]]; then
echo "[prepare] 解压失败, 未找到 WeKnora-* 目录" >&2
exit 1
fi
cp "${src}/docker-compose.yml" "${WEKNORA_DIR}/"
cp "${src}/.env.example" "${WEKNORA_DIR}/"
cp "${src}/config/config.yaml" "${WEKNORA_DIR}/config/"
rm -rf "${WEKNORA_DIR}/skills/preloaded"
cp -r "${src}/skills/preloaded" "${WEKNORA_DIR}/skills/"
# 记录元信息, 供 firstboot / 升级时参考
cat >"${WEKNORA_DIR}/.cloud-image-meta" <<EOF
WEKNORA_REF=${WEKNORA_REF}
WEKNORA_REPO=${WEKNORA_REPO}
PREPARED_AT=$(date -Iseconds)
EOF
echo "[prepare] 3/6 准备 .env (默认值, firstboot 会替换为随机密钥)"
cd "${WEKNORA_DIR}"
[[ -f .env ]] || cp .env.example .env
sed -i 's/^GIN_MODE=.*/GIN_MODE=release/' .env || true
echo "[prepare] 4/6 拉取并启动默认 5 个常驻容器 (frontend/app/docreader/postgres/redis)"
docker compose pull
docker compose up -d
# 提前 pull sandbox 镜像 (Agent Skills 运行时由 app 按需 docker run, 非常驻)
# 不预拉的话, 用户首次跑 Skill 会卡在下载
echo "[prepare] 4.5/6 预拉 sandbox 镜像 (Agent Skills 用, 非常驻)"
docker compose --profile full pull sandbox || true
# 其他向量库 / 可观测组件 (qdrant, milvus, weaviate, doris, neo4j, langfuse-*, minio, jaeger, dex)
# 不预拉, 体积可省 5-15GB. 用户如需启用:
# cd /opt/WeKnora && docker compose --profile <name> up -d
echo "[prepare] 5/6 安装 systemd 单元"
install -m 0644 "${SCRIPT_DIR}/systemd/weknora.service" /etc/systemd/system/weknora.service
install -m 0644 "${SCRIPT_DIR}/systemd/weknora-firstboot.service" /etc/systemd/system/weknora-firstboot.service
install -m 0755 "${SCRIPT_DIR}/firstboot.sh" /usr/local/sbin/weknora-firstboot.sh
systemctl daemon-reload
systemctl enable weknora.service
systemctl enable weknora-firstboot.service
echo "[prepare] 6/6 完成"
echo
echo " WeKnora 运行时已部署到 ${WEKNORA_DIR}"
echo " docker-compose.yml / config/config.yaml / skills/preloaded / .env"
echo " 版本: ${WEKNORA_REF} (见 ${WEKNORA_DIR}/.cloud-image-meta)"
echo
echo " 打开浏览器访问 http://<本机公网IP> 验证功能"
echo
echo " 验证通过后执行清理并制作镜像:"
echo " sudo bash ${SCRIPT_DIR}/cleanup.sh"

View File

@@ -0,0 +1,16 @@
[Unit]
Description=WeKnora first-boot initialization (runs once)
After=docker.service network-online.target
Requires=docker.service
Wants=network-online.target
Before=weknora.service
ConditionPathExists=/usr/local/sbin/weknora-firstboot.sh
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/weknora-firstboot.sh
RemainAfterExit=no
TimeoutStartSec=900
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,16 @@
[Unit]
Description=WeKnora (docker compose)
Requires=docker.service
After=docker.service network-online.target weknora-firstboot.service
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/WeKnora
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=600
[Install]
WantedBy=multi-user.target