容器化部署若依Vue全家桶:Docker Compose实战指南

在传统服务器部署中,开发者往往需要花费大量时间在环境配置、依赖安装和服务调优上。每次部署新环境,都要重复执行相同的步骤:安装JDK、配置MySQL、编译Redis、调整Nginx参数...这不仅效率低下,还容易因环境差异导致"在我机器上能跑"的经典问题。而容器化技术正是解决这一痛点的银弹——通过Docker Compose,我们可以将若依Vue项目及其全部依赖(Java/MySQL/Redis/Nginx)打包成可移植的标准化单元,实现一键部署、版本可控、环境一致的现代化运维体验。

1. 容器化部署的优势与架构设计

1.1 传统部署 vs 容器化部署对比

让我们通过一个典型场景来理解两种部署方式的本质区别。假设团队中有三位开发者:

  • 开发者A使用CentOS 7.9手动部署,耗时4小时完成全部环境搭建
  • 开发者B在Ubuntu 20.04上部署,因系统差异遇到3个兼容性问题
  • 开发者C使用Docker Compose,15分钟完成全栈服务启动

这种效率差异主要源于两种部署模式的根本不同:

对比维度传统部署Docker Compose部署
环境一致性依赖系统环境,易出现差异完全隔离,环境原子化
部署时间小时级分钟级
依赖管理需手动解决依赖冲突镜像自带完整依赖链
迁移成本需重新配置文件拷贝即可
资源占用直接使用系统资源有约10%的容器化开销
调试复杂度直接访问系统进程需进入容器环境

1.2 若依Vue的容器化架构

若依Vue全家桶的容器化部署需要协调多个服务:

用户请求 → Nginx容器 → Vue静态资源
           ↓
           Nginx反向代理 → RuoYi-Java容器(8080)
                          ↓
                          MySQL容器(3306)
                          ↓  
                          Redis容器(6379)

这种架构下,每个服务运行在独立的容器中,通过Docker网络互联。关键设计要点包括:

  • 网络隔离:所有容器共享自定义网络,避免端口冲突
  • 数据持久化:MySQL数据、Redis持久化文件、Nginx配置等需要volume挂载
  • 启动顺序:定义depends_on确保数据库先于应用启动
  • 资源限制:为各容器配置合理的CPU/Memory限制

提示:生产环境建议将MySQL和Redis这类有状态服务部署在容器编排平台(如Kubernetes)或独立服务器,本文方案更适合开发和测试环境。

2. 环境准备与Docker配置

2.1 基础环境要求

开始前请确保宿主机满足:

  • 任何Linux发行版(推荐CentOS 7+/Ubuntu 18.04+)
  • Docker Engine ≥ 20.10.14
  • Docker Compose ≥ 2.5.1
  • 至少4GB内存(建议8GB)
  • 20GB可用磁盘空间

验证环境:

# 检查Docker版本
docker --version
# 检查Compose版本
docker compose version
# 查看系统资源
free -h && df -h

若未安装Docker,可通过官方脚本快速安装:

# 通用安装命令(需root权限)
curl -fsSL https://get.docker.com | sh
# 启动Docker并设置开机自启
systemctl enable --now docker
# 安装Compose插件
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.20.3/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose

2.2 项目结构规划

建议按以下结构组织部署文件:

/opt/ruoyi-vue
├── compose
│   ├── mysql
│   │   ├── conf.d        # MySQL自定义配置
│   │   └── initdb.d      # 初始化SQL脚本
│   ├── nginx
│   │   ├── conf.d        # Nginx站点配置
│   │   └── ssl           # SSL证书
│   └── redis
│       └── redis.conf    # Redis配置文件
├── data
│   ├── mysql             # MySQL数据目录
│   └── redis             # Redis数据目录
├── frontend
│   └── dist              # Vue编译产物
└── backend
    └── ruoyi-admin.jar   # Java后端包

创建基础目录:

mkdir -p /opt/ruoyi-vue/{compose/{mysql/{conf.d,initdb.d},nginx/{conf.d,ssl},redis},data/{mysql,redis},frontend,backend}

3. Docker Compose文件详解

3.1 编写docker-compose.yml

创建主编排文件/opt/ruoyi-vue/docker-compose.yml

version: '3.8'

services:
  mysql:
    image: mysql:5.7
    container_name: ruoyi-mysql
    environment:
      MYSQL_ROOT_PASSWORD: RuoYi@123
      MYSQL_DATABASE: ry-vue
      TZ: Asia/Shanghai
    volumes:
      - ./data/mysql:/var/lib/mysql
      - ./compose/mysql/conf.d:/etc/mysql/conf.d
      - ./compose/mysql/initdb.d:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"
    networks:
      - ruoyi-net
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 5

  redis:
    image: redis:6.2-alpine
    container_name: ruoyi-redis
    command: redis-server --appendonly yes
    volumes:
      - ./data/redis:/data
      - ./compose/redis/redis.conf:/usr/local/etc/redis/redis.conf
    ports:
      - "6379:6379"
    networks:
      - ruoyi-net
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

  backend:
    image: openjdk:8-jdk
    container_name: ruoyi-backend
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - ./backend:/app
    working_dir: /app
    command: java -jar -Dspring.profiles.active=prod ruoyi-admin.jar
    ports:
      - "8080:8080"
    networks:
      - ruoyi-net
    restart: unless-stopped

  nginx:
    image: nginx:1.21-alpine
    container_name: ruoyi-nginx
    depends_on:
      backend:
        condition: service_started
    volumes:
      - ./frontend/dist:/usr/share/nginx/html
      - ./compose/nginx/conf.d:/etc/nginx/conf.d
    ports:
      - "80:80"
      - "443:443"
    networks:
      - ruoyi-net

networks:
  ruoyi-net:
    driver: bridge

3.2 关键配置解析

MySQL服务配置:

  • 使用官方MySQL 5.7镜像
  • 通过volume挂载数据目录和配置文件
  • 初始化脚本放在initdb.d目录(如quartz.sql和ry_20230223.sql)
  • 设置健康检查确保完全启动后再连接

Redis服务优化:

  • 选择Alpine版镜像减小体积
  • 启用AOF持久化模式
  • 自定义配置文件支持更多优化参数

Java后端服务:

  • 基于openjdk:8-jdk镜像
  • 通过depends_on确保依赖服务就绪
  • 设置restart策略实现故障自动恢复
  • 绑定后台JAR包到容器内

Nginx前端服务:

  • 挂载Vue编译后的dist目录
  • 自定义配置支持前后端分离架构
  • 暴露80/443端口支持HTTP/HTTPS

4. 服务配置与优化实践

4.1 MySQL性能调优

compose/mysql/conf.d/my-custom.cnf中添加:

[mysqld]
max_connections=500
innodb_buffer_pool_size=256M
innodb_log_file_size=128M
skip-name-resolve
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

初始化脚本示例compose/mysql/initdb.d/init.sql

CREATE USER 'ruoyi'@'%' IDENTIFIED BY 'RuoYi@123';
GRANT ALL PRIVILEGES ON `ry-vue`.* TO 'ruoyi'@'%';
FLUSH PRIVILEGES;

4.2 Redis安全配置

compose/redis/redis.conf关键参数:

bind 0.0.0.0
protected-mode yes
requirepass RuoYi@Redis123
maxmemory 256mb
maxmemory-policy allkeys-lru
appendonly yes

4.3 Nginx前端配置

创建compose/nginx/conf.d/ruoyi.conf

server {
    listen 80;
    server_name localhost;
    
    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
        index index.html index.htm;
    }

    location /prod-api/ {
        proxy_pass http://backend:8080/;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30s;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }

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

4.4 Java应用参数调优

修改backend服务的command部分:

command: >
  java -jar 
  -Xms512m -Xmx1024m 
  -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m 
  -Dspring.profiles.active=prod 
  -Dserver.tomcat.uri-encoding=UTF-8 
  ruoyi-admin.jar

5. 部署流程与日常运维

5.1 一键启动全栈服务

cd /opt/ruoyi-vue
# 构建并启动所有服务(-d表示后台运行)
docker compose up -d
# 查看服务状态
docker compose ps
# 查看实时日志
docker compose logs -f

5.2 常用运维命令

服务管理:

# 停止所有服务(保留数据)
docker compose stop
# 停止并删除容器(保留数据)
docker compose down
# 完全清理(包括匿名volume)
docker compose down -v
# 重启单个服务
docker compose restart backend

数据备份:

# MySQL备份
docker exec ruoyi-mysql sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" ry-vue' > backup.sql
# Redis备份
docker exec ruoyi-redis redis-cli -a RuoYi@Redis123 save
cp ./data/redis/dump.rdb ./redis-backup-$(date +%F).rdb

日志检查:

# 查看Java应用日志
docker compose logs backend
# 查看最近100行Nginx访问日志
docker exec ruoyi-nginx tail -100 /var/log/nginx/access.log
# 实时查看MySQL慢查询
docker exec -it ruoyi-mysql mysqladmin -uroot -pRuoYi@123 --sleep=2 processlist

5.3 版本更新策略

前端更新:

  1. 重新构建Vue项目生成dist目录
  2. 清空frontend/dist并复制新文件
  3. 重启Nginx服务
docker compose restart nginx

后端更新:

  1. 替换backend/ruoyi-admin.jar文件
  2. 重建backend容器
docker compose up -d --force-recreate backend

数据库迁移:

  1. 备份当前数据库
  2. 执行变更SQL脚本
  3. 验证数据一致性
docker exec -i ruoyi-mysql mysql -uroot -pRuoYi@123 ry-vue < alter.sql

6. 常见问题排查指南

6.1 容器启动失败排查

检查步骤:

  1. 查看容器日志
    docker compose logs service_name
    
  2. 检查端口冲突
    netstat -tulnp | grep -E '80|3306|6379|8080'
    
  3. 验证volume权限
    ls -l data/mysql
    

典型问题解决:

  • MySQL无法启动:通常由于volume权限问题,尝试:
    chown -R 999:999 data/mysql
    
  • Redis连接拒绝:检查requirepass是否与Java配置一致
  • Nginx 502错误:确认backend服务是否健康运行

6.2 性能优化建议

MySQL优化:

# 进入MySQL容器
docker exec -it ruoyi-mysql mysql -uroot -pRuoYi@123
# 查看运行状态
SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';

JVM内存分析:

# 获取Java进程PID
docker top ruoyi-backend
# 进入容器分析
docker exec -it ruoyi-backend bash
jstat -gcutil <pid> 1000 5

Nginx缓存配置:

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 30d;
    add_header Cache-Control "public, no-transform";
}

6.3 监控方案集成

基础监控命令:

# 查看容器资源使用
docker stats
# 查看服务健康状态
docker compose ps --filter "status=running"

Prometheus监控配置示例:

# 在docker-compose.yml中添加
monitor:
  image: prom/prometheus
  ports:
    - "9090:9090"
  volumes:
    - ./monitor/prometheus.yml:/etc/prometheus/prometheus.yml
  networks:
    - ruoyi-net

配套的prometheus.yml配置:

scrape_configs:
  - job_name: 'ruoyi-backend'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['backend:8080']
  - job_name: 'mysql'
    static_configs:
      - targets: ['mysql:9104']

7. 进阶:CI/CD集成与生产建议

7.1 自动化构建流程

结合GitHub Actions实现自动化部署:

# .github/workflows/deploy.yml
name: Deploy RuoYi-Vue

on:
  push:
    branches: [ master ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    
    - name: Build Frontend
      run: |
        cd ruoyi-ui
        npm install
        npm run build:prod
        tar czf ../frontend.tgz dist/
    
    - name: Build Backend
      run: |
        cd ruoyi
        mvn clean package -DskipTests
        cp target/ruoyi-admin.jar ../backend/
    
    - name: Deploy to Server
      uses: appleboy/scp-action@master
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USER }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        source: "frontend.tgz,backend/ruoyi-admin.jar"
        target: "/opt/ruoyi-vue"
    
    - name: Restart Services
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USER }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        script: |
          cd /opt/ruoyi-vue
          tar xzf frontend.tgz -C frontend/
          docker compose up -d --force-recreate nginx backend

7.2 生产环境安全加固

必要安全措施:

  1. 网络隔离

    networks:
      ruoyi-net:
        driver: bridge
        internal: true  # 禁止外部访问
    
  2. 最小权限原则

    mysql:
      environment:
        MYSQL_ROOT_PASSWORD: $DB_ROOT_PASS
        MYSQL_USER: ruoyi_prod
        MYSQL_PASSWORD: $DB_USER_PASS
    
  3. 定期备份策略

    # 每日备份脚本
    docker exec ruoyi-mysql mysqldump -u$DB_USER -p$DB_PASS ry-vue | gzip > /backups/mysql-$(date +%F).sql.gz
    
  4. 日志轮转配置

    nginx:
      logging:
        driver: "json-file"
        options:
          max-size: "10m"
          max-file: "3"
    

7.3 高可用架构建议

对于生产环境,建议采用:

  • 数据库集群:MySQL主从复制或Galera集群
  • Redis哨兵:配置Sentinel实现故障转移
  • Nginx负载均衡:多实例+Keepalived
  • 应用水平扩展:后端服务多实例部署

示例扩展配置:

backend:
  image: openjdk:8-jdk
  deploy:
    replicas: 3
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
    interval: 10s
    timeout: 5s
    retries: 3

8. 技术决策与替代方案

8.1 为什么选择Docker Compose

与Kubernetes等方案相比,Docker Compose的优势在于:

  • 学习曲线平缓:YAML语法简单直观
  • 开发友好:本地调试更方便
  • 资源消耗低:不需要集群管理组件
  • 快速迭代:适合中小项目快速部署

8.2 镜像选择考量

在官方镜像基础上,我们做了如下技术选型:

  1. MySQL 5.7:若依官方兼容版本,平衡功能与稳定性
  2. Redis Alpine版:小体积适合内存敏感场景
  3. OpenJDK 8:项目原始编译环境,避免兼容问题
  4. Nginx Alpine:足够轻量且功能完整

8.3 可能的技术变体

根据实际需求,可考虑以下变体方案:

  • 使用PostgreSQL替代MySQL:修改数据库配置和连接驱动
  • 前端使用Node.js SSR:调整Nginx配置支持服务端渲染
  • 集成ELK日志系统:增加Filebeat收集容器日志
  • 加入监控告警:集成Grafana+Alertmanager

配置示例:

elk:
  image: sebp/elk
  ports:
    - "5601:5601"
    - "9200:9200"
    - "5044:5044"
  volumes:
    - ./elk-data:/var/lib/elasticsearch
  networks:
    - ruoyi-net

9. 经验分享与踩坑记录

在实际部署中,有几个关键点需要特别注意:

  1. 时区问题:所有容器必须统一时区,否则可能导致日志时间错乱、定时任务异常。建议在docker-compose.yml中全局设置:

    environment:
      TZ: Asia/Shanghai
    
  2. 文件权限:Linux主机与容器之间的文件读写可能出现权限冲突。特别是MySQL数据目录,容器内默认使用mysql用户(UID 999)运行,需要确保挂载目录可写:

    chown -R 999:999 data/mysql
    
  3. 内存限制:Java应用在容器中需要显式设置堆内存参数,否则会尝试使用全部主机内存。推荐配置:

    deploy:
      resources:
        limits:
          memory: 2G
    
  4. 健康检查:服务间的依赖关系需要合理设置健康检查,避免启动顺序问题。MySQL的健康检查应该这样配置:

    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 5
    
  5. 网络延迟:在首次启动时,后端服务连接数据库可能会因网络初始化延迟而失败。解决方法是在应用中加入重试逻辑,或在command中添加等待脚本:

    command: >
      bash -c "
      while ! nc -z mysql 3306; do 
        echo 'Waiting for MySQL...'; 
        sleep 2; 
      done; 
      java -jar ruoyi-admin.jar
      "
    

10. 扩展思考:从Compose到云原生

虽然Docker Compose解决了单机部署问题,但当需要扩展到多节点或生产环境时,可以考虑:

  1. Docker Swarm:内置集群功能,兼容Compose文件格式

    docker swarm init
    docker stack deploy -c docker-compose.yml ruoyi
    
  2. Kubernetes:使用kompose工具转换:

    kompose convert -f docker-compose.yml
    kubectl apply -f .
    
  3. 云服务集成

    • AWS ECS with Fargate
    • Azure Container Instances
    • 阿里云容器服务

转换后的Kubernetes部署示例:

# deployment-backend.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ruoyi-backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ruoyi-backend
  template:
    metadata:
      labels:
        app: ruoyi-backend
    spec:
      containers:
      - name: backend
        image: openjdk:8-jdk
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        resources:
          limits:
            memory: "1Gi"
            cpu: "500m"

无论选择哪种进阶方案,基于Docker Compose的部署经验都将成为重要的基础。这种容器化的思维方式,正是现代DevOps实践的核心所在。

更多推荐