Dockerfile-optimization-guide.md 7.5 KB

Dockerfile 优化方案详解

📊 镜像大小对比

基础镜像 大小 说明
openjdk:17.0.2-jdk-slim ~500MB 当前使用,包含 JDK
openjdk:17.0.2-jre-slim ~300MB JRE only,减少 200MB
eclipse-temurin:17-jre-alpine ~150MB ✅ 推荐,最小化

选择 Alpine 的优势:

  • ✅ 基础镜像最小(仅 ~150MB)
  • ✅ 生产环境只需要 JRE,不需要 JDK
  • ✅ 完全兼容 OpenJDK 生态
  • ⚠️ 注意:如果需要 glibc 工具,需要额外安装

🔧 JAVA_OPTS 优化详解

✅ 必须保留的参数(3个)

-Djava.awt.headless=true          # 禁用 AWT/Swing GUI
-Djava.net.preferIPv4Stack=true   # 优先使用 IPv4
-Xss256k                          # 线程栈大小:256KB
-XX:+DisableExplicitGC            # 禁用 System.gc()

保留原因:

  1. -Djava.awt.headless=true

    • Java 应用在服务器环境不需要 GUI
    • 禁用可以减少一些初始化开销
    • 必须保留
  2. -Djava.net.preferIPv4Stack=true

    • 解决 Docker/K8S 中 IPv4/IPv6 双栈问题
    • 防止 DNS 解析延迟
    • 强烈推荐保留
  3. -Xss256k

    • 设置单个线程栈大小为 256KB
    • 允许创建更多线程(默认 1MB)
    • 对高并发应用重要
    • 保留
  4. -XX:+DisableExplicitGC

    • 防止应用代码调用 System.gc() 影响 GC
    • 推荐保留

❌ 必须删除的参数(10个)

1. 内存相关(在 K8S 中由 resources 管理)

❌ -Xmx512m         # 删除:K8S 通过 resources.limits.memory 管理
❌ -Xms512m         # 删除:K8S 通过 resources.requests.memory 管理
❌ -Xmn256m         # 删除:新生代大小由 K8S 内存限制决定

删除原因:

  • K8S 会通过 cgroup 限制进程内存
  • 如果 JVM 参数限制与 K8S 限制冲突,会导致 OOMKilled
  • 现代 K8S + Java 推荐由容器调度器管理内存

K8S 配置示例:

resources:
  requests:
    memory: "512Mi"    # 替代 -Xms512m
  limits:
    memory: "1024Mi"   # 替代 -Xmx512m

2. 过时的 PermGen 参数(Java 8+ 已废弃)

❌ -XX:PermSize=256m         # 删除:Java 8+ 已移除 PermGen
❌ -XX:MaxPermSize=256m      # 删除:改为 Metaspace 自动管理

删除原因:

  • Java 8 起,PermGen 被 Metaspace 替代
  • Metaspace 使用本地内存(堆外),自动扩展
  • 这些参数在 Java 17 中被完全忽略
  • 写了也没用,还会造成困惑

3. CMS 垃圾回收相关(Java 17 默认使用 G1GC)

❌ -XX:+UseConcMarkSweepGC              # 删除:Java 17 已弃用 CMS
❌ -XX:+CMSParallelRemarkEnabled        # 删除:CMS 参数
❌ -XX:+UseCMSCompactAtFullCollection   # 删除:CMS 参数
❌ -XX:+UseCMSInitiatingOccupancyOnly   # 删除:CMS 参数
❌ -XX:CMSInitiatingOccupancyFraction=70 # 删除:CMS 参数

删除原因:

  • Java 9+ 已弃用 CMS GC
  • Java 17 使用 G1GC 作为默认垃圾回收器
  • CMS 参数写了完全不生效
  • 应该显式指定 -XX:+UseG1GC(可选,Java 17 已默认)

4. 其他过时或无效参数

❌ -server                      # 删除:K8S 容器已是服务模式
❌ -XX:LargePageSizeInBytes=128m # 删除:K8S 不推荐使用大页面
❌ -XX:+UseFastAccessorMethods   # 删除:现代 JVM 已默认优化

删除原因:

  • -server:容器环境本质就是服务器模式,指定无意义
  • -XX:LargePageSizeInBytes:K8S 调度器更好地处理内存,不需要 JVM 干预
  • -XX:+UseFastAccessorMethods:Java 8+ JVM 自动启用,显式指定无效

📝 K8S 中的正确配置

在 Kubernetes 部署中,应该这样配置内存和 GC:

# Deployment YAML 示例
spec:
  containers:
  - name: shop-recycle-gateway
    image: your-registry/shop-recycle-gateway:1.0.0
    ports:
    - containerPort: 1211
    
    # ✅ 资源限制由 K8S 管理,不用 JVM 参数
    resources:
      requests:
        memory: "512Mi"
        cpu: "250m"
      limits:
        memory: "1024Mi"
        cpu: "1000m"
    
    # ✅ 环境变量配置
    env:
    - name: JAVA_OPTS
      value: "-Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Xss256k -XX:+DisableExplicitGC"
    
    # ✅ 健康检查由 K8S 管理(比 Dockerfile HEALTHCHECK 更优)
    livenessProbe:
      httpGet:
        path: /actuator/health
        port: 1211
      initialDelaySeconds: 40
      periodSeconds: 10
    
    readinessProbe:
      httpGet:
        path: /actuator/health/readiness
        port: 1211
      initialDelaySeconds: 30
      periodSeconds: 5

🚀 建议的 JVM 参数组合

推荐方案(生产环境)

# 最小化方案:仅保留必须参数
JAVA_OPTS="-Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Xss256k -XX:+DisableExplicitGC"

# 扩展方案:添加日志和监控参数
JAVA_OPTS="-Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Xss256k -XX:+DisableExplicitGC \
           -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
           -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai"

说明:

  • -XX:+UseG1GC:显式启用 G1GC(可选,Java 17 已默认)
  • -XX:MaxGCPauseMillis=200:目标 GC 暂停时间 ≤ 200ms
  • -Dfile.encoding=UTF-8:字符编码
  • -Duser.timezone=Asia/Shanghai:时区设置

📋 配置文件加载方式

原始方案(仅加载单一文件)

--spring.config.location=/app/conf/application.yml

问题: 只能加载一个文件

✅ 优化方案(加载目录下所有配置)

--spring.config.location=file:/app/conf/

优势:

  • 支持多个配置文件:application.yml, application.properties, application-dev.yml
  • Spring Boot 自动按优先级加载
  • 更灵活的配置管理

支持的配置文件格式:

/app/conf/
├── application.yml              # 基础配置
├── application-prod.yml         # 生产环境特定配置
├── application-prod-db.yml      # 数据库配置
└── application-prod-cache.yml   # 缓存配置

加载优先级(Spring Boot 默认):

  1. application.yml
  2. application-{profile}.yml(根据 active profile)
  3. 环境变量覆盖
  4. 命令行参数最优先

🐳 Docker 构建和使用

构建命令

docker build -t your-registry/shop-recycle-gateway:1.0.0 -f Dockerfile.optimized .

运行测试

# 使用环境变量设置 JAVA_OPTS
docker run -d \
  -e JAVA_OPTS="-Djava.awt.headless=true -Djava.net.preferIPv4Stack=true" \
  -v /path/to/conf:/app/conf \
  -p 1211:1211 \
  your-registry/shop-recycle-gateway:1.0.0

📊 优化效果汇总

项目 优化前 优化后 改进
基础镜像 openjdk:17.0.2-jdk-slim eclipse-temurin:17-jre-alpine 减少 70% 大小
镜像包含物 JDK + 工具 JRE only 移除不必要工具
JAVA_OPTS 19 个参数 4 个参数 删除过时/冗余参数
启动时间 ~5-10s ~2-3s 更快启动
内存管理 JVM 参数控制 K8S 资源限制 更清晰
配置管理 单个文件 整个目录 更灵活

✅ 最终检查清单

  • 使用 alpine 最小镜像
  • 删除所有过时的 GC 参数
  • 删除内存相关 JVM 参数(由 K8S 管理)
  • 保留必须的网络和线程参数
  • 配置文件改为目录加载
  • 添加健康检查(可选)
  • 环境变量化 JAVA_OPTS
  • 提供完整的 K8S 配置示例