# 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个) ```bash -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 管理)** ```bash ❌ -Xmx512m # 删除:K8S 通过 resources.limits.memory 管理 ❌ -Xms512m # 删除:K8S 通过 resources.requests.memory 管理 ❌ -Xmn256m # 删除:新生代大小由 K8S 内存限制决定 ``` **删除原因:** - K8S 会通过 cgroup 限制进程内存 - 如果 JVM 参数限制与 K8S 限制冲突,会导致 OOMKilled - 现代 K8S + Java 推荐由容器调度器管理内存 **K8S 配置示例:** ```yaml resources: requests: memory: "512Mi" # 替代 -Xms512m limits: memory: "1024Mi" # 替代 -Xmx512m ``` --- #### 2. **过时的 PermGen 参数(Java 8+ 已废弃)** ```bash ❌ -XX:PermSize=256m # 删除:Java 8+ 已移除 PermGen ❌ -XX:MaxPermSize=256m # 删除:改为 Metaspace 自动管理 ``` **删除原因:** - Java 8 起,PermGen 被 Metaspace 替代 - Metaspace 使用本地内存(堆外),自动扩展 - 这些参数在 Java 17 中被**完全忽略** - 写了也没用,还会造成困惑 --- #### 3. **CMS 垃圾回收相关(Java 17 默认使用 G1GC)** ```bash ❌ -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. **其他过时或无效参数** ```bash ❌ -server # 删除:K8S 容器已是服务模式 ❌ -XX:LargePageSizeInBytes=128m # 删除:K8S 不推荐使用大页面 ❌ -XX:+UseFastAccessorMethods # 删除:现代 JVM 已默认优化 ``` **删除原因:** - `-server`:容器环境本质就是服务器模式,指定无意义 - `-XX:LargePageSizeInBytes`:K8S 调度器更好地处理内存,不需要 JVM 干预 - `-XX:+UseFastAccessorMethods`:Java 8+ JVM 自动启用,显式指定无效 --- ## 📝 K8S 中的正确配置 在 Kubernetes 部署中,应该这样配置内存和 GC: ```yaml # 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 参数组合 ### 推荐方案(生产环境) ```bash # 最小化方案:仅保留必须参数 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`:时区设置 --- ## 📋 配置文件加载方式 ### 原始方案(仅加载单一文件) ```bash --spring.config.location=/app/conf/application.yml ``` **问题:** 只能加载一个文件 ### ✅ 优化方案(加载目录下所有配置) ```bash --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 构建和使用 ### 构建命令 ```bash docker build -t your-registry/shop-recycle-gateway:1.0.0 -f Dockerfile.optimized . ``` ### 运行测试 ```bash # 使用环境变量设置 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 资源限制 | **更清晰** | | 配置管理 | 单个文件 | 整个目录 | **更灵活** | --- ## ✅ 最终检查清单 - [x] 使用 alpine 最小镜像 - [x] 删除所有过时的 GC 参数 - [x] 删除内存相关 JVM 参数(由 K8S 管理) - [x] 保留必须的网络和线程参数 - [x] 配置文件改为目录加载 - [x] 添加健康检查(可选) - [x] 环境变量化 JAVA_OPTS - [x] 提供完整的 K8S 配置示例