mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-06-04 13:30:32 +08:00
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:
22
docs/cloud-image/README.md
Normal file
22
docs/cloud-image/README.md
Normal 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 | _欢迎贡献_ | ⏳ |
|
||||
|
||||
> 各平台文档结构尽量保持一致:实例规格建议 → 制作镜像操作 → 共享/公开方式 → 该平台独有的注意事项。
|
||||
100
docs/cloud-image/tencent-lighthouse.md
Normal file
100
docs/cloud-image/tencent-lighthouse.md
Normal 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. 等待 5–30 分钟(取决于系统盘大小)
|
||||
|
||||
**云服务器 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:跨账号共享(私下分享)
|
||||
|
||||
控制台 → 自定义镜像 → 选中镜像 → 「**共享**」→ 输入对方腾讯云账号 ID(UIN)。
|
||||
|
||||
- 对方在自己的「共享镜像」列表能看到,可直接基于它创建实例
|
||||
- **限制**:必须同地域;对方账号必须已开通对应产品(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 单独配置,不要把证书烤进镜像
|
||||
225
scripts/cloud-image/README.md
Normal file
225
scripts/cloud-image/README.md
Normal 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
76
scripts/cloud-image/cleanup.sh
Executable 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
|
||||
90
scripts/cloud-image/firstboot.sh
Executable file
90
scripts/cloud-image/firstboot.sh
Executable 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
107
scripts/cloud-image/prepare.sh
Executable 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"
|
||||
16
scripts/cloud-image/systemd/weknora-firstboot.service
Normal file
16
scripts/cloud-image/systemd/weknora-firstboot.service
Normal 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
|
||||
16
scripts/cloud-image/systemd/weknora.service
Normal file
16
scripts/cloud-image/systemd/weknora.service
Normal 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
|
||||
Reference in New Issue
Block a user