← 返回首页

用 Docker 标准化部署 Spring 应用:镜像优化与资源控制

本文介绍如何使用 Docker 容器化部署 Spring Boot 应用,包括多阶段构建、镜像优化、资源限制和生产环境最佳实践。

1. 基础 Dockerfile 编写

创建一个标准的 Spring Boot 应用 Dockerfile:

# 基础镜像选择
FROM openjdk:17-jdk-slim

# 设置工作目录
WORKDIR /app

# 复制 JAR 文件
COPY target/*.jar app.jar

# 暴露端口
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]

2. 多阶段构建优化

使用多阶段构建减少镜像大小,分离构建和运行环境:

# 构建阶段
FROM maven:3.8.6-openjdk-17 AS builder

WORKDIR /app
COPY pom.xml .
COPY src ./src

# 构建应用
RUN mvn clean package -DskipTests

# 运行阶段
FROM openjdk:17-jre-slim

WORKDIR /app

# 创建非 root 用户
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 复制构建产物
COPY --from=builder /app/target/*.jar app.jar

# 更改文件所有者
RUN chown appuser:appuser app.jar

# 切换到非 root 用户
USER appuser

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

3. JVM 参数优化

针对容器环境优化 JVM 参数:

# 优化的启动命令
ENTRYPOINT ["java", \
    "-XX:+UseContainerSupport", \
    "-XX:MaxRAMPercentage=75.0", \
    "-XX:+UseG1GC", \
    "-XX:+UseStringDeduplication", \
    "-Djava.security.egd=file:/dev/./urandom", \
    "-jar", "app.jar"]
参数说明:

4. Docker Compose 配置

使用 Docker Compose 管理应用和依赖服务:

version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/myapp
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=password
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '1.0'
        reservations:
          memory: 512M
          cpus: '0.5'
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_DATABASE=myapp
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  mysql_data:

5. 镜像分层优化

优化 Dockerfile 分层结构,提高构建缓存效率:

FROM openjdk:17-jre-slim

WORKDIR /app

# 先复制依赖文件(变化较少)
COPY target/lib ./lib/

# 再复制应用 JAR(变化较多)
COPY target/*.jar app.jar

# 设置启动脚本
COPY docker-entrypoint.sh .
RUN chmod +x docker-entrypoint.sh

EXPOSE 8080
ENTRYPOINT ["./docker-entrypoint.sh"]

6. 启动脚本优化

创建灵活的启动脚本 docker-entrypoint.sh

#!/bin/bash
set -e

# 等待数据库就绪
if [ -n "$DB_HOST" ]; then
    echo "Waiting for database..."
    while ! nc -z $DB_HOST ${DB_PORT:-3306}; do
        sleep 1
    done
    echo "Database is ready!"
fi

# 动态 JVM 参数
JAVA_OPTS="${JAVA_OPTS} -XX:+UseContainerSupport"

# 根据容器内存动态设置堆大小
if [ -n "$MEMORY_LIMIT" ]; then
    JAVA_OPTS="${JAVA_OPTS} -Xmx${MEMORY_LIMIT}"
else
    JAVA_OPTS="${JAVA_OPTS} -XX:MaxRAMPercentage=75.0"
fi

# 启动应用
exec java $JAVA_OPTS -jar app.jar "$@"

7. 生产环境部署

生产环境的完整部署配置:

# docker-compose.prod.yml
version: '3.8'

services:
  app:
    image: myapp:latest
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - JAVA_OPTS=-XX:+UseContainerSupport -XX:MaxRAMPercentage=70.0
    volumes:
      - ./logs:/app/logs
      - ./config:/app/config
    restart: unless-stopped
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 2G
          cpus: '2.0'
        reservations:
          memory: 1G
          cpus: '1.0'
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "3"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - app
    restart: unless-stopped

8. 监控与日志

集成监控和日志收集:

  # 添加到 docker-compose.yml
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  grafana_data:

9. 常用运维命令

# 构建镜像
docker build -t myapp:latest .

# 运行容器
docker-compose up -d

# 查看日志
docker-compose logs -f app

# 扩容服务
docker-compose up -d --scale app=3

# 滚动更新
docker-compose pull app
docker-compose up -d --no-deps app

# 备份数据
docker exec mysql_container mysqldump -u root -p myapp > backup.sql

# 监控资源使用
docker stats

# 清理无用镜像
docker system prune -a

10. 故障排查

常见问题:

11. 安全最佳实践

# 安全扫描
docker scan myapp:latest

# 使用 secrets
echo "db_password" | docker secret create db_password -

以上配置提供了完整的 Docker 化部署方案,可根据具体项目需求进行调整和优化。