初始化提交

This commit is contained in:
chenxin committed 2026-08-21 16:00:13 +08:00
commit 887c1f449e
963 files changed
+166869

No files matched your search

+24
View File
@@ -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"]
+145
View File
@@ -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` 忽略)。
+47
View File
@@ -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"]
+36
View File
@@ -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
+88
View File
@@ -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
+25
View File
@@ -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
+31
View File
@@ -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] 完成"
+236
View File
@@ -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 }
+366
View File
@@ -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 }
+50
View File
@@ -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"]
+51
View File
@@ -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";
}
}
+49
View File
@@ -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"]
+23
View File
@@ -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"]
+26
View File
@@ -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"]