初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,24 @@
|
||||
# 博闻(Bowen)MCP Web Search 多阶段构建
|
||||
# 独立于其他服务的 Dockerfile,通过 docker-compose.app.yml 编排
|
||||
|
||||
# ---- 构建阶段 ----
|
||||
FROM golang:1.22-alpine AS builder
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/bowen-server ./cmd/server
|
||||
|
||||
# ---- 运行阶段 ----
|
||||
FROM alpine:3.19
|
||||
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
COPY --from=builder /bin/bowen-server /usr/local/bin/bowen-server
|
||||
|
||||
EXPOSE 8080 9090
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/bowen-server"]
|
||||
CMD ["--config", "/etc/bowen/config.yaml"]
|
||||
@@ -0,0 +1,145 @@
|
||||
# 魂镜 Docker 部署
|
||||
|
||||
本目录是魂镜的容器化编排入口。本地开发默认使用 SQLite,**无需**启动本文件;如需模拟正式环境或进行生产部署,按本文说明使用。
|
||||
|
||||
## 编排结构(基础设施 / 应用 解耦)
|
||||
|
||||
编排按职责拆分为两个文件,通过共享网络 `hunjing-net` 互通:
|
||||
|
||||
| 文件 | 职责 | 网络 / 卷 |
|
||||
| --- | --- | --- |
|
||||
| `docker-compose.infra.yml` | 数据基础设施(关系库 / 缓存 / 向量 / 图库 / 对象存储 / 备份) | **创建并拥有** `hunjing-net` 网络与全部命名卷 |
|
||||
| `docker-compose.app.yml` | 应用层(后端 / 前端 / 反向代理) | 以 `external` 引用 `hunjing-net`,用 bind mount 持久化日志/数据 |
|
||||
|
||||
> 应用层依赖 `hunjing-net` 网络,**必须先启动基础设施**(首次启动会创建该网络)。容器名统一以 `hunjing-` 开头,两个文件归属同一 compose project(`name: hunjing`)。
|
||||
|
||||
### 基础设施服务(`docker-compose.infra.yml`)
|
||||
|
||||
| 服务 | 镜像 | 内存上限 / 预留 | 用途 |
|
||||
| --- | --- | --- | --- |
|
||||
| `hunjing-postgres` | `postgres:16-alpine` | 512M / 128M | 正式环境关系型数据库 |
|
||||
| `hunjing-redis` | `redis:7-alpine` | 256M / 64M | 缓存 / 后台任务队列 |
|
||||
| `hunjing-neo4j` | `neo4j:5-community` | 1G / 256M | 图数据库(原型 / 中小规模,默认) |
|
||||
| `hunjing-nebula-metad` | `vesoft/nebula-metad:v3.6.0` | 512M / 128M | NebulaGraph 元数据(需 `--profile nebulagraph`) |
|
||||
| `hunjing-nebula-storaged` | `vesoft/nebula-storaged:v3.6.0` | 1G / 256M | NebulaGraph 存储 |
|
||||
| `hunjing-nebula-graphd` | `vesoft/nebula-graphd:v3.6.0` | 512M / 128M | NebulaGraph 查询(暴露 9669) |
|
||||
| `hunjing-nebula-console-init` | `vesoft/nebula-console:v3.6.0` | 256M / 64M | 一次性注册 hosts / 建 space |
|
||||
| `hunjing-etcd` | `quay.io/coreos/etcd:v3.5.16` | 512M / 128M | Milvus 元数据依赖 |
|
||||
| `hunjing-minio` | `minio/minio:RELEASE...` | 512M / 128M | Milvus 存储 + 对象存储 |
|
||||
| `hunjing-milvus` | `milvusdb/milvus:v2.4.17` | 2G / 512M | 向量检索(暴露 19530) |
|
||||
| `hunjing-attu` | `zilliztech/attu:v2.4` | 256M / 64M | Milvus Web 可视化(`:8001`) |
|
||||
| `hunjing-pg-backup` | `postgres:16-alpine` | 256M / 64M | PostgreSQL 备份 / 恢复(需 `--profile backup`) |
|
||||
|
||||
### 应用服务(`docker-compose.app.yml`)
|
||||
|
||||
| 服务 | 镜像 | 内存上限 / 预留 | 用途 |
|
||||
| --- | --- | --- | --- |
|
||||
| `hunjing-backend` | 本目录 `backend/Dockerfile` 构建 | 1G / 256M | FastAPI 后端(`:8000`) |
|
||||
| `hunjing-frontend` | 本目录 `frontend/Dockerfile` 构建 | 512M / 128M | Next.js 前端(`:3000`) |
|
||||
| `hunjing-sandbox` | 本目录 `sandbox/Dockerfile` 构建 | 512M / 1.0CPU / 256pids | 安全沙箱(`:8090`,需 `--profile sandbox`) |
|
||||
| `hunjing-yuanqi` | 本目录 `yuanqi/Dockerfile` 构建 | 1G / 4G | 本地推理引擎(`:11434`,需 `--profile yuanqi`) |
|
||||
| `hunjing-nginx` | `nginx:1.27-alpine` | 128M / 32M | 反向代理,统一入口 80(需 `--profile nginx`) |
|
||||
|
||||
> 内存上限为本地模拟正式环境的保守默认值(写在各服务 `deploy.resources`),可按宿主机规格调整。Neo4j / NebulaGraph 二选一;本地开发向量检索使用内嵌 ChromaDB(`db/chroma`),与本编排无关。
|
||||
|
||||
## 启动顺序
|
||||
|
||||
- 基础设施 `up` 创建 `hunjing-net` 网络;应用层以 external 引用,必须后启动。
|
||||
- 后端镜像内置 `wait-for-it.sh` + `entrypoint.sh`:容器启动时按 `*_ENABLED` / `DATABASE_TYPE` 开关**动态等待依赖**(pgsql 必等 postgres;redis / milvus / neo4j / nebula 仅在对应开关启用时等待),超时只告警不阻断,再 `uv run python -m app.main`。因此即便先起应用层,后端也会等待数据库就绪而非立即失败。
|
||||
|
||||
## 前置准备
|
||||
|
||||
1. 在项目根目录复制并填写环境变量:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
重点修改:`POSTGRES_PASSWORD`、各模型 provider 的 `*_API_KEY`、`config/models.yaml`(由 `config/models.example.yaml` 复制)。若使用 NebulaGraph,确认 `GRAPH_DB_TYPE=nebulagraph`。
|
||||
|
||||
2. 应用层的 `environment` 会自动覆盖容器网络服务发现地址(如 `POSTGRES_HOST=hunjing-postgres`),无需手动改 `.env` 里的 `127.0.0.1`。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
# 1) 先启动基础设施(首次会创建 hunjing-net 网络与命名卷)
|
||||
docker compose -f docker/docker-compose.infra.yml up -d
|
||||
|
||||
# 2) 再启动应用层(构建前后端镜像)
|
||||
docker compose -f docker/docker-compose.app.yml up -d --build
|
||||
|
||||
# 一键:基础设施 + 应用(&& 保证顺序)
|
||||
docker compose -f docker/docker-compose.infra.yml up -d && \
|
||||
docker compose -f docker/docker-compose.app.yml up -d --build
|
||||
```
|
||||
|
||||
按需 / 辅助命令:
|
||||
|
||||
```bash
|
||||
# 仅启动部分基础设施
|
||||
docker compose -f docker/docker-compose.infra.yml up -d hunjing-postgres hunjing-redis
|
||||
|
||||
# NebulaGraph(需带 --profile,会自动带起 metad / storaged)
|
||||
docker compose -f docker/docker-compose.infra.yml --profile nebulagraph up -d hunjing-nebula-graphd hunjing-nebula-console-init
|
||||
|
||||
# Attu(Milvus 可视化):启动后访问 http://localhost:${ATTU_PORT:-8001}
|
||||
docker compose -f docker/docker-compose.infra.yml up -d hunjing-attu
|
||||
|
||||
# 仅启动后端 / 前端
|
||||
docker compose -f docker/docker-compose.app.yml up -d hunjing-backend
|
||||
|
||||
# 日志
|
||||
docker compose -f docker/docker-compose.app.yml logs -f hunjing-backend
|
||||
|
||||
# 停止应用 / 停止基础设施(保留数据卷)
|
||||
docker compose -f docker/docker-compose.app.yml down
|
||||
docker compose -f docker/docker-compose.infra.yml down
|
||||
|
||||
# 清除基础设施数据卷(谨慎,会丢失数据库与对象存储数据)
|
||||
docker compose -f docker/docker-compose.infra.yml down -v
|
||||
```
|
||||
|
||||
## 反向代理(可选,`--profile nginx`)
|
||||
|
||||
对外只暴露 80,`/api/*` 与 SSE 走后端、其余走前端,配置见 `docker/nginx/nginx.conf`。启用:
|
||||
|
||||
```bash
|
||||
docker compose -f docker/docker-compose.app.yml --profile nginx up -d hunjing-nginx
|
||||
# 访问 http://localhost:${NGINX_PORT:-80}
|
||||
```
|
||||
|
||||
> 启用 nginx 后,浏览器可直接访问 80 端口(同源,无跨域);后端 8000 / 前端 3000 端口可不再对外暴露(按需从 compose 删除其 `ports`)。
|
||||
|
||||
## 备份与恢复(`--profile backup`)
|
||||
|
||||
PostgreSQL 备份脚本 `docker/backup/backup-postgres.sh`:`pg_dump` 导出 + gzip 压缩 + 按 `BACKUP_RETAIN_DAYS`(默认 7)清理过期备份,产物落在项目根 `backups/`(已加入 `.gitignore`)。
|
||||
|
||||
```bash
|
||||
# 备份
|
||||
docker compose -f docker/docker-compose.infra.yml --profile backup run --rm hunjing-pg-backup
|
||||
|
||||
# 恢复(覆盖现有数据,覆盖 entrypoint 并指定 RESTORE_FILE)
|
||||
docker compose -f docker/docker-compose.infra.yml --profile backup run --rm \
|
||||
-e RESTORE_FILE=hunjing-20260620-000000.sql.gz \
|
||||
--entrypoint /bin/sh hunjing-pg-backup /backup/backup-restore.sh
|
||||
```
|
||||
|
||||
> 定时备份可结合主机 crontab 定期执行上述备份命令(脚本自身已做保留期清理)。`hunjing-postgres` 的 `PG*` 凭据由 `.env` 注入。
|
||||
|
||||
## 与 `.env` 的关系
|
||||
|
||||
- `hunjing-backend` 通过 `env_file: ../.env` 注入全部业务配置(含模型密钥);`entrypoint.sh` 读取其中的 `*_ENABLED` 决定等待哪些依赖。
|
||||
- `environment` 段覆盖容器网络服务发现地址,优先级高于 `env_file`,保证后端连到容器内服务而非 `127.0.0.1`。
|
||||
- 是否真正连接 Redis / Milvus / Neo4j / NebulaGraph,仍由 `REDIS_ENABLED` / `VECTOR_DB_ENABLED` / `GRAPH_DB_ENABLED`(配合 `GRAPH_DB_TYPE`)显式开关控制(与 `docs/architecture/system-architecture.md` 一致:不能仅凭地址是否存在隐式启用)。
|
||||
|
||||
## 数据持久化
|
||||
|
||||
- 基础设施命名卷(pg / redis / neo4j / nebula / etcd / minio / milvus)由 `docker-compose.infra.yml` 拥有。
|
||||
- 应用的 `logs/`、`db/`、`storage/`、`config/` 通过 bind mount 挂载到项目根目录(`config` 为只读挂载)。
|
||||
- 备份产物 `backups/` 为 bind mount,落在项目根。
|
||||
|
||||
## 备注
|
||||
|
||||
- 本地开发首选 SQLite + `uv run python -m app.main` + `pnpm dev`,无需 Docker;本编排面向**正式环境 / 模拟正式环境**。
|
||||
- NebulaGraph / 备份 / nginx 均用 `profiles` 隔离,默认不随 `up` 拉起。
|
||||
- 本文件未提供本地开发覆盖配置;如需差异化本地编排,可在 `docker/` 下新增 `*.local.yml`(已被 `.gitignore` 忽略)。
|
||||
@@ -0,0 +1,47 @@
|
||||
# ==============================
|
||||
# 灵台 · 魂镜后端镜像(Backend / FastAPI):Python 3.12 + uv
|
||||
# 启动入口 entrypoint.sh:先按 *_ENABLED 开关用 wait-for-it 等待依赖就绪,再 uv run python -m app.main
|
||||
# 工作目录 /app/backend,使 .env 中的相对路径 ../db ../logs ../config ../storage 解析到 /app 下。
|
||||
# ==============================
|
||||
FROM python:3.12-slim
|
||||
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
UV_LINK_MODE=copy \
|
||||
UV_COMPILE_BYTECODE=1 \
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
WORKDIR /app/backend
|
||||
|
||||
# 系统依赖:
|
||||
# build-essential / libpq-dev —— 编译 asyncpg、argon2 等 C 扩展
|
||||
# curl —— 容器健康检查
|
||||
# tzdata —— 时区数据
|
||||
# netcat-openbsd —— wait-for-it 端口探测
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential libpq-dev curl tzdata netcat-openbsd \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime && echo "$TZ" > /etc/timezone
|
||||
|
||||
# 安装 uv(沿用 backend/pyproject.toml 的清华镜像源,加速国内构建)
|
||||
RUN pip install --no-cache-dir uv -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
|
||||
# 先复制依赖清单,最大化 Docker 层缓存命中
|
||||
COPY backend/pyproject.toml backend/uv.lock ./
|
||||
|
||||
# 安装生产依赖(排除 dev 组、暂不安装项目本体)
|
||||
RUN uv sync --frozen --no-dev --no-install-project
|
||||
|
||||
# 复制后端源码并安装项目本体
|
||||
COPY backend/ ./
|
||||
RUN uv sync --frozen --no-dev
|
||||
|
||||
# 启动顺序:wait-for-it(端口探测工具)+ entrypoint.sh(按开关动态等待依赖后启动应用)
|
||||
COPY docker/backend/wait-for-it.sh /usr/local/bin/wait-for-it.sh
|
||||
COPY docker/backend/entrypoint.sh /app/backend/entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/wait-for-it.sh /app/backend/entrypoint.sh
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# entrypoint 等待依赖就绪后 exec 应用;如需覆盖启动命令,覆盖此 ENTRYPOINT 即可
|
||||
ENTRYPOINT ["/app/backend/entrypoint.sh"]
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
# 后端启动入口:按 *_ENABLED 开关动态等待依赖就绪,再启动应用。
|
||||
# 仅等待「实际启用」的依赖,避免硬等待未启用的可选服务(与 environment-config-design 一致)。
|
||||
# 某依赖超时未就绪只告警不阻断——应用自身应具备连接重试逻辑。
|
||||
set -e
|
||||
|
||||
WAIT_TIMEOUT="${WAIT_TIMEOUT:-60}"
|
||||
WAIT=""
|
||||
|
||||
# postgres:正式环境(DATABASE_TYPE=pgsql)必需依赖
|
||||
if [ "${DATABASE_TYPE}" = "pgsql" ]; then
|
||||
WAIT="$WAIT ${POSTGRES_HOST:-hunjing-postgres}:${POSTGRES_PORT:-5432}"
|
||||
fi
|
||||
|
||||
# 可选依赖:仅在对应开关启用时等待
|
||||
if [ "${REDIS_ENABLED}" = "true" ]; then
|
||||
WAIT="$WAIT ${REDIS_HOST:-hunjing-redis}:${REDIS_PORT:-6379}"
|
||||
fi
|
||||
if [ "${VECTOR_DB_ENABLED}" = "true" ] && [ "${VECTOR_DB_TYPE}" = "milvus" ]; then
|
||||
WAIT="$WAIT ${MILVUS_HOST:-hunjing-milvus}:${MILVUS_PORT:-19530}"
|
||||
fi
|
||||
if [ "${GRAPH_DB_ENABLED}" = "true" ]; then
|
||||
case "${GRAPH_DB_TYPE}" in
|
||||
neo4j) WAIT="$WAIT ${NEO4J_HOST:-hunjing-neo4j}:7687" ;;
|
||||
nebulagraph) WAIT="$WAIT ${NEBULAGRAPH_HOST:-hunjing-nebula-graphd}:${NEBULAGRAPH_PORT:-9669}" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for target in $WAIT; do
|
||||
echo "[entrypoint] 等待依赖就绪: $target"
|
||||
/usr/local/bin/wait-for-it.sh "$target" -t "$WAIT_TIMEOUT" \
|
||||
|| echo "[entrypoint] 警告: $target 未就绪,继续启动(应用应具备连接重试)"
|
||||
done
|
||||
|
||||
echo "[entrypoint] 启动后端: uv run python -m app.main"
|
||||
exec uv run python -m app.main
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
# 等待 TCP host:port 就绪的工具(wait-for-it 自包含实现)。
|
||||
# 思路来自 https://github.com/vishnubob/wait-for-it(MIT),精简为项目内置脚本,
|
||||
# 避免依赖已废弃的 compose/wait-for-it 镜像。
|
||||
#
|
||||
# 用法:
|
||||
# wait-for-it.sh host:port [-t timeout] [-s] [-q] [-- command...]
|
||||
# -t / --timeout 超时秒数(默认 15)
|
||||
# -s / --strict 探测失败时不执行后续命令、返回非零
|
||||
# -q / --quiet 不输出状态
|
||||
# -- command... 探测成功后执行的命令
|
||||
set -o pipefail
|
||||
|
||||
cmdname=$(basename "$0")
|
||||
echoerr() { [ "${QUIET:-0}" != 1 ] && echo "$@" 1>&2; }
|
||||
|
||||
usage() {
|
||||
cat <<EOF >&2
|
||||
Usage: $cmdname host:port [-t timeout] [-s] [-q] [-- command...]
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
HOST=""
|
||||
PORT=""
|
||||
TIMEOUT=15
|
||||
STRICT=0
|
||||
QUIET=0
|
||||
CLI=()
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
*:*) HOST=${1%%:*}; PORT=${1##*:}; shift ;;
|
||||
-h) HOST="$2"; shift 2 ;;
|
||||
--host=*) HOST="${1#*=}"; shift ;;
|
||||
-p) PORT="$2"; shift 2 ;;
|
||||
--port=*) PORT="${1#*=}"; shift ;;
|
||||
-t) TIMEOUT="$2"; shift 2 ;;
|
||||
--timeout=*) TIMEOUT="${1#*=}"; shift ;;
|
||||
-s|--strict) STRICT=1; shift ;;
|
||||
-q|--quiet) QUIET=1; shift ;;
|
||||
--) shift; CLI=("$@"); break ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$HOST" ] || [ -z "$PORT" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
# 探测方式:优先 nc,否则用 bash 内建 /dev/tcp(python:3.12-slim 自带 bash)。
|
||||
USE_NC=0
|
||||
if command -v nc >/dev/null 2>&1; then
|
||||
USE_NC=1
|
||||
fi
|
||||
|
||||
probe() {
|
||||
if [ "$USE_NC" -eq 1 ]; then
|
||||
nc -z "$HOST" "$PORT"
|
||||
else
|
||||
(echo > "/dev/tcp/$HOST/$PORT")
|
||||
fi
|
||||
}
|
||||
|
||||
echoerr "$cmdname: 等待 $HOST:$PORT(超时 ${TIMEOUT}s)"
|
||||
start=$(date +%s)
|
||||
while :; do
|
||||
if probe >/dev/null 2>&1; then
|
||||
echoerr "$cmdname: $HOST:$PORT 就绪(耗时 $(($(date +%s) - start))s)"
|
||||
if [ "${#CLI[@]}" -gt 0 ]; then
|
||||
exec "${CLI[@]}"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
if [ "$TIMEOUT" -ge 0 ] && [ "$(date +%s)" -gt $((start + TIMEOUT)) ]; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echoerr "$cmdname: $HOST:$PORT 在 ${TIMEOUT}s 内未就绪"
|
||||
if [ "${#CLI[@]}" -gt 0 ]; then
|
||||
if [ "$STRICT" -eq 1 ]; then
|
||||
exit 1
|
||||
fi
|
||||
exec "${CLI[@]}"
|
||||
fi
|
||||
exit 1
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# PostgreSQL 备份脚本:pg_dump 导出 + gzip 压缩 + 按天清理过期备份。
|
||||
# 通过 infra.yml 的 hunjing-pg-backup 服务运行(profile: backup):
|
||||
# docker compose -f docker/docker-compose.infra.yml --profile backup run --rm hunjing-pg-backup
|
||||
# 备份产物落在项目根 backups/ 目录(已加入 .gitignore)。
|
||||
set -eu
|
||||
|
||||
PGDATABASE="${PGDATABASE:-hunjing}"
|
||||
PGHOST="${PGHOST:-hunjing-postgres}"
|
||||
PGPORT="${PGPORT:-5432}"
|
||||
|
||||
mkdir -p /backups
|
||||
TS=$(date +%Y%m%d-%H%M%S)
|
||||
OUT="/backups/${PGDATABASE}-${TS}.sql.gz"
|
||||
|
||||
echo "[backup] 导出 ${PGDATABASE}@${PGHOST}:${PGPORT} -> ${OUT}"
|
||||
pg_dump --no-owner --no-privileges | gzip > "$OUT"
|
||||
|
||||
# 清理过期备份
|
||||
retain="${BACKUP_RETAIN_DAYS:-7}"
|
||||
echo "[backup] 清理 ${retain} 天前的备份"
|
||||
find /backups -name '*.sql.gz' -type f -mtime +"$retain" -delete 2>/dev/null || true
|
||||
|
||||
echo "[backup] 完成。当前备份列表:"
|
||||
ls -lh /backups 2>/dev/null || true
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
# PostgreSQL 恢复脚本:从指定备份文件恢复到数据库(覆盖现有数据)。
|
||||
# 通过 infra.yml 的 hunjing-pg-backup 服务运行,需覆盖 entrypoint 并传入 RESTORE_FILE:
|
||||
# docker compose -f docker/docker-compose.infra.yml --profile backup run --rm \
|
||||
# -e RESTORE_FILE=hunjing-20260620-000000.sql.gz \
|
||||
# --entrypoint /bin/sh hunjing-pg-backup /backup/backup-restore.sh
|
||||
set -eu
|
||||
|
||||
PGDATABASE="${PGDATABASE:-hunjing}"
|
||||
file="${RESTORE_FILE:-}"
|
||||
|
||||
if [ -z "$file" ]; then
|
||||
echo "[restore] 请通过 RESTORE_FILE 指定备份文件名,例如:"
|
||||
echo " -e RESTORE_FILE=${PGDATABASE}-20260620-000000.sql.gz"
|
||||
echo "[restore] 可用备份:"
|
||||
ls -1 /backups/*.sql.gz 2>/dev/null | sed 's|.*/||' || echo " (无)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
target="/backups/$file"
|
||||
if [ ! -f "$target" ]; then
|
||||
echo "[restore] 备份文件不存在: $target"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[restore] 从 $file 恢复到 ${PGDATABASE}@${PGHOST:-hunjing-postgres}:${PGPORT:-5432}"
|
||||
echo "[restore] ⚠️ 将覆盖现有数据,5 秒后开始(Ctrl+C 取消)"
|
||||
sleep 5
|
||||
|
||||
gunzip -c "$target" | psql -v ON_ERROR_STOP=1
|
||||
echo "[restore] 完成"
|
||||
@@ -0,0 +1,236 @@
|
||||
# ==============================
|
||||
# 魂镜应用层编排(后端 FastAPI + 前端 Next.js)。
|
||||
# 与基础设施 docker-compose.infra.yml 解耦:本文件以 external 方式引用共享网络 hunjing-net,
|
||||
# 通过服务名(hunjing-postgres / hunjing-redis / hunjing-milvus / hunjing-neo4j / hunjing-nebula-graphd)连接基础设施。
|
||||
#
|
||||
# 前置:先启动基础设施以创建 hunjing-net 网络与所需数据库等:
|
||||
# docker compose -f docker/docker-compose.infra.yml up -d
|
||||
#
|
||||
# 常用命令:
|
||||
# # 启动前后端(会构建镜像)
|
||||
# docker compose -f docker/docker-compose.app.yml up -d --build
|
||||
# # 仅启动后端 / 前端
|
||||
# docker compose -f docker/docker-compose.app.yml up -d hunjing-backend
|
||||
# docker compose -f docker/docker-compose.app.yml up -d hunjing-frontend
|
||||
# # 查看日志
|
||||
# docker compose -f docker/docker-compose.app.yml logs -f hunjing-backend
|
||||
# # 停止
|
||||
# docker compose -f docker/docker-compose.app.yml down
|
||||
#
|
||||
# 内存限制:每个服务通过 deploy.resources.limits/reservations 约束内存,可按宿主机规格调整。
|
||||
# ==============================
|
||||
name: hunjing
|
||||
|
||||
networks:
|
||||
hunjing-net:
|
||||
external: true
|
||||
name: hunjing-net
|
||||
# 沙箱专属内部网络:internal=true 禁止公网出站,仅同网络容器互访。
|
||||
# 后端通过此网络访问沙箱;沙箱不接入 hunjing-net,彻底切断其公网出口,防数据外泄。
|
||||
sandbox-net:
|
||||
internal: true
|
||||
name: hunjing-sandbox-net
|
||||
|
||||
services:
|
||||
# ============================== 灵台 · 后端 FastAPI(Backend) ==============================
|
||||
hunjing-backend:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/backend/Dockerfile
|
||||
image: hunjing-backend:latest
|
||||
container_name: hunjing-backend
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net, sandbox-net]
|
||||
# 注入根目录 .env(含模型密钥等);缺失时启动会报错,请先 `cp .env.example .env`。
|
||||
env_file:
|
||||
- ../.env
|
||||
environment:
|
||||
# 覆盖容器网络服务发现地址(优先级高于 env_file),保持 .env 中 127.0.0.1 默认值不变。
|
||||
APP_ENV: production
|
||||
DATABASE_TYPE: pgsql
|
||||
POSTGRES_HOST: hunjing-postgres
|
||||
POSTGRES_PORT: "5432"
|
||||
REDIS_HOST: hunjing-redis
|
||||
MILVUS_HOST: hunjing-milvus
|
||||
NEO4J_URI: bolt://hunjing-neo4j:7687
|
||||
NEBULAGRAPH_HOST: hunjing-nebula-graphd
|
||||
MINIO_ENDPOINT: http://hunjing-minio:9000
|
||||
STORAGE_PROVIDER: ${STORAGE_PROVIDER:-local}
|
||||
volumes:
|
||||
# bind mount 到 /app 下的对应目录,使 .env 相对路径 ../logs ../db ../storage ../config 生效。
|
||||
- ../logs:/app/logs
|
||||
- ../db:/app/db
|
||||
- ../storage:/app/storage
|
||||
- ../config:/app/config:ro
|
||||
ports:
|
||||
- "${BACKEND_PORT:-8000}:8000"
|
||||
# 注意:数据库等基础设施位于 infra 文件,跨文件无法用 depends_on 表达。
|
||||
# 请先启动 infra;后端自身应有数据库连接重试逻辑以应对基础设施尚未就绪的情况。
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 1G }
|
||||
reservations: { memory: 256M }
|
||||
|
||||
# ============================== 琉璃镜 · 前端 Next.js(Frontend) ==============================
|
||||
hunjing-frontend:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/frontend/Dockerfile
|
||||
image: hunjing-frontend:latest
|
||||
container_name: hunjing-frontend
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
# 容器内 Next 服务端(BFF)通过容器网络服务名连后端;浏览器只同源访问前端,
|
||||
# 所有请求经 BFF(/api/proxy、/api/sse、/api/auth),不需要后端地址。
|
||||
BACKEND_URL: http://hunjing-backend:8000
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-3000}:3000"
|
||||
depends_on:
|
||||
hunjing-backend:
|
||||
condition: service_started
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 512M }
|
||||
reservations: { memory: 128M }
|
||||
|
||||
# ============================== 乾坤 · 安全沙箱(可选,profile: sandbox,Rust 微服务 / Sandbox Engine) ==============================
|
||||
# 为百工 Tool Runtime 提供本地隔离的命令执行层(POST /v1/exec,GET /healthz)。详见 sandbox/README.md。
|
||||
# 启用:docker compose -f docker/docker-compose.app.yml --profile sandbox up -d --build hunjing-sandbox
|
||||
#
|
||||
# 容器级隔离加固(沙箱进程内另有进程组 / 超时 / 输出截断 / 路径逃逸拒绝,见 sandbox/README.md):
|
||||
# - 网络:仅接入 internal 的 sandbox-net,无公网出站;后端通过该网络以服务名 hunjing-sandbox 访问。
|
||||
# - 资源:pids_limit 防 fork bomb、cpus+memory 限额防 CPU/内存耗尽、/tmp 走 tmpfs 限大小防磁盘写满。
|
||||
# - 权限:read-only 根、no-new-privileges、cap_drop ALL、默认 seccomp profile(最小权限)。
|
||||
# - 更强隔离(自定义 seccomp 白名单 / nsjail·gVisor·microVM)见 sandbox/README.md「后续加固」。
|
||||
hunjing-sandbox:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/sandbox/Dockerfile
|
||||
image: hunjing-sandbox:latest
|
||||
container_name: hunjing-sandbox
|
||||
profiles: ["sandbox"]
|
||||
restart: unless-stopped
|
||||
networks: [sandbox-net]
|
||||
# 注入根目录 .env(与 backend 一致)。compose 的 ${} 插值不查找项目根 .env(插值源是
|
||||
# compose 文件所在目录 docker/.env,不存在),故 SANDBOX_AUTH_SECRET 必须经 env_file 注入
|
||||
# 容器;environment 块不重设它以免覆盖。executor 另有 env_clear + allowed_env 白名单
|
||||
# (仅 PATH/HOME/TZ/LANG),被执行的子进程拿不到本环境里的其他敏感变量。
|
||||
env_file:
|
||||
- ../.env
|
||||
environment:
|
||||
SANDBOX_HOST: 0.0.0.0
|
||||
SANDBOX_PORT: "8090"
|
||||
SANDBOX_BASE_WORKDIR: /app/sandbox-work
|
||||
SANDBOX_LOG_DIR: /app/logs
|
||||
SANDBOX_MAX_TIMEOUT_MS: ${SANDBOX_MAX_TIMEOUT_MS:-30000}
|
||||
SANDBOX_MAX_CONCURRENT: ${SANDBOX_MAX_CONCURRENT:-8}
|
||||
SANDBOX_MAX_OUTPUT_BYTES: ${SANDBOX_MAX_OUTPUT_BYTES:-1048576}
|
||||
SANDBOX_ALLOWED_ENV: ${SANDBOX_ALLOWED_ENV:-PATH,HOME,TZ,LANG}
|
||||
SANDBOX_AUTH_ENABLED: ${SANDBOX_AUTH_ENABLED:-true}
|
||||
RUST_LOG: ${RUST_LOG:-info}
|
||||
volumes:
|
||||
# 持久化沙箱工作目录到项目根 sandbox-work/(已加入 .gitignore)
|
||||
- ../sandbox-work:/app/sandbox-work
|
||||
# 日志写到项目根 logs/sandbox.log(与后端 app.log 同目录,便于统一查看/收集)
|
||||
- ../logs:/app/logs
|
||||
# /tmp 走 tmpfs(64M),限制临时文件占用,防止写满磁盘
|
||||
tmpfs:
|
||||
- /tmp:size=67108864,mode=1777
|
||||
# 最小权限加固:只读根文件系统(仅 /tmp tmpfs 与 sandbox-work bind mount 可写)、
|
||||
# 禁止子进程提权(no-new-privileges)、丢弃所有 capabilities(kill 进程组属同 UID 操作无需 cap)。
|
||||
# seccomp 用 Docker 默认 profile。
|
||||
read_only: true
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
ports:
|
||||
- "${SANDBOX_PORT:-8090}:8090"
|
||||
# 进程数与 CPU 限额:防 fork bomb 与 CPU 耗尽(pids/cpus 随 deploy.resources 在非 swarm 下生效)
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 512M, cpus: "1.0", pids: 256 }
|
||||
reservations: { memory: 64M }
|
||||
|
||||
# ============================== 元炁 · 本地推理引擎(可选,profile: yuanqi,Local Inference Engine) ==============================
|
||||
# 纯 Python 本地推理引擎,提供 LLM / Embedding / Reranker / 多模态推理能力。
|
||||
# 所有模型文件存放在项目根目录 models/ 下,容器启动时挂载到 /app/models。
|
||||
# 启用:docker compose -f docker/docker-compose.app.yml --profile yuanqi up -d --build hunjing-yuanqi
|
||||
hunjing-yuanqi:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/yuanqi/Dockerfile
|
||||
image: hunjing-yuanqi:latest
|
||||
container_name: hunjing-yuanqi
|
||||
profiles: ['yuanqi']
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
YUANQI_HOST: 0.0.0.0
|
||||
YUANQI_PORT: '11434'
|
||||
YUANQI_MODELS_DIR: /app/models
|
||||
YUANQI_DEVICE: ${YUANQI_DEVICE:-auto}
|
||||
YUANQI_LOG_LEVEL: ${YUANQI_LOG_LEVEL:-info}
|
||||
volumes:
|
||||
- ../models:/app/models:ro
|
||||
- ../logs:/app/logs
|
||||
ports:
|
||||
- '${YUANQI_PORT:-11434}:11434'
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 4G }
|
||||
reservations: { memory: 1G }
|
||||
|
||||
# ============================== 天目 · 本地 OCR 引擎(可选,profile: tianmu) ==============================
|
||||
# 本地 OCR 推理引擎,提供 PP-OCRv6 快速文字提取 + PaddleOCR-VL-1.6 复杂文档解析。
|
||||
# 所有模型文件存放在项目根目录 models/ 下,容器启动时挂载到 /app/models。
|
||||
# 启用:docker compose -f docker/docker-compose.app.yml --profile tianmu up -d --build hunjing-tianmu
|
||||
hunjing-tianmu:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/tianmu/Dockerfile
|
||||
image: hunjing-tianmu:latest
|
||||
container_name: hunjing-tianmu
|
||||
profiles: ['tianmu']
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
TIANMU_HOST: '0.0.0.0'
|
||||
TIANMU_PORT: '8101'
|
||||
TIANMU_MODELS_DIR: /app/models
|
||||
TIANMU_DEVICE: ${TIANMU_DEVICE:-auto}
|
||||
TIANMU_LOG_LEVEL: ${TIANMU_LOG_LEVEL:-info}
|
||||
volumes:
|
||||
- ../models:/app/models:ro
|
||||
- ../logs:/app/logs
|
||||
ports:
|
||||
- '${TIANMU_PORT:-8101}:8101'
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 4G }
|
||||
reservations: { memory: 1G }
|
||||
|
||||
# ============================== 天门 · 反向代理(Nginx,可选,profile: nginx,统一入口) ==============================
|
||||
# 对外只暴露 80:/api/* 与 SSE 走后端,其余走前端。配置见 docker/nginx/nginx.conf。
|
||||
# 启用:docker compose -f docker/docker-compose.app.yml --profile nginx up -d hunjing-nginx
|
||||
hunjing-nginx:
|
||||
image: nginx:1.27-alpine
|
||||
container_name: hunjing-nginx
|
||||
profiles: ["nginx"]
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
ports:
|
||||
- "${NGINX_PORT:-80}:80"
|
||||
depends_on:
|
||||
hunjing-backend:
|
||||
condition: service_started
|
||||
hunjing-frontend:
|
||||
condition: service_started
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 128M }
|
||||
reservations: { memory: 32M }
|
||||
@@ -0,0 +1,366 @@
|
||||
# ==============================
|
||||
# 魂镜数据基础设施编排(关系库 / 缓存 / 向量 / 图库 / 对象存储)。
|
||||
# 与应用层 docker-compose.app.yml 解耦:本文件创建并拥有共享网络 hunjing-net 与全部命名卷,
|
||||
# 应用层以 external 方式引用该网络连接数据库等服务。
|
||||
# 本地开发默认 SQLite,无需启动本文件;详见根目录 CLAUDE.md 与 docker/README.md。
|
||||
#
|
||||
# 常用命令:
|
||||
# # 启动全部基础设施(首次会创建 hunjing-net 网络)
|
||||
# docker compose -f docker/docker-compose.infra.yml up -d
|
||||
# # 仅启动部分按需服务
|
||||
# docker compose -f docker/docker-compose.infra.yml up -d hunjing-postgres hunjing-redis
|
||||
# # 启用后期大规模图数据库 NebulaGraph(需带 --profile)
|
||||
# docker compose -f docker/docker-compose.infra.yml --profile nebulagraph up -d hunjing-nebula-graphd hunjing-nebula-console-init
|
||||
# # 停止并保留数据卷
|
||||
# docker compose -f docker/docker-compose.infra.yml down
|
||||
# # 停止并清除数据卷(谨慎,会丢失数据)
|
||||
# docker compose -f docker/docker-compose.infra.yml down -v
|
||||
#
|
||||
# 内存限制:每个服务通过 deploy.resources.limits/reservations 约束内存,
|
||||
# 默认值为本地模拟正式环境的保守配置,可按宿主机规格调整。
|
||||
# ==============================
|
||||
name: hunjing
|
||||
|
||||
networks:
|
||||
hunjing-net:
|
||||
name: hunjing-net
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
hunjing-pg-data:
|
||||
hunjing-redis-data:
|
||||
hunjing-neo4j-data:
|
||||
hunjing-neo4j-logs:
|
||||
hunjing-nebula-data:
|
||||
hunjing-nebula-logs:
|
||||
hunjing-etcd-data:
|
||||
hunjing-minio-data:
|
||||
hunjing-milvus-data:
|
||||
|
||||
services:
|
||||
# ============================== 金匮 · 正式环境关系型数据库(PostgreSQL) ==============================
|
||||
hunjing-postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: hunjing-postgres
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-hunjing}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-hunjing}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-change-me}
|
||||
TZ: ${TIMEZONE:-Asia/Shanghai}
|
||||
volumes:
|
||||
- hunjing-pg-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-hunjing} -d ${POSTGRES_DB:-hunjing}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 512M }
|
||||
reservations: { memory: 128M }
|
||||
|
||||
# ============================== 灵泉 · 缓存与后台任务队列(Redis) ==============================
|
||||
hunjing-redis:
|
||||
image: redis:7-alpine
|
||||
container_name: hunjing-redis
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
command: ["redis-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- hunjing-redis-data:/data
|
||||
ports:
|
||||
- "${REDIS_PORT:-6379}:6379"
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 256M }
|
||||
reservations: { memory: 64M }
|
||||
|
||||
# ============================== 玄络 · 图数据库 Neo4j(原型 / 中小规模,默认图库) ==============================
|
||||
hunjing-neo4j:
|
||||
image: neo4j:5-community
|
||||
container_name: hunjing-neo4j
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
NEO4J_AUTH: ${NEO4J_USER:-neo4j}/${NEO4J_PASSWORD:-change-me}
|
||||
NEO4J_PLUGINS: '["apoc"]'
|
||||
TZ: ${TIMEZONE:-Asia/Shanghai}
|
||||
volumes:
|
||||
- hunjing-neo4j-data:/data
|
||||
- hunjing-neo4j-logs:/logs
|
||||
ports:
|
||||
- "7474:7474" # Neo4j Browser(HTTP)
|
||||
- "7687:7687" # Bolt 协议
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 1G }
|
||||
reservations: { memory: 256M }
|
||||
|
||||
# ============================== 玄络 · 图数据库 NebulaGraph(后期大规模,二选一) ==============================
|
||||
# NebulaGraph 由 metad(元数据)、storaged(存储)、graphd(查询)三个服务组成,
|
||||
# 首次启动需通过 console 注册 storage hosts 并创建 space(由下方 console-init 自动完成)。
|
||||
# 启用方式(需带 --profile nebulagraph):
|
||||
# docker compose -f docker/docker-compose.infra.yml --profile nebulagraph up -d hunjing-nebula-graphd hunjing-nebula-console-init
|
||||
# 同时在 .env 中设置 GRAPH_DB_TYPE=nebulagraph;默认不启用、不随 up 拉起。
|
||||
hunjing-nebula-metad:
|
||||
image: vesoft/nebula-metad:v3.6.0
|
||||
container_name: hunjing-nebula-metad
|
||||
profiles: ["nebulagraph"]
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
USER: root
|
||||
TZ: ${TIMEZONE:-Asia/Shanghai}
|
||||
command:
|
||||
- --meta_server_addrs=hunjing-nebula-metad:9559
|
||||
- --local_ip=hunjing-nebula-metad
|
||||
- --ws_ip=hunjing-nebula-metad
|
||||
- --port=9559
|
||||
- --data_path=/data/meta
|
||||
- --log_dir=/logs/meta
|
||||
volumes:
|
||||
- hunjing-nebula-data:/data
|
||||
- hunjing-nebula-logs:/logs
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 512M }
|
||||
reservations: { memory: 128M }
|
||||
|
||||
hunjing-nebula-storaged:
|
||||
image: vesoft/nebula-storaged:v3.6.0
|
||||
container_name: hunjing-nebula-storaged
|
||||
profiles: ["nebulagraph"]
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
USER: root
|
||||
TZ: ${TIMEZONE:-Asia/Shanghai}
|
||||
command:
|
||||
- --meta_server_addrs=hunjing-nebula-metad:9559
|
||||
- --local_ip=hunjing-nebula-storaged
|
||||
- --ws_ip=hunjing-nebula-storaged
|
||||
- --port=9779
|
||||
- --data_path=/data/storage
|
||||
- --log_dir=/logs/storage
|
||||
volumes:
|
||||
- hunjing-nebula-data:/data
|
||||
- hunjing-nebula-logs:/logs
|
||||
depends_on:
|
||||
hunjing-nebula-metad:
|
||||
condition: service_started
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 1G }
|
||||
reservations: { memory: 256M }
|
||||
|
||||
hunjing-nebula-graphd:
|
||||
image: vesoft/nebula-graphd:v3.6.0
|
||||
container_name: hunjing-nebula-graphd
|
||||
profiles: ["nebulagraph"]
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
USER: root
|
||||
TZ: ${TIMEZONE:-Asia/Shanghai}
|
||||
command:
|
||||
- --meta_server_addrs=hunjing-nebula-metad:9559
|
||||
- --ws_ip=hunjing-nebula-graphd
|
||||
- --port=9669
|
||||
- --log_dir=/logs/graph
|
||||
volumes:
|
||||
- hunjing-nebula-logs:/logs
|
||||
ports:
|
||||
- "${NEBULAGRAPH_PORT:-9669}:9669" # 应用连接端口
|
||||
depends_on:
|
||||
hunjing-nebula-metad:
|
||||
condition: service_started
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 512M }
|
||||
reservations: { memory: 128M }
|
||||
|
||||
# 一次性初始化:注册 storage hosts 并创建 space。
|
||||
# 若 storaged 尚未 ONLINE 导致失败,待几秒后重跑:
|
||||
# docker compose -f docker/docker-compose.infra.yml --profile nebulagraph up hunjing-nebula-console-init
|
||||
hunjing-nebula-console-init:
|
||||
image: vesoft/nebula-console:v3.6.0
|
||||
container_name: hunjing-nebula-console-init
|
||||
profiles: ["nebulagraph"]
|
||||
restart: "no"
|
||||
networks: [hunjing-net]
|
||||
depends_on:
|
||||
hunjing-nebula-graphd:
|
||||
condition: service_started
|
||||
hunjing-nebula-storaged:
|
||||
condition: service_started
|
||||
environment:
|
||||
NEBULA_USER: ${NEBULAGRAPH_USER:-root}
|
||||
NEBULA_PASSWORD: ${NEBULAGRAPH_PASSWORD:-nebula}
|
||||
NEBULA_SPACE: ${NEBULAGRAPH_SPACE:-hunjing}
|
||||
entrypoint:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "等待 graphd 就绪..."
|
||||
for i in $$(seq 1 30); do
|
||||
if nebula-console -addr=hunjing-nebula-graphd -port=9669 -u "$$NEBULA_USER" -p "$$NEBULA_PASSWORD" -e "SHOW HOSTS;" >/dev/null 2>&1; then
|
||||
echo "graphd 就绪,开始注册 hosts 并创建 space"
|
||||
nebula-console -addr=hunjing-nebula-graphd -port=9669 -u "$$NEBULA_USER" -p "$$NEBULA_PASSWORD" -e \
|
||||
"ADD HOSTS \"hunjing-nebula-storaged\":9779; CREATE SPACE IF NOT EXISTS $$NEBULA_SPACE(vid_type=FIXED_STRING(64), partition_num=10, replica_factor=1);"
|
||||
echo "初始化完成"
|
||||
exit 0
|
||||
fi
|
||||
echo "graphd 尚未就绪,2s 后重试 ($$i/30)"
|
||||
sleep 2
|
||||
done
|
||||
echo "graphd 在超时内未就绪" >&2
|
||||
exit 1
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 256M }
|
||||
reservations: { memory: 64M }
|
||||
|
||||
# ============================== 灵犀 · 向量数据库(Milvus)及其依赖 ==============================
|
||||
# 灵犀 standalone 依赖 etcd(灵犀·元册,元数据)与 minio(玉函,对象存储块)。
|
||||
hunjing-etcd:
|
||||
image: quay.io/coreos/etcd:v3.5.16
|
||||
container_name: hunjing-etcd
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
- ETCD_AUTO_COMPACTION_MODE=revision
|
||||
- ETCD_AUTO_COMPACTION_RETENTION=1000
|
||||
- ETCD_QUOTA_BACKEND_BYTES=4294967296
|
||||
- ETCD_SNAPSHOT_COUNT=50000
|
||||
volumes:
|
||||
- hunjing-etcd-data:/etcd
|
||||
command: >-
|
||||
etcd
|
||||
-advertise-client-urls=http://127.0.0.1:2379
|
||||
-listen-client-urls=http://0.0.0.0:2379
|
||||
--data-dir=/etcd
|
||||
healthcheck:
|
||||
test: ["CMD", "etcdctl", "endpoint", "health"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 512M }
|
||||
reservations: { memory: 128M }
|
||||
|
||||
# 玉函 · 对象存储 MinIO(兼灵犀块存储)
|
||||
hunjing-minio:
|
||||
image: minio/minio:RELEASE.2024-11-07T00-52-20Z
|
||||
container_name: hunjing-minio
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
# MinIO 同时作为 Milvus 的存储后端与魂镜对象存储(STORAGE_PROVIDER=minio)。
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY_ID:-minioadmin}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_ACCESS_KEY:-minioadmin}
|
||||
volumes:
|
||||
- hunjing-minio-data:/minio_data
|
||||
command: minio server /minio_data --console-address ":9001"
|
||||
ports:
|
||||
- "${MINIO_EXTERNAL_PORT:-9000}:9000" # S3 API
|
||||
- "9001:9001" # 管理控制台
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mc ready local || exit 0"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 512M }
|
||||
reservations: { memory: 128M }
|
||||
|
||||
# 灵犀 · 向量数据库 Milvus
|
||||
hunjing-milvus:
|
||||
image: milvusdb/milvus:v2.4.17
|
||||
container_name: hunjing-milvus
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
command: ["milvus", "run", "standalone"]
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
environment:
|
||||
ETCD_ENDPOINTS: hunjing-etcd:2379
|
||||
MINIO_ADDRESS: hunjing-minio:9000
|
||||
TZ: ${TIMEZONE:-Asia/Shanghai}
|
||||
volumes:
|
||||
- hunjing-milvus-data:/var/lib/milvus
|
||||
ports:
|
||||
- "${MILVUS_PORT:-19530}:19530"
|
||||
- "9091:9091" # 健康检查 / metrics
|
||||
depends_on:
|
||||
hunjing-etcd:
|
||||
condition: service_healthy
|
||||
hunjing-minio:
|
||||
condition: service_started
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -sf http://localhost:9091/healthz || exit 1"]
|
||||
interval: 30s
|
||||
start_period: 120s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 2G }
|
||||
reservations: { memory: 512M }
|
||||
|
||||
# ============================== 灵犀·观 · Milvus 可视化管理(Attu) ==============================
|
||||
# Attu 是 Milvus 官方 Web 管理界面,用于查看 collection、数据与运行查询。
|
||||
# 启动 Milvus 后访问 http://localhost:${ATTU_PORT:-8001},连接地址默认指向 hunjing-milvus。
|
||||
hunjing-attu:
|
||||
image: zilliztech/attu:v2.4
|
||||
container_name: hunjing-attu
|
||||
restart: unless-stopped
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
MILVUS_URL: hunjing-milvus:19530
|
||||
TZ: ${TIMEZONE:-Asia/Shanghai}
|
||||
ports:
|
||||
- "${ATTU_PORT:-8001}:3000"
|
||||
depends_on:
|
||||
hunjing-milvus:
|
||||
condition: service_started
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 256M }
|
||||
reservations: { memory: 64M }
|
||||
|
||||
# ============================== 金匮·摹本 · PostgreSQL 备份 / 恢复(手动触发,profile: backup) ==============================
|
||||
# 备份:docker compose -f docker/docker-compose.infra.yml --profile backup run --rm hunjing-pg-backup
|
||||
# 恢复:见 docker/backup/backup-restore.sh 顶部命令(需 override entrypoint 并传 RESTORE_FILE)。
|
||||
# 备份产物落在项目根 backups/ 目录(已加入 .gitignore)。
|
||||
hunjing-pg-backup:
|
||||
image: postgres:16-alpine
|
||||
container_name: hunjing-pg-backup
|
||||
profiles: ["backup"]
|
||||
restart: "no"
|
||||
networks: [hunjing-net]
|
||||
environment:
|
||||
PGHOST: hunjing-postgres
|
||||
PGPORT: "5432"
|
||||
PGUSER: ${POSTGRES_USER:-hunjing}
|
||||
PGPASSWORD: ${POSTGRES_PASSWORD:-change-me}
|
||||
PGDATABASE: ${POSTGRES_DB:-hunjing}
|
||||
BACKUP_RETAIN_DAYS: ${BACKUP_RETAIN_DAYS:-7}
|
||||
volumes:
|
||||
- ./backup:/backup:ro
|
||||
- ../backups:/backups
|
||||
entrypoint: ["/bin/sh", "/backup/backup-postgres.sh"]
|
||||
deploy:
|
||||
resources:
|
||||
limits: { memory: 256M }
|
||||
reservations: { memory: 64M }
|
||||
@@ -0,0 +1,50 @@
|
||||
# ==============================
|
||||
# 琉璃镜 · 魂镜前端镜像(Frontend / Next.js):Node 22 + pnpm
|
||||
# 多阶段构建:deps -> builder -> prod-deps -> runner
|
||||
# 启动入口:pnpm start(即 next start,默认端口 3000)
|
||||
# 依赖 BuildKit 的 cache mount,Docker 23+ 默认开启;旧版本需设置 DOCKER_BUILDKIT=1。
|
||||
# ==============================
|
||||
|
||||
# ---------- 基础镜像 ----------
|
||||
FROM node:22-alpine AS base
|
||||
ENV PNPM_HOME="/pnpm" \
|
||||
PATH="$PNPM_HOME:$PATH" \
|
||||
NEXT_TELEMETRY_DISABLED=1
|
||||
RUN corepack enable
|
||||
|
||||
# ---------- 全量依赖(用于构建) ----------
|
||||
FROM base AS deps
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml ./
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# ---------- 构建 ----------
|
||||
FROM base AS builder
|
||||
WORKDIR /app/frontend
|
||||
COPY --from=deps /app/frontend/node_modules ./node_modules
|
||||
COPY frontend/ ./
|
||||
# 当前无 public 目录;mkdir 保证 runner 阶段 COPY 不会失败
|
||||
RUN mkdir -p public \
|
||||
&& pnpm build
|
||||
|
||||
# ---------- 仅生产依赖(缩小运行时镜像) ----------
|
||||
FROM base AS prod-deps
|
||||
WORKDIR /app/frontend
|
||||
COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml ./
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
|
||||
pnpm install --frozen-lockfile --prod
|
||||
|
||||
# ---------- 运行时 ----------
|
||||
FROM base AS runner
|
||||
WORKDIR /app/frontend
|
||||
ENV NODE_ENV=production
|
||||
|
||||
COPY --from=prod-deps /app/frontend/node_modules ./node_modules
|
||||
COPY --from=builder /app/frontend/.next ./.next
|
||||
COPY --from=builder /app/frontend/public ./public
|
||||
COPY frontend/package.json frontend/next.config.ts ./
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["pnpm", "start"]
|
||||
@@ -0,0 +1,51 @@
|
||||
# 天门 · 魂镜统一入口反向代理(Nginx,可选,profile: nginx)。
|
||||
# 对外只暴露 80:/api/* 与 SSE 走后端,其余走前端 Next.js。
|
||||
# 启用前需先启动 backend 与 frontend;详见 docker/README.md。
|
||||
|
||||
upstream hunjing_backend {
|
||||
server hunjing-backend:8000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream hunjing_frontend {
|
||||
server hunjing-frontend:3000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# 适配人物资料 / 感知文件上传(IMPORT_MAX_FILE_MB 等)。
|
||||
client_max_body_size 64m;
|
||||
|
||||
# ============================== 后端 API 与 SSE 流 ==============================
|
||||
location /api/ {
|
||||
proxy_pass http://hunjing_backend;
|
||||
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;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# SSE 必须关闭缓冲,并允许长连接。
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
}
|
||||
|
||||
# ============================== 前端 Next.js ==============================
|
||||
location / {
|
||||
proxy_pass http://hunjing_frontend;
|
||||
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;
|
||||
# WebSocket 预留(后续双向实时交互 / Avatar)。
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# ==============================
|
||||
# 乾坤 · 魂镜安全沙箱镜像(Sandbox Engine):Rust + axum,多阶段构建。
|
||||
# 构建:rust:bookworm(含 toolchain);运行:debian:bookworm-slim(需 glibc)。
|
||||
# 监听 8090,健康检查走 GET /healthz。
|
||||
# 隔离在进程组 + 超时 + 资源限制层面;如需更强隔离(seccomp / cgroup / read-only 根),见 sandbox/README.md「后续加固」。
|
||||
# ==============================
|
||||
|
||||
# ---------- 构建阶段 ----------
|
||||
FROM rust:1.83-bookworm AS builder
|
||||
WORKDIR /build
|
||||
|
||||
# 国内 crates 镜像(按网络情况保留或删除该 RUN)
|
||||
RUN printf '[source.crates-io]\nreplace-with = "ustc"\n[source.ustc]\nregistry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index"\n' \
|
||||
> /usr/local/cargo/config.toml
|
||||
|
||||
# 先复制依赖清单以最大化层缓存(dummy src 触发依赖编译),再覆盖真实源码
|
||||
COPY sandbox/Cargo.toml ./
|
||||
RUN mkdir -p src && echo 'fn main() {}' > src/main.rs && cargo build --release || true
|
||||
|
||||
# 复制真实源码并构建(sandbox/ 整体拷入;根 .dockerignore 已忽略 target/)
|
||||
COPY sandbox/ ./
|
||||
RUN touch src/main.rs && cargo build --release
|
||||
|
||||
# ---------- 运行阶段 ----------
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
ENV TZ=Asia/Shanghai \
|
||||
RUST_LOG=info \
|
||||
SANDBOX_HOST=0.0.0.0 \
|
||||
SANDBOX_PORT=8090 \
|
||||
SANDBOX_BASE_WORKDIR=/app/sandbox-work \
|
||||
SANDBOX_LOG_DIR=/app/logs
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates tzdata curl \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime && echo "$TZ" > /etc/timezone
|
||||
|
||||
WORKDIR /app
|
||||
RUN mkdir -p /app/sandbox-work /app/logs
|
||||
|
||||
COPY --from=builder /build/target/release/hunjing-sandbox /usr/local/bin/hunjing-sandbox
|
||||
|
||||
EXPOSE 8090
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -fsS "http://127.0.0.1:${SANDBOX_PORT}/healthz" || exit 1
|
||||
|
||||
CMD ["hunjing-sandbox"]
|
||||
@@ -0,0 +1,23 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 编译依赖(部分 Python 包需要)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
g++ \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 安装 Python 依赖
|
||||
COPY tianmu/pyproject.toml .
|
||||
RUN pip install --no-cache-dir -e ".[all]"
|
||||
|
||||
# 复制源代码
|
||||
COPY tianmu/src/ src/
|
||||
|
||||
EXPOSE 8101
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
|
||||
CMD python -c "import urllib.request; import json; r = urllib.request.urlopen('http://localhost:8101/healthz'); d = json.loads(r.read()); assert d['status'] != 'error'" || exit 1
|
||||
|
||||
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8101"]
|
||||
@@ -0,0 +1,26 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 编译依赖(llama-cpp-python 需要)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
g++ \
|
||||
make \
|
||||
cmake \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 安装 Python 依赖(含 LLM + Embedding + Reranker)
|
||||
COPY yuanqi/pyproject.toml .
|
||||
RUN pip install --no-cache-dir -e ".[all]"
|
||||
|
||||
# 复制源代码
|
||||
COPY yuanqi/src/ src/
|
||||
|
||||
EXPOSE 11434
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
|
||||
CMD python -c "import urllib.request; import json; r = urllib.request.urlopen('http://localhost:11434/healthz'); d = json.loads(r.read()); assert d['status'] != 'error'" || exit 1
|
||||
|
||||
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "11434"]
|
||||
Reference in new issue
Block a user