From 5a5377e9451526db7d848bee8be2de532290936c Mon Sep 17 00:00:00 2001 From: nanako <469449812@qq.com> Date: Mon, 10 Nov 2025 22:23:42 +0800 Subject: [PATCH] Dockerfile --- .env.example | 92 +++++ README-DOCKER.md | 699 ++++++++++++++++++++++++++++++++ backend/.dockerignore | 41 ++ backend/Dockerfile | 42 ++ backend/gateway.db | Bin 1445888 -> 1445888 bytes backend/internal/db/database.go | 11 +- docker-compose.yml | 59 +++ frontend/.dockerignore | 59 +++ frontend/Dockerfile | 36 ++ frontend/nginx.conf | 62 +++ 10 files changed, 1099 insertions(+), 2 deletions(-) create mode 100644 .env.example create mode 100644 README-DOCKER.md create mode 100644 backend/.dockerignore create mode 100644 backend/Dockerfile create mode 100644 docker-compose.yml create mode 100644 frontend/.dockerignore create mode 100644 frontend/Dockerfile create mode 100644 frontend/nginx.conf diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..56c4b04 --- /dev/null +++ b/.env.example @@ -0,0 +1,92 @@ +# ====================================== +# AI Router Docker 环境变量配置示例 +# ====================================== +# +# 使用说明: +# 1. 复制此文件为 .env +# 2. 根据实际情况修改配置值 +# 3. 不要将 .env 文件提交到版本控制系统 +# + +# ====================================== +# 后端服务配置 +# ====================================== + +# 数据库文件路径 +# 默认值:/app/data/gateway.db +# 说明:在 Docker 容器中,数据库文件会存储在此路径 +DB_PATH=/app/data/gateway.db + +# 后端服务端口 +# 默认值:8080 +# 说明:后端 API 服务监听的端口 +BACKEND_PORT=8080 + +# 后端服务主机地址(仅在需要修改时设置) +# 默认值:0.0.0.0 +# BACKEND_HOST=0.0.0.0 + +# ====================================== +# 前端服务配置 +# ====================================== + +# 前端服务端口 +# 默认值:3000 +# 说明:前端 Web 界面访问端口 +FRONTEND_PORT=3000 + +# API 基础路径(前端调用后端 API 的地址) +# 默认值:http://localhost:8080 +# 说明:在生产环境中,应该设置为实际的后端服务地址 +# VITE_API_BASE_URL=http://localhost:8080 + +# ====================================== +# Docker Compose 配置 +# ====================================== + +# 项目名称 +# 默认值:ai-router +# 说明:Docker Compose 会使用此名称作为容器、网络、卷的前缀 +COMPOSE_PROJECT_NAME=ai-router + +# ====================================== +# 日志配置 +# ====================================== + +# 日志级别 +# 可选值:debug, info, warn, error +# 默认值:info +LOG_LEVEL=info + +# ====================================== +# 数据库配置(高级选项) +# ====================================== + +# 数据库备份目录 +# 说明:可以挂载一个额外的卷用于数据库备份 +# BACKUP_PATH=/app/backups + +# ====================================== +# 网络配置(高级选项) +# ====================================== + +# 是否启用 HTTPS +# 默认值:false +# 说明:如果启用,需要配置 SSL 证书 +# ENABLE_HTTPS=false + +# SSL 证书路径(启用 HTTPS 时需要) +# SSL_CERT_PATH=/etc/ssl/certs/cert.pem +# SSL_KEY_PATH=/etc/ssl/private/key.pem + +# ====================================== +# 性能优化配置 +# ====================================== + +# Go 运行时最大处理器数 +# 默认值:自动检测 +# GOMAXPROCS=4 + +# Node.js 内存限制(MB) +# 默认值:4096 +# NODE_OPTIONS=--max-old-space-size=4096 \ No newline at end of file diff --git a/README-DOCKER.md b/README-DOCKER.md new file mode 100644 index 0000000..b84f7da --- /dev/null +++ b/README-DOCKER.md @@ -0,0 +1,699 @@ +# AI Router Docker 部署指南 + +本文档提供了 AI Router 项目的 Docker 容器化部署完整指南。 + +## 📋 目录 + +- [系统架构](#系统架构) +- [前置要求](#前置要求) +- [快速开始](#快速开始) +- [详细配置](#详细配置) +- [构建和运行](#构建和运行) +- [管理和维护](#管理和维护) +- [故障排查](#故障排查) +- [性能优化](#性能优化) +- [安全建议](#安全建议) + +## 🏗️ 系统架构 + +本项目使用 Docker Compose 编排两个主要服务: + +``` +┌─────────────────────────────────────────┐ +│ Docker Host │ +│ │ +│ ┌────────────────────────────────────┐ │ +│ │ Frontend Container (Nginx) │ │ +│ │ 端口: 3000 → 80 │ │ +│ │ - React 应用 │ │ +│ │ - Nginx 静态文件服务 │ │ +│ │ - API 请求代理 │ │ +│ └────────────────┬───────────────────┘ │ +│ │ │ +│ │ API 代理 │ +│ ↓ │ +│ ┌────────────────────────────────────┐ │ +│ │ Backend Container (Go) │ │ +│ │ 端口: 8080 │ │ +│ │ - Go API 服务 │ │ +│ │ - SQLite 数据库 │ │ +│ └────────────────┬───────────────────┘ │ +│ │ │ +│ ↓ │ +│ ┌────────────────────────────────────┐ │ +│ │ Volume: backend-data │ │ +│ │ 持久化数据库文件 │ │ +│ └────────────────────────────────────┘ │ +└─────────────────────────────────────────┘ +``` + +### 容器说明 + +#### 前端容器 (ai-router-frontend) +- **基础镜像**: nginx:1.27-alpine +- **构建方式**: 多阶段构建(Node.js 构建 + Nginx 运行) +- **端口映射**: 3000:80 +- **功能**: + - 托管 React 静态文件 + - 支持 React Router 的 HTML5 History API + - 将 `/api/*` 请求代理到后端服务 + - 提供健康检查端点 `/health` + +#### 后端容器 (ai-router-backend) +- **基础镜像**: debian:bookworm-slim +- **构建方式**: 多阶段构建(Go 编译 + 精简运行环境) +- **端口映射**: 8080:8080 +- **功能**: + - 提供 RESTful API 服务 + - 管理 SQLite 数据库 + - 处理 AI 模型路由逻辑 + - 提供健康检查端点 + +## 📦 前置要求 + +### 必需软件 + +- **Docker**: 版本 20.10.0 或更高 +- **Docker Compose**: 版本 2.0.0 或更高 + +### 检查安装 + +```bash +# 检查 Docker 版本 +docker --version + +# 检查 Docker Compose 版本 +docker compose version + +# 验证 Docker 运行状态 +docker info +``` + +### 系统要求 + +- **最低配置**: + - CPU: 2 核 + - 内存: 2GB + - 磁盘空间: 5GB + +- **推荐配置**: + - CPU: 4 核或更多 + - 内存: 4GB 或更多 + - 磁盘空间: 10GB 或更多 + +## 🚀 快速开始 + +### 1. 克隆项目(如果还没有) + +```bash +git clone +cd ai_router +``` + +### 2. 配置环境变量(可选) + +```bash +# 复制环境变量示例文件 +cp .env.example .env + +# 根据需要编辑 .env 文件 +# 默认配置已经可以直接使用 +``` + +### 3. 构建并启动服务 + +```bash +# 构建镜像并启动所有服务 +docker compose up --build -d +``` + +### 4. 验证部署 + +```bash +# 查看容器运行状态 +docker compose ps + +# 查看服务日志 +docker compose logs -f + +# 检查前端服务 +curl http://localhost:3000/health + +# 检查后端服务 +curl http://localhost:8080/api/providers +``` + +### 5. 访问应用 + +- **前端界面**: http://localhost:3000 +- **后端 API**: http://localhost:8080/api + +## ⚙️ 详细配置 + +### 环境变量配置 + +项目支持通过环境变量进行配置。创建 `.env` 文件或在 `docker-compose.yml` 中直接设置。 + +#### 核心配置 + +```bash +# 数据库路径 +DB_PATH=/app/data/gateway.db + +# 服务端口 +BACKEND_PORT=8080 +FRONTEND_PORT=3000 + +# 日志级别 +LOG_LEVEL=info + +# 项目名称 +COMPOSE_PROJECT_NAME=ai-router +``` + +#### 修改端口映射 + +如果需要修改对外暴露的端口,编辑 `docker-compose.yml`: + +```yaml +services: + backend: + ports: + - "自定义端口:8080" # 例如 "9000:8080" + + frontend: + ports: + - "自定义端口:80" # 例如 "8080:80" +``` + +### 数据持久化 + +后端数据库文件通过 Docker Volume 持久化: + +```bash +# 查看数据卷 +docker volume ls | grep ai-router + +# 查看数据卷详情 +docker volume inspect ai-router-backend-data + +# 备份数据卷 +docker run --rm -v ai-router-backend-data:/data -v $(pwd):/backup alpine tar czf /backup/backup-$(date +%Y%m%d-%H%M%S).tar.gz -C /data . + +# 恢复数据卷 +docker run --rm -v ai-router-backend-data:/data -v $(pwd):/backup alpine tar xzf /backup/backup-YYYYMMDD-HHMMSS.tar.gz -C /data +``` + +## 🔧 构建和运行 + +### 开发环境 + +```bash +# 启动服务(前台运行,可以看到实时日志) +docker compose up + +# 启动服务(后台运行) +docker compose up -d + +# 仅启动特定服务 +docker compose up backend +docker compose up frontend + +# 重新构建并启动 +docker compose up --build +``` + +### 生产环境 + +```bash +# 构建生产镜像 +docker compose build --no-cache + +# 启动服务 +docker compose up -d + +# 查看服务状态 +docker compose ps + +# 查看资源使用情况 +docker stats +``` + +### 单独构建镜像 + +```bash +# 构建后端镜像 +cd backend +docker build -t ai-router-backend:latest . + +# 构建前端镜像 +cd frontend +docker build -t ai-router-frontend:latest . +``` + +## 🛠️ 管理和维护 + +### 查看日志 + +```bash +# 查看所有服务日志 +docker compose logs + +# 实时跟踪日志 +docker compose logs -f + +# 查看特定服务的日志 +docker compose logs backend +docker compose logs frontend + +# 查看最近 100 行日志 +docker compose logs --tail=100 + +# 查看带时间戳的日志 +docker compose logs -t +``` + +### 重启服务 + +```bash +# 重启所有服务 +docker compose restart + +# 重启特定服务 +docker compose restart backend +docker compose restart frontend + +# 优雅停止后重启 +docker compose down && docker compose up -d +``` + +### 更新服务 + +```bash +# 拉取最新代码 +git pull + +# 重新构建并启动 +docker compose up --build -d + +# 查看更新后的状态 +docker compose ps +``` + +### 停止和删除 + +```bash +# 停止所有服务 +docker compose stop + +# 停止并删除容器(保留数据卷) +docker compose down + +# 停止并删除容器和数据卷(谨慎使用!) +docker compose down -v + +# 停止并删除所有内容(包括镜像) +docker compose down -v --rmi all +``` + +### 清理系统 + +```bash +# 删除未使用的容器 +docker container prune + +# 删除未使用的镜像 +docker image prune + +# 删除未使用的数据卷 +docker volume prune + +# 清理所有未使用的资源 +docker system prune -a +``` + +## 🔍 故障排查 + +### 常见问题 + +#### 1. 容器无法启动 + +```bash +# 检查容器状态 +docker compose ps + +# 查看详细错误信息 +docker compose logs + +# 检查端口占用 +netstat -an | grep 3000 +netstat -an | grep 8080 + +# Windows 系统 +netstat -ano | findstr "3000" +netstat -ano | findstr "8080" +``` + +#### 2. 前端无法连接后端 + +```bash +# 检查网络连接 +docker compose exec frontend ping backend + +# 检查后端服务是否正常 +docker compose exec backend wget -O- http://localhost:8080/api/providers + +# 查看 Nginx 配置 +docker compose exec frontend cat /etc/nginx/conf.d/default.conf + +# 查看 Nginx 错误日志 +docker compose logs frontend | grep error +``` + +#### 3. 数据库问题 + +```bash +# 进入后端容器 +docker compose exec backend sh + +# 检查数据库文件 +ls -lh /app/data/ + +# 查看数据库文件权限 +ls -l /app/data/gateway.db + +# 检查数据库连接 +# 在容器内执行应用的健康检查 +``` + +#### 4. 构建失败 + +```bash +# 清理 Docker 缓存 +docker builder prune + +# 完全重新构建(不使用缓存) +docker compose build --no-cache + +# 检查 Dockerfile 语法 +docker compose config + +# 查看构建日志 +docker compose build --progress=plain +``` + +#### 5. 性能问题 + +```bash +# 查看容器资源使用 +docker stats + +# 查看容器详细信息 +docker compose exec backend top +docker compose exec frontend top + +# 检查磁盘空间 +docker system df + +# 查看网络延迟 +docker compose exec frontend ping -c 5 backend +``` + +### 健康检查 + +```bash +# 前端健康检查 +curl -f http://localhost:3000/health || echo "Frontend unhealthy" + +# 后端健康检查 +curl -f http://localhost:8080/api/providers || echo "Backend unhealthy" + +# 查看容器健康状态 +docker compose ps +``` + +### 调试模式 + +```bash +# 进入容器进行调试 +docker compose exec backend sh +docker compose exec frontend sh + +# 以 root 用户进入 +docker compose exec -u root backend sh + +# 运行临时调试容器 +docker run -it --rm --network ai-router-network alpine sh +``` + +## ⚡ 性能优化 + +### 镜像优化 + +1. **使用多阶段构建**:已在 Dockerfile 中实现 +2. **最小化镜像层**:合并 RUN 命令 +3. **使用 .dockerignore**:已配置,排除不必要的文件 + +### 资源限制 + +编辑 `docker-compose.yml` 添加资源限制: + +```yaml +services: + backend: + deploy: + resources: + limits: + cpus: '2' + memory: 1G + reservations: + cpus: '1' + memory: 512M + + frontend: + deploy: + resources: + limits: + cpus: '1' + memory: 512M + reservations: + cpus: '0.5' + memory: 256M +``` + +### 网络优化 + +```yaml +networks: + ai-router-network: + driver: bridge + driver_opts: + com.docker.network.driver.mtu: 1500 +``` + +### 日志优化 + +```yaml +services: + backend: + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +``` + +## 🔒 安全建议 + +### 1. 使用非 root 用户 + +修改 Dockerfile 添加专用用户: + +```dockerfile +# 后端 Dockerfile +RUN groupadd -r appuser && useradd -r -g appuser appuser +USER appuser +``` + +### 2. 网络隔离 + +```yaml +# 限制容器间通信 +networks: + ai-router-network: + internal: true # 仅内部通信 +``` + +### 3. 环境变量安全 + +- 不要在 `docker-compose.yml` 中硬编码敏感信息 +- 使用 `.env` 文件,并将其添加到 `.gitignore` +- 生产环境使用 Docker Secrets + +### 4. 定期更新 + +```bash +# 更新基础镜像 +docker compose pull + +# 重新构建 +docker compose build --pull + +# 重启服务 +docker compose up -d +``` + +### 5. 限制容器权限 + +```yaml +services: + backend: + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + cap_add: + - NET_BIND_SERVICE +``` + +## 📊 监控建议 + +### 使用 Docker Stats + +```bash +# 实时监控 +docker stats + +# 监控特定容器 +docker stats ai-router-backend ai-router-frontend +``` + +### 集成监控工具 + +推荐使用以下监控工具: + +- **Prometheus + Grafana**: 指标收集和可视化 +- **ELK Stack**: 日志聚合和分析 +- **cAdvisor**: 容器性能监控 + +### 日志管理 + +```bash +# 设置日志轮转 +# 在 docker-compose.yml 中配置 +logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "5" +``` + +## 🌐 生产部署建议 + +### 1. 使用反向代理 + +在生产环境中,建议在 Docker 服务前部署 Nginx 或 Traefik 作为反向代理: + +```nginx +# 示例 Nginx 配置 +server { + listen 80; + server_name your-domain.com; + + location / { + proxy_pass http://localhost:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } +} +``` + +### 2. 启用 HTTPS + +使用 Let's Encrypt 免费证书: + +```bash +# 使用 Certbot +certbot --nginx -d your-domain.com +``` + +### 3. 配置自动重启 + +```yaml +services: + backend: + restart: unless-stopped + frontend: + restart: unless-stopped +``` + +### 4. 数据备份策略 + +```bash +# 创建自动备份脚本 +#!/bin/bash +BACKUP_DIR="/backup" +DATE=$(date +%Y%m%d-%H%M%S) +docker run --rm -v ai-router-backend-data:/data -v $BACKUP_DIR:/backup alpine tar czf /backup/db-$DATE.tar.gz -C /data . + +# 添加到 crontab +0 2 * * * /path/to/backup-script.sh +``` + +### 5. CI/CD 集成 + +```yaml +# GitHub Actions 示例 +name: Build and Deploy +on: + push: + branches: [main] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build images + run: docker compose build + - name: Push images + run: docker compose push +``` + +## 📝 维护清单 + +### 每日检查 +- [ ] 查看容器运行状态 +- [ ] 检查日志是否有错误 +- [ ] 监控资源使用情况 + +### 每周检查 +- [ ] 备份数据库 +- [ ] 清理未使用的镜像和容器 +- [ ] 检查磁盘空间 + +### 每月检查 +- [ ] 更新基础镜像 +- [ ] 审查安全更新 +- [ ] 性能优化评估 + +## 🆘 获取帮助 + +如果遇到问题,可以: + +1. 查看日志:`docker compose logs` +2. 检查本文档的故障排查部分 +3. 查看 GitHub Issues +4. 联系项目维护者 + +## 📚 相关资源 + +- [Docker 官方文档](https://docs.docker.com/) +- [Docker Compose 文档](https://docs.docker.com/compose/) +- [项目 GitHub](https://github.com/your-repo) +- [API 文档](./backend/docs/) + +--- + +**最后更新**: 2025-11-10 + +**版本**: 1.0.0 \ No newline at end of file diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..d969a93 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,41 @@ +# Git 相关 +.git +.gitignore + +# 文档 +*.md +docs/ + +# IDE 配置 +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# 测试文件 +*_test.go +testdata/ + +# 临时文件和日志 +*.log +tmp/ +temp/ + +# 操作系统文件 +.DS_Store +Thumbs.db + +# 数据库文件(构建时不需要) +*.db +*.db-shm +*.db-wal + +# 构建产物 +main +*.exe + +# 环境变量文件 +.env +.env.local +.env.*.local \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..9d13724 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,42 @@ +# 第一阶段:构建 Go 应用 +FROM golang:1.24-bookworm AS builder + +WORKDIR /app + +# 复制 go.mod 和 go.sum 文件 +COPY go.mod go.sum ./ + +# 下载依赖 +RUN go mod download + +# 复制所有源代码 +COPY . . + +# 编译应用程序 +# CGO_ENABLED=1 是因为 SQLite 驱动需要 CGO +RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -o main . + +# 第二阶段:创建最小化的运行镜像 +FROM debian:bookworm-slim + +WORKDIR /app + +# 安装运行时依赖(SQLite 需要) +RUN apt-get update && apt-get install -y \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# 从构建阶段复制编译好的二进制文件 +COPY --from=builder /app/main . + +# 创建数据目录 +RUN mkdir -p /app/data + +# 暴露后端服务端口 +EXPOSE 8080 + +# 设置默认的数据库路径环境变量 +ENV DB_PATH=/app/data/gateway.db + +# 启动应用 +CMD ["./main"] \ No newline at end of file diff --git a/backend/gateway.db b/backend/gateway.db index 8ceed47969cc67d0e4e18a245a8f2dca50093f06..0d1823a684b3b666f16b69f285191bde1f4c89ef 100644 GIT binary patch delta 641 zcmWmAOH30{6b9h==h>N3W(x9_c3N5$DV9i#ito757{Np|#Q1=XON|jqTLN|@#LP~; z31^{;uGFPCNhfim!Ic}sR-%Y*WMk~kM2UL3_;PRld+y1(w^E&`R3{$x#JeR4Q<5a5 zcs(si5&2PW7~LzAu{;LFt$YqS@gzTv!JwY+sbFW&^zw&|?%?;Es1s@@0VmMwe@}$! z1#H`|)>P-YI>ec}xoy`b`7~+TQdG}Lv60AqYs?IySIqs^N8^`qO@F87l&v7{j3K;z z2Ljg8xSujQmPY*)d3W&zS(=*05W2$0(>MdMuHB%_Jla*$mS51u3`SM2)gKTW$_RB8 z;L=bDaat&$UupOEHVTf2@3JeZ%25vb8$qvlFDFDrad~Z_a|N`CeM*~U_R z@^cu7dYyj8=(yMq?-Z?+&`FoeNYJ+u(zIO0Vb#<9K1vjEkfzI=9V@~TRlSc}?WU)* z+-kju1Ed!D#fw+{EM1>NhCgF*4zGb!czhnKa_uet*+|sZpg2f}U$^9>5uPl_pCiv4 z*;#fKRhJw|YIcH@t2c+v2h`CRx`(_~y)~>C%_gqD$@R5ODA}0HXUWE+Sa0ZPa^0%A rvgSO{T$w3MWg63&!6M9L7K^eb7GurKW{taX*1}rZj!<*k{`~w8d!Mis delta 1017 zcmZ9LU5FD`6vywGxyfWEGs$!2$rJAQ;J>n4+#Og58MiRgnqXrwG6vbE-+wx!S_ zO1>5)QKXCy!7{~*AIJ)#FT&aec0f=;>~nps)Ve;1Qd-=HK3Q8mvu&yC!;j1P-*fK& z+;h2|xdWZK1LsBuWI~`u2w98k4nl-qzu9bo{M}(C2OGUFbDQAS#V_-nqPalU}K)Xpd&1wxudvp9Mrqp4K%{M^7XbYcoR|i{D-g9n-7u-?y zX9J2}hu2xE2?{&Zgbn;jcZB`i2FpuPo#kd>oOZ+Rptm(V$?i5`9lP0v2`c?azF?2r z@G0$TZi*RoD6`L6ptC)77+{y`u!$Glq*oJ4?2i@}EjA%XyD2xp{1$9wXWOVH+u$fT zYe^W|oq_I2cR27(dw+w(W?CSF>UH@s49Dr|8Fz>snuQH`%hR)P8$k27oq+os#rhG} zy&u;6rKTtge&q=FI1;F%{?ixS7z%Y1&g&uSQVetprdNzNN zPqE8Ah=`_{HEqYRO;+haGJ%;!cG7VivuKSQrLtj2jcJy@sx7k458BQG_Ird7dg1*h zK2cUSVLIh}A)B`fwo?jDPRJ{iS`R7;Dn>DDI*w%)?4Z%XY5<}uhP&&2FW&+(-UV_1 diff --git a/backend/internal/db/database.go b/backend/internal/db/database.go index 5e4adc1..9862850 100644 --- a/backend/internal/db/database.go +++ b/backend/internal/db/database.go @@ -2,6 +2,7 @@ package db import ( "ai-gateway/internal/models" + "os" "gorm.io/driver/sqlite" "gorm.io/gorm" @@ -9,8 +10,14 @@ import ( // InitDB 初始化数据库连接并执行自动迁移 func InitDB() (*gorm.DB, error) { - // 使用SQLite驱动连接到gateway.db数据库文件 - db, err := gorm.Open(sqlite.Open("gateway.db"), &gorm.Config{}) + // 从环境变量读取数据库路径,如果未设置则使用默认路径 + dbPath := os.Getenv("DB_PATH") + if dbPath == "" { + dbPath = "gateway.db" + } + + // 使用SQLite驱动连接到数据库文件 + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) if err != nil { return nil, err } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..302c839 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,59 @@ +version: '3.8' + +services: + # 后端服务 + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: ai-router-backend + restart: unless-stopped + ports: + - "8080:8080" + environment: + # 数据库文件路径(映射到数据卷) + - DB_PATH=/app/data/gateway.db + volumes: + # 持久化数据库文件 + - backend-data:/app/data + networks: + - ai-router-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/api/providers"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # 前端服务 + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: ai-router-frontend + restart: unless-stopped + ports: + - "3000:80" + depends_on: + backend: + condition: service_healthy + networks: + - ai-router-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 20s + +# 数据卷定义 +volumes: + backend-data: + driver: local + name: ai-router-backend-data + +# 网络定义 +networks: + ai-router-network: + driver: bridge + name: ai-router-network \ No newline at end of file diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..3e9795f --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,59 @@ +# 依赖目录 +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# 构建产物 +dist/ +build/ +.vite/ + +# Git 相关 +.git +.gitignore + +# 文档 +*.md +README.md +docs/ + +# IDE 和编辑器 +.vscode/ +.idea/ +*.swp +*.swo +*~ +*.sublime-* + +# 测试相关 +coverage/ +.nyc_output/ +*.spec.js +*.spec.jsx +*.test.js +*.test.jsx + +# 环境变量文件 +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# 操作系统文件 +.DS_Store +Thumbs.db +*.log + +# 临时文件 +tmp/ +temp/ +*.tmp + +# ESLint 缓存 +.eslintcache + +# Vite 缓存 +.vite-inspect/ \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..5c50f41 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,36 @@ +# 第一阶段:构建 React 应用 +FROM node:20-alpine AS builder + +WORKDIR /app + +# 复制 package.json 和 package-lock.json +COPY package*.json ./ + +# 安装依赖 +RUN npm ci --only=production=false + +# 复制所有源代码 +COPY . . + +# 构建生产版本 +RUN npm run build + +# 第二阶段:使用 Nginx 托管静态文件 +FROM nginx:1.27-alpine + +WORKDIR /usr/share/nginx/html + +# 删除 Nginx 默认的静态文件 +RUN rm -rf ./* + +# 从构建阶段复制构建好的静态文件 +COPY --from=builder /app/dist . + +# 复制自定义的 Nginx 配置 +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# 暴露前端服务端口 +EXPOSE 80 + +# 启动 Nginx +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..cba1c01 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,62 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # 启用 gzip 压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; + + # 静态资源缓存配置 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # API 代理到后端服务 + location /api/ { + proxy_pass http://backend:8080; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + 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_cache_bypass $http_upgrade; + + # 超时设置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # 支持 React Router 的 HTML5 History API + # 所有非文件请求都返回 index.html + location / { + try_files $uri $uri/ /index.html; + } + + # 健康检查端点 + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + # 安全头部 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # 错误页面 + error_page 404 /index.html; + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file