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 输出或错误信息,我可进一步协助!

相关推荐
遇见火星5 小时前
Docker入门:快速部署你的第一个Web应用
前端·docker·容器
浪飘7 小时前
k8s device plugin
java·docker·kubernetes
冬夜戏雪7 小时前
milvus容器restart不成功,但docker仍在running问题排查
docker·容器·milvus
m0_579146658 小时前
docker desktop的容器间通信
docker·容器·php
key_Go8 小时前
12.docker swarm
运维·docker·容器·docker swarm
码农阿豪10 小时前
一个浏览器多人用?Docker+Neko+cpolar实现跨网共享
运维·docker·容器
梁辰兴12 小时前
企业培训笔记:外卖平台后端--套餐管理模块--回显套餐信息
笔记·vue·mybatis·springboot·外卖管理系统
m0_5791466512 小时前
docker desktop创建ollama容器端口绑定失败
运维·docker·容器
愚昧之山绝望之谷开悟之坡12 小时前
docker和docker compose离线安装-2-报错
运维·docker·容器