react+springboot的Docker部署

React + Vite 前端 + Spring Boot (Java 17, MyBatis) 后端 + MySQL 项目,提供一个完整的、零错误的部署方案。方案将:

  1. 使用 Docker Compose 的 build 指令直接构建前端(Nginx)和后端(Spring Boot)镜像,省去手动 docker build。
  2. 保留 MyBatis 配置和 ./mysql/db.sql:/docker-entrypoint-initdb.d/db.sql 初始化。
  3. 确保部署后直接通过浏览器访问 http://localhost(Nginx 默认 80 端口)。
  4. 支持云部署,通用且只需修改少量配置。

项目结构

假设项目结构如下(基于 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)
  1. 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',
      },
    });
  2. package.json (frontend/package.json):

    json

    复制代码
    "scripts": {
      "build": "vite build"
    }
后端 (Spring Boot + MyBatis)
  1. 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=true
        username: ${DB_USERNAME:root}
        password: ${DB_PASSWORD:password}
        driver-class-name: com.mysql.cj.jdbc.Driver
    
    mybatis:
      mapper-locations: classpath:mapper/*.xml  # 调整为您的 XML Mapper 路径(若无则移除)
      type-aliases-package: com.example.entity  # 调整为您的实体包路径
      configuration:
        map-underscore-to-camel-case: true
    
    logging:
      level:
        root: INFO
        com.example: DEBUG  # 调整为您的包路径
    • 注意:替换 com.example 为实际包路径。若使用 MyBatis 注解(@Mapper),移除 mapper-locations。
  2. 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>
  3. 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 防止重复执行错误。
  4. 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 -DskipTests

FROM 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.frontend
    ports:
      - "80:80"
    depends_on:
      backend:
        condition: service_healthy
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 3s
      retries: 3

  backend:
    build:
      context: .
      dockerfile: Dockerfile.backend
    ports:
      - "8080:8080"
    environment:
      - SERVER_PORT=8080
      - DB_HOST=db
      - DB_PORT=3306
      - DB_NAME=mydatabase
      - DB_USERNAME=root
      - DB_PASSWORD=password
    depends_on:
      db:
        condition: service_healthy
    restart: always
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 3

  db:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_DATABASE=mydatabase
    volumes:
      - db-data:/var/lib/mysql
      - ./mysql/db.sql:/docker-entrypoint-initdb.d/db.sql
    restart: always
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  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: 本地部署与访问

  1. 运行 Docker Compose

    bash

    复制代码
    cd C:\full-stack\admin-react
    docker-compose up --build
    • --build 强制构建 frontend 和 backend 镜像(从 Dockerfile.frontend 和 Dockerfile.backend)。
    • 自动拉取 mysql:8.0(若网络问题,参考下方调试)。
  2. 浏览器访问

    • 打开浏览器,输入: text

      复制代码
      http://localhost
      • Nginx serve React 页面(/usr/share/nginx/html)。
      • API 请求(/api/)代理到后端。
    • 如果 80 端口被占用,修改 docker-compose.yml 的 frontend 端口(e.g., 8080:80),访问 http://localhost:8080

  3. 验证

    • 检查容器: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: 云部署

推送镜像(可选)

如果云部署需要预构建镜像:

  1. 构建: bash

    复制代码
    docker-compose build
  2. 打标签并推送: 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 示例
  1. 推送镜像到 ECR(若不推送,可在云端用 docker-compose.yml 构建)。

  2. 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" }
            ]
          }
        ]
      }
  3. Service:Fargate,ALB 绑定到 frontend 的 80 端口。

  4. RDS:手动运行 db.sql 初始化。

其他云(GKE/AKS)
  • 用 Kubernetes Deployment/Service,frontend 暴露 80 端口(LoadBalancer)。
  • 数据库:Cloud SQL 或 Azure MySQL,手动初始化 db.sql。

步骤 6: 调试与注意事项

  1. 无法访问 http://localhost
    • 检查端口:netstat -aon | findstr :80.
    • 确认 Nginx 日志:docker logs admin-react-frontend-1.
    • 确保 frontend/dist 构建成功:dir frontend\dist.
  2. API 请求失败
  3. MySQL 初始化失败
    • 确认 db.sql 语法:docker exec -it admin-react-db-1 mysql -uroot -ppassword -e "use mydatabase; show tables;".
    • 检查 MyBatis 连接:后端日志是否有 SQL 错误。
  4. 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.

  5. 配置修改
    • 数据库名/密码:改 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 输出或错误信息,我可进一步协助!

相关推荐
计算机小手1 天前
一个带Web UI管理的轻量级高性能OpenAI模型代理网关,支持Docker快速部署
经验分享·docker·语言模型·开源软件
坚持学习前端日记1 天前
常见docker指令
docker·容器·eureka
小芳矶1 天前
Dify本地docker部署踩坑记录
python·docker·容器
70asunflower1 天前
阿里云无影云电脑 Docker 使用完全指南
阿里云·docker·云计算
feasibility.1 天前
在OpenCode使用skills搭建基于LLM的dify工作流
人工智能·低代码·docker·ollama·skills·opencode·智能体/工作流
lpruoyu1 天前
【Docker进阶-02】Docker命令
docker·容器
FLGB1 天前
Docker网段和服务器内部网段172.17 网段冲突导致网络不通
服务器·网络·docker
long3161 天前
K‘ 未排序数组中的最小/最大元素 |期望线性时间
java·算法·排序算法·springboot·sorting algorithm
骂我的人都死了1 天前
DevOps架构部署
运维·ubuntu·docker·k8s·github·devops·python3.11
AL3171 天前
模拟实现NetDevOps全生命周期自动化网络运维
运维·docker·centos·ensp·netmiko