告别手动部署:用Docker Compose高效构建JeecgBoot单体应用

在当今快节奏的开发环境中,时间就是最宝贵的资源。想象一下这样的场景:你刚刚完成了一个基于JeecgBoot的单体应用开发,正准备部署到服务器上。按照传统方式,你需要手动安装MySQL、Redis、Nginx,配置各种环境变量,处理服务间的网络连接,还要确保数据持久化不会丢失。这个过程不仅耗时耗力,而且容易出错,一旦需要迁移服务器,所有工作又得重来一遍。

这就是为什么越来越多的开发者转向Docker Compose的原因。它不仅仅是一个工具,更是一种工程化的思维方式——将部署流程代码化、版本化、自动化。通过一个简单的YAML文件,你就能定义整个应用的拓扑结构、服务依赖和资源配置,实现一键部署和可重复的构建过程。

1. 为什么选择Docker Compose部署JeecgBoot

传统手动部署JeecgBoot项目通常需要经历以下繁琐步骤:

  1. 在服务器上安装并配置MySQL数据库
  2. 安装Redis并设置密码和持久化
  3. 配置Nginx作为反向代理和静态资源服务器
  4. 部署后端Java应用并确保它能连接到数据库和Redis
  5. 处理各种环境变量和配置文件
  6. 设置数据备份和持久化方案

这种方式的痛点显而易见:

  • 环境不一致:开发、测试、生产环境配置差异导致"在我机器上能运行"的问题
  • 部署效率低:每次部署都需要重复相同的操作,无法自动化
  • 维护困难:服务间依赖关系不明确,故障排查困难
  • 可移植性差:迁移到新服务器需要重新配置所有服务

相比之下,Docker Compose方案具有显著优势:

对比维度 传统部署 Docker Compose部署
部署时间 1-2小时 5-10分钟
环境一致性
配置管理 分散在各处 集中在一个文件
可重复性 依赖人工操作 完全自动化
扩展性 困难 轻松扩展服务实例

技术选型考量:对于JeecgBoot这样的单体应用,Docker Compose提供了恰到好处的抽象层级——比纯Docker更结构化,又比Kubernetes更轻量。它特别适合中小型项目、开发测试环境以及需要快速迭代的场景。

2. 项目结构与Docker Compose设计

一个典型的JeecgBoot单体应用包含以下核心组件:

  • 前端:基于Vue.js的Web界面,通常打包为静态文件
  • 后端:Spring Boot应用,提供RESTful API
  • 数据库:MySQL,存储业务数据
  • 缓存:Redis,用于会话管理和缓存
  • Web服务器:Nginx,作为反向代理和静态文件服务器

我们的Docker Compose设计需要解决几个关键问题:

  1. 服务依赖:确保服务按正确顺序启动(数据库→Redis→后端→前端)
  2. 网络通信:让容器间能相互访问,同时对外暴露必要端口
  3. 数据持久化:确保数据库和Redis数据不会随容器销毁而丢失
  4. 配置管理:集中管理环境变量和配置文件
  5. 资源限制:合理分配CPU和内存资源

推荐的项目目录结构如下:

jeecgboot-docker/
├── docker-compose.yml      # Compose主配置文件
├── backend/
│   ├── Dockerfile          # 后端应用Dockerfile
│   └── application.yml     # 应用配置文件
├── mysql/
│   ├── conf/               # MySQL自定义配置
│   └── data/               # 数据库数据目录
├── redis/
│   ├── redis.conf          # Redis配置文件
│   └── data/               # Redis数据目录
└── nginx/
    ├── nginx.conf          # Nginx主配置
    └── html/               # 前端静态文件

这种结构将所有Docker相关配置与源代码分离,既保持了项目整洁,又便于版本控制和管理。

3. 编写docker-compose.yml文件

下面是一个完整的docker-compose.yml示例,我们分段解析关键配置:

version: '3.8'

services:
  mysql:
    image: mysql:5.7
    container_name: jeecg-mysql
    environment:
      MYSQL_ROOT_PASSWORD: jeecg123456
      MYSQL_DATABASE: jeecg_boot
      TZ: Asia/Shanghai
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/conf:/etc/mysql/conf.d
    ports:
      - "3306:3306"
    networks:
      - jeecg-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 10s
      retries: 3
    restart: unless-stopped

  redis:
    image: redis:6.2-alpine
    container_name: jeecg-redis
    command: redis-server --requirepass jeecg123456 --appendonly yes
    volumes:
      - ./redis/data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    ports:
      - "6379:6379"
    networks:
      - jeecg-network
    restart: unless-stopped

  backend:
    build: ./backend
    container_name: jeecg-backend
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - TZ=Asia/Shanghai
    volumes:
      - ./backend/application.yml:/app/config/application.yml
    ports:
      - "8080:8080"
    networks:
      - jeecg-network
    restart: unless-stopped

  nginx:
    image: nginx:1.21-alpine
    container_name: jeecg-nginx
    depends_on:
      - backend
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/html:/usr/share/nginx/html
    ports:
      - "80:80"
      - "443:443"
    networks:
      - jeecg-network
    restart: unless-stopped

networks:
  jeecg-network:
    driver: bridge

关键配置解析

  1. 版本声明:使用较新的3.8版本以获得最新功能
  2. 网络配置
    • 创建专用网络jeecg-network确保容器间安全通信
    • 避免使用默认的host网络模式,提高安全性
  3. 健康检查
    • MySQL服务添加健康检查,确保后端应用只在数据库就绪后启动
    • 使用depends_on条件控制服务启动顺序
  4. 数据持久化
    • MySQL数据目录映射到宿主机./mysql/data
    • Redis启用AOF持久化并映射数据目录
  5. 资源配置
    • 为生产环境设置时区(Asia/Shanghai
    • 使用restart: unless-stopped确保服务异常退出后自动重启

提示:对于生产环境,建议在MySQL和Redis配置中添加资源限制(如mem_limit),避免单个服务占用过多系统资源。

4. 服务配置与优化技巧

4.1 MySQL优化配置

mysql/conf/my.cnf中添加以下优化配置:

[mysqld]
lower_case_table_names=1
innodb_buffer_pool_size=256M
max_connections=200
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
default-time-zone='+8:00'

参数说明

  • lower_case_table_names=1:解决Linux下MySQL表名大小写敏感问题
  • innodb_buffer_pool_size:根据服务器内存调整,通常设为物理内存的50-70%
  • max_connections:根据应用需求设置合理连接数

4.2 Redis生产级配置

redis/redis.conf关键配置:

bind 0.0.0.0
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo no
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes

4.3 Nginx高性能配置

nginx/nginx.conf优化建议:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
    use epoll;
    multi_accept on;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    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;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    keepalive_timeout  65;
    types_hash_max_size 2048;

    gzip  on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80;
        server_name  localhost;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

        location /jeecg-boot {
            proxy_pass http://backend:8080/jeecg-boot;
            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_read_timeout 600s;
            proxy_send_timeout 600s;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
}

4.4 后端应用Dockerfile

backend/Dockerfile最佳实践:

# 使用多阶段构建减小镜像体积
FROM eclipse-temurin:17-jdk-jammy as builder
WORKDIR /app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY src ./src
RUN ./mvnw package -DskipTests

# 生产阶段
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
COPY ./config/application.yml ./config/application.yml

# 设置JVM参数
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=4 -XX:ConcGCThreads=2 -XX:+HeapDumpOnOutOfMemoryError"

# 创建非root用户运行
RUN useradd -m myuser && chown -R myuser:myuser /app
USER myuser

EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app/app.jar --spring.config.location=/app/config/application.yml"]

优化要点

  • 使用多阶段构建减少最终镜像体积
  • 采用非root用户运行增强安全性
  • 配置合理的JVM参数优化性能
  • 将配置文件外挂便于修改

5. 部署流程与日常运维

5.1 一键部署与启动

准备好所有配置文件后,部署变得极其简单:

# 克隆项目(如果尚未克隆)
git clone https://gitee.com/jeecg/jeecg-boot.git
cd jeecg-boot

# 构建并启动所有服务
docker-compose up -d --build

# 查看服务状态
docker-compose ps

# 查看实时日志
docker-compose logs -f backend

5.2 常用运维命令

服务管理

# 停止所有服务
docker-compose down

# 重启特定服务(如后端)
docker-compose restart backend

# 更新代码后重新构建
docker-compose up -d --build backend

数据备份

# 备份MySQL数据
docker exec jeecg-mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > backup.sql

# 备份Redis数据
docker cp jeecg-redis:/data/. ./redis-backup

日志查看

# 查看后端最近100行日志
docker-compose logs --tail=100 backend

# 跟踪Nginx访问日志
docker-compose logs -f nginx | grep "GET"

5.3 监控与调优

基础监控命令

# 查看容器资源使用情况
docker stats

# 检查服务健康状态
docker inspect --format='{{json .State.Health}}' jeecg-mysql

性能调优建议

  1. 数据库优化

    • 定期执行ANALYZE TABLE更新统计信息
    • 监控慢查询日志并优化相应SQL
    • 考虑添加适当的索引
  2. JVM调优

    • 根据实际负载调整-Xms-Xmx参数
    • 使用G1垃圾收集器平衡吞吐量和延迟
    • 启用GC日志分析性能瓶颈
  3. 前端优化

    • 配置Nginx启用Brotli压缩
    • 设置合理的缓存头减少重复请求
    • 考虑使用CDN分发静态资源

在实际项目中,我们发现最大的性能提升往往来自于合理的数据库设计和索引优化。例如,JeecgBoot的权限表关联较多,确保这些关联查询有合适的索引可以显著提高系统响应速度。

更多推荐