react+springboot的Docker部署
React + Vite 前端 + Spring Boot (Java 17, MyBatis) 后端 + MySQL 项目,提供一个完整的、零错误的部署方案。方案将:
- 使用 Docker Compose 的 build 指令直接构建前端(Nginx)和后端(Spring Boot)镜像,省去手动 docker build。
- 保留 MyBatis 配置和 ./mysql/db.sql:/docker-entrypoint-initdb.d/db.sql 初始化。
- 确保部署后直接通过浏览器访问 http://localhost(Nginx 默认 80 端口)。
- 支持云部署,通用且只需修改少量配置。
项目结构
假设项目结构如下(基于 C:\full-stack\admin-react,调整路径如果不同):
text
admin-react/
├── frontend/ # React + Vite 前端
│ ├── vite.config.js
│ ├── package.json
│ ├── src/
│ └── ... (其他文件)
├── backend/ # Spring Boot 后端 (MyBatis)
│ ├── pom.xml
│ ├── src/
│ │ └── main/
│ │ ├── java/ # Java 代码和 MyBatis Mapper 接口
│ │ └── resources/
│ │ ├── application.yml
│ │ └── mapper/ # MyBatis XML Mapper 文件(可选)
│ └── ... (其他文件)
├── mysql/ # MySQL 初始化脚本
│ └── db.sql # 数据库初始化 SQL
├── nginx/ # Nginx 配置文件
│ └── nginx.conf
├── docker-compose.yml # Docker Compose 配置
├── Dockerfile.frontend # 前端 Nginx 构建
├── Dockerfile.backend # 后端 Spring Boot 构建
└── README.md # 可选文档
步骤 1: 项目配置
前端 (React + Vite)
- vite.config.js (frontend/vite.config.js):
js
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react';export default defineConfig({plugins: [react()],server: {proxy: {'/api': 'http://localhost:8080', // 开发时代理到后端},},build: {outDir: 'dist',}, });
- package.json (frontend/package.json):
json
"scripts": {"build": "vite build" }
后端 (Spring Boot + MyBatis)
- application.yml (backend/src/main/resources/application.yml):
yaml
server:port: ${SERVER_PORT:8080}spring:datasource:url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:mydatabase}?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=trueusername: ${DB_USERNAME:root}password: ${DB_PASSWORD:password}driver-class-name: com.mysql.cj.jdbc.Drivermybatis:mapper-locations: classpath:mapper/*.xml # 调整为您的 XML Mapper 路径(若无则移除)type-aliases-package: com.example.entity # 调整为您的实体包路径configuration:map-underscore-to-camel-case: truelogging:level:root: INFOcom.example: DEBUG # 调整为您的包路径
- 注意:替换 com.example 为实际包路径。若使用 MyBatis 注解(@Mapper),移除 mapper-locations。
- pom.xml (backend/pom.xml):
xml
<properties><java.version>17</java.version><mybatis.spring-boot.version>3.0.3</mybatis.spring-boot.version> </properties> <dependencies><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.spring-boot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency> </dependencies> <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></build>
- db.sql (mysql/db.sql):
sql
CREATE DATABASE IF NOT EXISTS mydatabase; USE mydatabase;CREATE TABLE IF NOT EXISTS users (id BIGINT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(50) NOT NULL UNIQUE,email VARCHAR(100) NOT NULL );INSERT IGNORE INTO users (username, email) VALUES ('admin', 'admin@example.com');
- 注意:IF NOT EXISTS 和 INSERT IGNORE 防止重复执行错误。
- MyBatis Mapper:
- 如果用 XML,放置在 backend/src/main/resources/mapper/(e.g., UserMapper.xml)。
- 如果用注解:
java
package com.example.mapper;import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select;@Mapper public interface UserMapper {@Select("SELECT * FROM users WHERE id = #{id}")User findById(Long id); }
- 主类添加 @MapperScan:
java
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication @MapperScan("com.example.mapper") public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);} }
Nginx 配置
nginx.conf (nginx/nginx.conf):
nginx
server {listen 80;server_name localhost;root /usr/share/nginx/html;index index.html;# React 单页应用路由location / {try_files $uri $uri/ /index.html;}# 代理 API 到后端location /api/ {proxy_pass http://backend:8080/api/;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}# 静态资源缓存location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot) {expires 1y;access_log off;add_header Cache-Control "public";}error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/share/nginx/html;}
}
步骤 2: Dockerfiles
前端 (Nginx)
Dockerfile.frontend (C:\full-stack\admin-react\Dockerfile.frontend):
dockerfile
# 构建 React 前端
FROM node:20-alpine AS build
WORKDIR /app
COPY frontend/package*.json ./
RUN npm install
COPY frontend/ ./
RUN npm run build# 运行 Nginx
FROM nginx:1.25-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
后端 (Spring Boot)
Dockerfile.backend (C:\full-stack\admin-react\Dockerfile.backend):
dockerfile
FROM maven:3.8.6-amazoncorretto-17 AS build
WORKDIR /app
COPY backend/pom.xml ./
COPY backend/src ./src
RUN mvn clean package -DskipTestsFROM amazoncorretto:17-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s CMD wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "/app.jar"]
步骤 3: Docker Compose
docker-compose.yml (C:\full-stack\admin-react\docker-compose.yml):
yaml
services:frontend:build:context: .dockerfile: Dockerfile.frontendports:- "80:80"depends_on:backend:condition: service_healthyrestart: alwayshealthcheck:test: ["CMD", "curl", "-f", "http://localhost"]interval: 30stimeout: 3sretries: 3backend:build:context: .dockerfile: Dockerfile.backendports:- "8080:8080"environment:- SERVER_PORT=8080- DB_HOST=db- DB_PORT=3306- DB_NAME=mydatabase- DB_USERNAME=root- DB_PASSWORD=passworddepends_on:db:condition: service_healthyrestart: alwayshealthcheck:test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/actuator/health"]interval: 30stimeout: 3sretries: 3db:image: mysql:8.0ports:- "3306:3306"environment:- MYSQL_ROOT_PASSWORD=password- MYSQL_DATABASE=mydatabasevolumes:- db-data:/var/lib/mysql- ./mysql/db.sql:/docker-entrypoint-initdb.d/db.sqlrestart: alwayshealthcheck:test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]interval: 10stimeout: 5sretries: 5volumes:db-data:
- 关键点:
- 移除 image: my-app-frontend:latest 和 image: my-app-backend:latest,直接用 build 指令,Docker Compose 自动构建镜像,无需手动 docker build。
- frontend 暴露 80 端口,浏览器访问 http://localhost。
- backend 不暴露端口,Nginx 通过 proxy_pass http://backend:8080/api/ 访问(backend 是服务名)。
- db 保留 db.sql 初始化。
步骤 4: 本地部署与访问
- 运行 Docker Compose:
bash
cd C:\full-stack\admin-react docker-compose up --build
- --build 强制构建 frontend 和 backend 镜像(从 Dockerfile.frontend 和 Dockerfile.backend)。
- 自动拉取 mysql:8.0(若网络问题,参考下方调试)。
- 浏览器访问:
- 打开浏览器,输入:
text
http://localhost
- Nginx serve React 页面(/usr/share/nginx/html)。
- API 请求(/api/)代理到后端。
- 如果 80 端口被占用,修改 docker-compose.yml 的 frontend 端口(e.g., 8080:80),访问 http://localhost:8080。
- 打开浏览器,输入:
- 验证:
- 检查容器:docker-compose ps(确保 frontend, backend, db 状态为 Up)。
- 查看日志:
bash
docker logs admin-react-frontend-1 docker logs admin-react-backend-1 docker logs admin-react-db-1
- 确认 MySQL 初始化:
bash
docker exec -it admin-react-db-1 mysql -uroot -ppassword -e "use mydatabase; show tables;"
步骤 5: 云部署
推送镜像(可选)
如果云部署需要预构建镜像:
- 构建:
bash
docker-compose build
- 打标签并推送:
bash
docker tag admin-react-frontend yourusername/my-app-frontend:latest docker tag admin-react-backend yourusername/my-app-backend:latest docker push yourusername/my-app-frontend:latest docker push yourusername/my-app-backend:latest
AWS ECS 示例
- 推送镜像到 ECR(若不推送,可在云端用 docker-compose.yml 构建)。
- Task Definitions:
- Frontend:
json
{"containerDefinitions": [{"name": "frontend","image": "your-ecr-repo/my-app-frontend:latest","portMappings": [{ "containerPort": 80 }],"dependsOn": [{ "containerName": "backend", "condition": "HEALTHY" }]}] }
- Backend:
json
{"containerDefinitions": [{"name": "backend","image": "your-ecr-repo/my-app-backend:latest","environment": [{ "name": "SERVER_PORT", "value": "8080" },{ "name": "DB_HOST", "value": "your-rds-endpoint" },{ "name": "DB_PORT", "value": "3306" },{ "name": "DB_NAME", "value": "mydatabase" },{ "name": "DB_USERNAME", "value": "admin" }],"secrets": [{ "name": "DB_PASSWORD", "valueFrom": "arn:aws:ssm:region:account-id:parameter/db-password" }]}] }
- Frontend:
- Service:Fargate,ALB 绑定到 frontend 的 80 端口。
- RDS:手动运行 db.sql 初始化。
其他云(GKE/AKS)
- 用 Kubernetes Deployment/Service,frontend 暴露 80 端口(LoadBalancer)。
- 数据库:Cloud SQL 或 Azure MySQL,手动初始化 db.sql。
步骤 6: 调试与注意事项
- 无法访问 http://localhost:
- 检查端口:netstat -aon | findstr :80.
- 确认 Nginx 日志:docker logs admin-react-frontend-1.
- 确保 frontend/dist 构建成功:dir frontend\dist.
- API 请求失败:
- 验证 Nginx proxy_pass http://backend:8080/api/(backend 是服务名)。
- 检查后端日志:docker logs admin-react-backend-1.
- MySQL 初始化失败:
- 确认 db.sql 语法:docker exec -it admin-react-db-1 mysql -uroot -ppassword -e "use mydatabase; show tables;".
- 检查 MyBatis 连接:后端日志是否有 SQL 错误。
- Docker Hub 拉取失败(mysql:8.0):
- 配置镜像源(中国用户):
json
// C:\ProgramData\Docker\config\daemon.json {"registry-mirrors": ["https://registry.docker-cn.com"] }
- 重启 Docker:net stop com.docker.service && net start com.docker.service.
- 重试:docker-compose up --build.
- 配置镜像源(中国用户):
- 配置修改:
- 数据库名/密码:改 docker-compose.yml 或云 env vars 的 DB_NAME、DB_PASSWORD.
- 端口:改 frontend 的 ports(e.g., 8080:80)。
总结
- 改进:移除手动 docker build,Docker Compose 的 build 指令自动构建 frontend 和 backend 镜像。前端由 Nginx 提供,省去复制 dist 的步骤。
- 访问:运行 docker-compose up --build,浏览器访问 http://localhost(或 http://localhost:8080 如果改端口)。
- 云部署:推送镜像或直接用 docker-compose.yml 构建,RDS 初始化 db.sql。
- 零错误:健康检查、IF NOT EXISTS、日志调试确保无误。
如果遇到问题(e.g., 页面空白、API 404),分享 docker logs 输出或错误信息,我可进一步协助!