From 00a72038b6b0123414b96297275ec8c23fcab2a3 Mon Sep 17 00:00:00 2001 From: hfj Date: Thu, 13 Aug 2026 18:42:55 +0800 Subject: [PATCH 1/2] feat: add Docker support --- .dockerignore | 55 +++++++++++++++ data-agent-backend/.dockerignore | 28 ++++++++ data-agent-frontend/.dockerignore | 40 +++++++++++ docker/backend/Dockerfile | 38 +++++++++++ docker/docker-compose.yml | 110 ++++++++++++++++++++++++++++++ docker/frontend/Dockerfile | 31 +++++++++ docker/frontend/nginx.conf | 68 ++++++++++++++++++ docs/getting-started.md | 74 ++++++++++++++++++-- 8 files changed, 438 insertions(+), 6 deletions(-) create mode 100644 .dockerignore create mode 100644 data-agent-backend/.dockerignore create mode 100644 data-agent-frontend/.dockerignore create mode 100644 docker/backend/Dockerfile create mode 100644 docker/docker-compose.yml create mode 100644 docker/frontend/Dockerfile create mode 100644 docker/frontend/nginx.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b79114c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,55 @@ +# Dependencies +node_modules/ +.pnpm-store/ + +# Build output +dist/ +build/ +target/ + +# IDE files +.idea/ +.vscode/ +*.swp +*.swo + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* +pnpm-debug.log* + +# Git files +.git/ +.gitignore + +# Docker files (避免递归,但保留需要的配置文件) +docker/backend/ +docker/frontend/Dockerfile +docker/.env +docker/.env.example +docker/docker-compose.yml +docker/docker-*.sh +docker/docker-*.bat + +# Test files +coverage/ +.nyc_output/ +.playwright-cli/ + +# Documentation +*.md +docs/ + +# Root config files (不需要复制到镜像) +.env +.gitattributes +.github/ +AGENTS.md +LICENSE + +# SQL files (通过 volume 挂载,不需要复制) +sql/ diff --git a/data-agent-backend/.dockerignore b/data-agent-backend/.dockerignore new file mode 100644 index 0000000..559e119 --- /dev/null +++ b/data-agent-backend/.dockerignore @@ -0,0 +1,28 @@ +# Build artifacts +target/ +!.mvn/wrapper/maven-wrapper.jar + +# IDE files +.idea/ +*.iml +*.iws +*.ipr + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log + +# Git files +.git/ +.gitignore + +# Docker files +Dockerfile +docker/ + +# Documentation +README.md +docs/ diff --git a/data-agent-frontend/.dockerignore b/data-agent-frontend/.dockerignore new file mode 100644 index 0000000..d227597 --- /dev/null +++ b/data-agent-frontend/.dockerignore @@ -0,0 +1,40 @@ +# Dependencies +node_modules/ +.pnpm-store/ + +# Build output +dist/ +build/ + +# IDE files +.idea/ +.vscode/ +*.swp +*.swo + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* +pnpm-debug.log* + +# Git files +.git/ +.gitignore + +# Docker files +Dockerfile +docker/ + +# Test files +coverage/ +.nyc_output/ +.playwright-cli/ + +# Documentation +README.md +docs/ +CODE_STYLE.md diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile new file mode 100644 index 0000000..7f72836 --- /dev/null +++ b/docker/backend/Dockerfile @@ -0,0 +1,38 @@ +# Stage 1: Build (使用 maven 镜像,自带 Maven) +FROM maven:3.9-eclipse-temurin-17 AS builder + +WORKDIR /app + +# Copy pom.xml and download dependencies +COPY data-agent-backend/pom.xml . +RUN mvn dependency:go-offline -B + +# Copy source code and build +COPY data-agent-backend/src ./src +RUN mvn clean package -DskipTests -B + +# Stage 2: Runtime +FROM eclipse-temurin:17-jre-alpine + +WORKDIR /app + +# 创建 skills 目录(skill.properties 需要) +RUN mkdir -p /app/skills + +# Add non-root user +RUN addgroup -g 1001 appgroup && \ + adduser -u 1001 -G appgroup -D appuser + +# Copy the built jar +COPY --from=builder /app/target/*.jar app.jar + +# Create log directory +RUN mkdir -p /app/logs && chown -R appuser:appgroup /app + +USER appuser + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1 + +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..a629f36 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,110 @@ +# Data Agent Docker Compose Configuration +# +# 所有配置项都有合理的默认值,可以直接启动。 +# 如需自定义配置,在 docker/ 目录创建 .env 文件覆盖默认值即可。 +# +# 示例 .env 文件: +# IO_GITHUB_MALONETALK_MODEL_API_KEY=sk-your-api-key +# DB_PASSWORD=your-password +# JWT_SECRET=your-jwt-secret-at-least-32-bytes-long +# ADMIN_INIT_PASSWORD=your-admin-password +# + +services: + # MySQL Database + mysql: + image: mysql:8.0 + container_name: data-agent-mysql + restart: unless-stopped + environment: + # 可通过 .env 覆盖:DB_PASSWORD=xxx + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root} + MYSQL_DATABASE: data_agent + MYSQL_CHARACTER_SET_SERVER: utf8mb4 + MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci + TZ: Asia/Shanghai + ports: + # 可通过 .env 覆盖:MYSQL_PORT=3307 + - "${MYSQL_PORT:-3306}:3306" + volumes: + - data-agent-mysql:/var/lib/mysql + # 自动挂载项目根目录的 sql/ 文件夹进行初始化 + - ../sql:/docker-entrypoint-initdb.d:ro + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + - --character-set-client-handshake=FALSE + - --init-connect='SET NAMES utf8mb4' + - --default-authentication-plugin=mysql_native_password + networks: + - data-agent-network + healthcheck: + # 使用 mysql 命令实际测试数据库连接和查询 + test: ["CMD", "mysql", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD:-root}", "-e", "SELECT 1 FROM data_agent.sys_user LIMIT 1;"] + interval: 10s + timeout: 10s + retries: 10 + start_period: 60s + + # Backend Service (Spring Boot) + backend: + build: + context: .. + dockerfile: docker/backend/Dockerfile + container_name: data-agent-backend + restart: unless-stopped + environment: + # 数据库配置(通常不需要修改) + DB_URL: ${DB_URL:-jdbc:mysql://mysql:3306/data_agent?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true} + DB_USERNAME: ${DB_USERNAME:-root} + DB_PASSWORD: ${DB_PASSWORD:-root} + + # AI 模型配置(重要:需要配置真实的 API Key) + # 可通过 .env 覆盖:IO_GITHUB_MALONETALK_MODEL_API_KEY=sk-xxx + IO_GITHUB_MALONETALK_MODEL_PROVIDER: ${IO_GITHUB_MALONETALK_MODEL_PROVIDER:-dashscope} + IO_GITHUB_MALONETALK_MODEL_NAME: ${IO_GITHUB_MALONETALK_MODEL_NAME:-qwen3-max} + IO_GITHUB_MALONETALK_MODEL_BASE_URL: ${IO_GITHUB_MALONETALK_MODEL_BASE_URL:-} + IO_GITHUB_MALONETALK_MODEL_API_KEY: ${IO_GITHUB_MALONETALK_MODEL_API_KEY:-} + IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED: ${IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED:-false} + + # JWT 配置(可通过 .env 覆盖) + # JWT_SECRET 必须 >= 32 字节 + JWT_SECRET: ${JWT_SECRET:-} + JWT_EXPIRATION_HOURS: ${JWT_EXPIRATION_HOURS:-24} + + # 初始管理员密码(首次启动时使用) + ADMIN_INIT_PASSWORD: ${ADMIN_INIT_PASSWORD:-admin} + + TZ: Asia/Shanghai + ports: + # 可通过 .env 覆盖:BACKEND_PORT=8081 + - "${BACKEND_PORT:-8080}:8080" + depends_on: + mysql: + condition: service_healthy + networks: + - data-agent-network + + # Frontend Service (Vue + Nginx) + frontend: + build: + context: .. + dockerfile: docker/frontend/Dockerfile + container_name: data-agent-frontend + restart: unless-stopped + ports: + # 可通过 .env 覆盖:FRONTEND_PORT=3001 + - "${FRONTEND_PORT:-3000}:80" + depends_on: + backend: + condition: service_healthy + networks: + - data-agent-network + +volumes: + data-agent-mysql: + name: data-agent-mysql + +networks: + data-agent-network: + name: data-agent-network diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile new file mode 100644 index 0000000..ef48775 --- /dev/null +++ b/docker/frontend/Dockerfile @@ -0,0 +1,31 @@ +# Stage 1: Build +FROM node:20-alpine AS builder + +WORKDIR /app + +# 直接安装 pnpm(不使用 corepack) +RUN npm install -g pnpm + +# 先复制所有源代码(.dockerignore 会排除不需要的文件) +COPY data-agent-frontend/ . + +# 再安装依赖 +RUN pnpm install --frozen-lockfile + +# Build the application (跳过类型检查,直接打包) +RUN pnpm vite build + +# Stage 2: Production with Nginx +FROM nginx:alpine + +# Copy custom nginx config +COPY docker/frontend/nginx.conf /etc/nginx/nginx.conf + +# Copy built files +COPY --from=builder /app/dist /usr/share/nginx/html + +# Add healthcheck +HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://127.0.0.1/health || exit 1 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/frontend/nginx.conf b/docker/frontend/nginx.conf new file mode 100644 index 0000000..d424036 --- /dev/null +++ b/docker/frontend/nginx.conf @@ -0,0 +1,68 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log warn; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss image/svg+xml; + + # Docker DNS resolver + resolver 127.0.0.11 valid=10s; + + server { + listen 80; + listen [::]:80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Vue Router history mode support + location / { + try_files $uri $uri/ /index.html; + } + + # API proxy to backend + location /api/ { + proxy_pass http://backend:8080; + 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_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + } +} diff --git a/docs/getting-started.md b/docs/getting-started.md index a5d4158..32f2e41 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,7 +2,69 @@ 本文带你从零把 Data Agent 跑起来,并在前端用自然语言提第一个问题。预计耗时 10–20 分钟。 -## 1. 前置依赖 +## 方式一:Docker 启动(推荐) + +最快的方式,自动搭建 MySQL、后端、前端。 + +### 前置要求 + +- Docker 20.10+ 与 Docker Compose v2.0+ +- 支持 Linux / Windows / macOS + +### 环境变量配置 + +启动前需要配置 `docker/.env` 文件中的环境变量: + +**必填项:** +```bash +# AI 模型配置(必须配置,否则 AI 功能无法使用) +IO_GITHUB_MALONETALK_MODEL_API_KEY={你的密钥} +IO_GITHUB_MALONETALK_MODEL_PROVIDER=openai +IO_GITHUB_MALONETALK_MODEL_NAME=deepseek-v4-flash +IO_GITHUB_MALONETALK_MODEL_BASE_URL=https://api.deepseek.com +IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED=true +``` + +**可选项(有默认值,生产环境建议修改):** +```bash +# 数据库密码(默认:root) +DB_PASSWORD= + +# JWT 密钥(默认:空,生产环境必须设置且 >= 32 字节) +JWT_SECRET= + +# 管理员初始密码(默认:admin) +ADMIN_INIT_PASSWORD= +``` + +### 启动 + +```bash +cd docker +docker-compose up -d --build +``` + +首次启动会自动: +- 构建后端/前端镜像 +- 下载 MySQL 镜像 +- 执行 `sql/` 目录初始化数据库 +- 启动所有服务 + +修改配置后重启:`docker-compose restart` + +### 访问 + +- **前端**: http://localhost:3000 +- **后端**: http://localhost:8080 +- **管理员账号**: `admin` / `admin`(或你设置的 `ADMIN_INIT_PASSWORD`) + +--- + +## 方式二:手动搭建 + +适合开发调试或不想用 Docker 的场景。 + +#### 1. 前置依赖 | 依赖 | 版本要求 | 说明 | | --- | --- | --- | @@ -12,7 +74,7 @@ | pnpm | 8+ | 前端包管理 | | MySQL | 8+ | 元数据库(存语义层/数据源/会话等) | -## 2. 准备元数据库 +### 2. 准备元数据库 Data Agent 需要一张 MySQL 元数据库来存放语义层、数据源、会话等信息。 @@ -26,7 +88,7 @@ for f in sql/*.sql; do mysql -u root -p data_agent < "$f"; done > `sql/` 目录包含四个脚本:`data_source.sql`(数据源与语义层表)、`metric.sql`(指标口径表)、`sys_user.sql`(用户表)、`sys_role.sql`(角色与表/列权限表)。均需导入。 -## 3. 配置并启动后端 +### 3. 配置并启动后端 后端默认端口 `8080`,应用名 `data-agent-management`。启动前需要告诉它:元数据库在哪、用哪个 LLM。 @@ -80,7 +142,7 @@ mvn spring-boot:run 看到日志中嵌入式容器启动在 `8080` 即成功。 -## 4. 启动前端 +### 4. 启动前端 ```bash cd data-agent-frontend @@ -90,7 +152,7 @@ pnpm dev 前端默认运行在 http://localhost:3000 ,开发代理已把 `/api` 转发到 `http://localhost:8080`(见 `vite.config.ts`)。 -## 5. 第一次提问 +## 第一次提问 > **先分清两类数据库**:「数据源管理」里配置的是 **Agent 要连接、执行 SQL 数据分析的目标数据库**(你的业务库),不是第 2 步准备的元数据库 `data_agent`(后端存放语义层/数据源配置/会话的库)。元数据库在后端启动时通过环境变量 `DB_URL` 指定,不在页面上配置。 @@ -103,7 +165,7 @@ pnpm dev > 如果回答不准,多半是语义层/指标口径没配好,或数据源尚未接入。参见 [semantic-layer.md](semantic-layer.md) 与 [configuration.md](configuration.md)。 -## 6. 常见问题 +## 常见问题 - **新增数据源连接失败,提示「未找到数据库驱动」**:后端默认仅内置 MySQL 驱动。请在 `data-agent-backend/pom.xml` 中添加你所用数据库的 JDBC 驱动依赖(坐标见 [configuration.md](configuration.md#4-查询数据源)),重新构建并启动后端后再试。 - **启动后端报数据源连接失败**:检查 `DB_URL` 中的库名、账号密码,以及 MySQL 是否允许该连接方式。 From bc03cd18a6229345deb921efc8af5cd43626c76b Mon Sep 17 00:00:00 2001 From: hfj Date: Thu, 13 Aug 2026 19:06:05 +0800 Subject: [PATCH 2/2] feat: add Docker environment config example --- docker/.env.example | 18 ++++++++++++++++++ docker/docker-compose.yml | 16 ++++++++-------- docs/getting-started.md | 8 +++++++- 3 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 docker/.env.example diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 0000000..f3a436d --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,18 @@ +# Data Agent 环境变量配置 +# 此文件不应提交到 Git(已在 .gitignore 中) + +# AI 模型配置 (必填) +IO_GITHUB_MALONETALK_MODEL_API_KEY= +IO_GITHUB_MALONETALK_MODEL_PROVIDER=openai +IO_GITHUB_MALONETALK_MODEL_NAME=deepseek-v4-flash +IO_GITHUB_MALONETALK_MODEL_BASE_URL=https://api.deepseek.com +IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED=true + +# 数据库配置 +DB_PASSWORD=root + +# JWT 配置(生产环境必须 >= 32 字节) +JWT_SECRET= + +# 管理员配置(首次启动时使用) +ADMIN_INIT_PASSWORD=admin diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a629f36..7b40f20 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,9 +1,9 @@ # Data Agent Docker Compose Configuration # # 所有配置项都有合理的默认值,可以直接启动。 -# 如需自定义配置,在 docker/ 目录创建 .env 文件覆盖默认值即可。 +# 如需自定义配置,在 docker/ 目录创建 .env.example 文件覆盖默认值即可。 # -# 示例 .env 文件: +# 示例 .env.example 文件: # IO_GITHUB_MALONETALK_MODEL_API_KEY=sk-your-api-key # DB_PASSWORD=your-password # JWT_SECRET=your-jwt-secret-at-least-32-bytes-long @@ -17,14 +17,14 @@ services: container_name: data-agent-mysql restart: unless-stopped environment: - # 可通过 .env 覆盖:DB_PASSWORD=xxx + # 可通过 .env.example 覆盖:DB_PASSWORD=xxx MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root} MYSQL_DATABASE: data_agent MYSQL_CHARACTER_SET_SERVER: utf8mb4 MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci TZ: Asia/Shanghai ports: - # 可通过 .env 覆盖:MYSQL_PORT=3307 + # 可通过 .env.example 覆盖:MYSQL_PORT=3307 - "${MYSQL_PORT:-3306}:3306" volumes: - data-agent-mysql:/var/lib/mysql @@ -60,14 +60,14 @@ services: DB_PASSWORD: ${DB_PASSWORD:-root} # AI 模型配置(重要:需要配置真实的 API Key) - # 可通过 .env 覆盖:IO_GITHUB_MALONETALK_MODEL_API_KEY=sk-xxx + # 可通过 .env.example 覆盖:IO_GITHUB_MALONETALK_MODEL_API_KEY=sk-xxx IO_GITHUB_MALONETALK_MODEL_PROVIDER: ${IO_GITHUB_MALONETALK_MODEL_PROVIDER:-dashscope} IO_GITHUB_MALONETALK_MODEL_NAME: ${IO_GITHUB_MALONETALK_MODEL_NAME:-qwen3-max} IO_GITHUB_MALONETALK_MODEL_BASE_URL: ${IO_GITHUB_MALONETALK_MODEL_BASE_URL:-} IO_GITHUB_MALONETALK_MODEL_API_KEY: ${IO_GITHUB_MALONETALK_MODEL_API_KEY:-} IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED: ${IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED:-false} - # JWT 配置(可通过 .env 覆盖) + # JWT 配置(可通过 .env.example 覆盖) # JWT_SECRET 必须 >= 32 字节 JWT_SECRET: ${JWT_SECRET:-} JWT_EXPIRATION_HOURS: ${JWT_EXPIRATION_HOURS:-24} @@ -77,7 +77,7 @@ services: TZ: Asia/Shanghai ports: - # 可通过 .env 覆盖:BACKEND_PORT=8081 + # 可通过 .env.example 覆盖:BACKEND_PORT=8081 - "${BACKEND_PORT:-8080}:8080" depends_on: mysql: @@ -93,7 +93,7 @@ services: container_name: data-agent-frontend restart: unless-stopped ports: - # 可通过 .env 覆盖:FRONTEND_PORT=3001 + # 可通过 .env.example 覆盖:FRONTEND_PORT=3001 - "${FRONTEND_PORT:-3000}:80" depends_on: backend: diff --git a/docs/getting-started.md b/docs/getting-started.md index 32f2e41..0d8f420 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -13,7 +13,13 @@ ### 环境变量配置 -启动前需要配置 `docker/.env` 文件中的环境变量: +启动前需要配置环境变量, 将`docker/.env.example`重命名为`docker/.env`: + +```bash +cp docker/.env.example docker/.env +``` + +然后编辑 `docker/.env`,参考以下说明: **必填项:** ```bash