文章目录
- 前言
- 一、更小的jdk基础镜像
- 二、服务瘦包(thin jar)
- 2.1 maven
- 2.2 修改dockerfile
- 2.3 container run options
- 三、 基础jdk镜像入手?
- 总结
前言
idea docker 内网应用实践遗留问题
- idea docker插件 build 服务镜像太慢
- 服务镜像太大
一、更小的jdk基础镜像
精简的jdk镜像 123MB,jdk17-alpine
二、服务瘦包(thin jar)
打包成瘦包(thin jar)在许多情况下都有明显的优势,尤其是在企业级和分布式部署环境中。以下是瘦包的一些主要好处:
-
减小文件尺寸: 瘦包仅包含应用本身的代码和必需的资源,不包含外部依赖。这通常会导致文件尺寸比包含所有依赖的胖包(fat jar)小得多,这使得上传到服务器或存储在仓库中更快速、更高效。
-
快速构建: 在构建过程中,由于不需要打包所有的依赖,构建瘦包通常比构建胖包要快。
-
部署灵活性: 瘦包允许在部署时选择性地添加依赖。你可以根据不同的环境(开发、测试、生产)来管理依赖,这意味着可以有针对性地解决依赖性问题。
-
共享依赖: 如果多个应用部署在同一服务器上,它们可以共享相同的依赖库,减少重复的依赖文件,节省空间。
-
依赖管理: 瘦包使得依赖管理更加明确。所有的外部依赖都在构建或部署过程中被解决,这样可以更容易地处理依赖之间的冲突。
-
更新和维护: 如果需要更新或替换某个依赖,你可以单独更新那个依赖,而不需要重新构建整个应用的胖包。
-
资源占用: 对于基于微服务的架构,使用瘦包可以在微服务之间共享公共的运行时环境,减少了每个服务实例的资源占用。
-
安全性: 在有严格安全要求的环境中,使用瘦包可以更容易地进行安全审计,因为你可以精确地知道哪些库是运行时必需的。
-
与容器技术协同: 在使用容器技术(如 Docker)时,瘦包可以轻松结合基础镜像中的依赖,便于创建轻量级的容器镜像。
然而,瘦包也有它的缺点,例如可能需要额外的依赖管理工具或处理依赖传递的复杂性,因此在决定使用瘦包时,需要根据项目的具体需求和部署环境来权衡利弊。
2.1 maven
plugins 添加maven打包策略
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.12.RELEASE</version>
<configuration>
<mainClass>com.cuzue.file.FileServerApplication</mainClass>
<layout>ZIP</layout>
<!-- 打增量包时需要includes部分, 要打全量包删除includes -->
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>../lib</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<stripVersion>false</stripVersion>
<!--complie和runtime的包都打到lib中,否则可能lib缺包-->
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
2.2 修改dockerfile
主要修改:
- 外部lib 容器卷挂载
- JVM使用/dev/urandom作为熵源来加快随机数的生成速度
-Djava.security.egd=file:/dev/./urandom
FROM jdk-alpine:17
LABEL maintainer="cuzue 991446772@qq.com"
VOLUME ["/tmp","/mnt/upload","/server/lib"]
ADD target/*.jar file.jar
ENV SERVER_PORT="8000" XMS="1024m" XMX="1024m" NACOS_CONFIG_ADDR="nacos.server:8848" NACOS_CONFIG_SPA="intelligence-gsdss" NACOS_DISCOVERY_ADDR="nacos.server:8848" NACOS_DISCOVERY_SPA="intelligence-gsdss" MAIN_CLASS="com.cuzue.file.FileServerApplication"
ENTRYPOINT java -server -Xms$XMS -Xmx$XMX -Dserver.port=$SERVER_PORT -Dloader.path=/server/lib/ -Djava.security.egd=file:/dev/./urandom -Duser.timezone=Asia/Shanghai -Dspring.profiles.active=dev -Dspring.cloud.nacos.config.server-addr=$NACOS_CONFIG_ADDR -Dspring.cloud.nacos.config.namespace=$NACOS_CONFIG_SPA -Dspring.cloud.nacos.discovery.server-addr=$NACOS_DISCOVERY_ADDR -Dspring.cloud.nacos.discovery.namespace=$NACOS_DISCOVERY_SPA -jar file.jar $MAIN_CLASS
2.3 container run options
- 上传服务package生成的lib包到宿主机
/data/server/app/lib
- docker容器启动指定容器卷
-v /data/server/app/lib:/server/lib
容器卷指定路径,宿主机/data/server/app/lib
容器路径/server/lib
三、 基础jdk镜像入手?
下一步思路多阶段构建或更好的方案,没有具体实践,不知道怎么弄,有经验的小伙伴希望可以分享一下可行性高的方案
打包镜像时不将jdk基础镜像打包到每一个服务中,类似挂载jdk容器卷可以实现,但是这种方式并不是Docker的最佳实践,且可能会导致预期之外的问题,特别是在使用不同版本的JDK或涉及到JDK内部路径变动时。
GPT的建议:
在Docker中,如果你想要避免将JDK基础镜像打包到每一个服务的镜像中,可以采用以下几种常见的方法:
-
使用多阶段构建(Multi-stage build):
多阶段构建允许你在一个Dockerfile中使用多个FROM语句。你可以在一个阶段使用包含JDK的基础镜像来编译和构建应用,然后在第二个阶段只将编译后的应用复制到一个不含JDK的轻量级基础镜像中。例如:# 第一阶段:构建 FROM maven:3.6.3-jdk-11 AS builder WORKDIR /app COPY src /app/src COPY pom.xml /app RUN mvn clean package # 第二阶段:创建最终镜像 FROM openjdk:11-jre-slim COPY --from=builder /app/target/my-app.jar /app/ CMD ["java", "-jar", "/app/my-app.jar"]
这样做的好处是你的最终镜像大小减小,因为它不包含整个JDK,只有JRE。
-
使用JRE基础镜像:
如果你的应用不需要JDK中的编译工具,而只需要运行Java字节码,你可以直接使用JRE基础镜像而不是JDK。这可以减少镜像大小并缩短下载时间。 -
创建自定义基础镜像:
你可以创建一个自定义的基础镜像,其中包含了JDK和你的应用通用的其他依赖。然后,你可以在构建每个服务的Docker镜像时使用这个自定义镜像作为基础。 -
JDK资源共享:
如果你的多个服务部署在同一台机器上,可以考虑在Docker主机上安装JDK,并通过卷(volume)共享的方式让不同的容器使用同一个JDK。 -
使用精简版JDK发行版:
对于需要JDK环境的服务,可以考虑使用精简版的JDK发行版,如AdoptOpenJDK的OpenJ9、Alibaba的Dragonwell或者Amazon的Corretto等。这些发行版往往提供了更小的容器镜像。
请注意,选择哪种方法取决于你的具体需求和约束。多阶段构建是最常见的做法,因为它提供了灵活性和优化的最终镜像大小。不过,如果你的服务确实需要JDK来运行,例如它们使用JDK特定的工具或API,那么这种情况下你可能无法避免在每个服务镜像中包含JDK。
总结
GPT的建议:
如果你在使用 IntelliJ IDEA 配合 Docker 进行镜像构建时遇到速度慢的问题,可以尝试以下几种方法来改善这个情况:
- 优化 Dockerfile:
- 减少镜像层数:每条
RUN
、COPY
或ADD
指令都会创建一个新的镜像层。通过组合指令来减少层数可以减少构建时间。 - 使用
.dockerignore
文件:排除不必要的文件和目录被复制到 Docker 上下文中,减少构建时间和镜像大小。 - 利用构建缓存:确保更改频繁的指令放在 Dockerfile 的底部,这样可以最大化地利用 Docker 缓存。
- 提升硬件性能:
- Docker 镜像构建过程中可能会占用大量的 CPU 和内存资源,确保你的开发机器有足够的资源分配给 Docker。
- 如果使用的是虚拟机或远程 Docker 守护进程,提高网络速度和分配给虚拟机的资源可能有帮助。
- 使用更快的网络连接:
- 镜像构建过程中可能需要下载基础镜像和依赖,网络速度的快慢直接影响到下载速度。
- 设置 Docker 镜像加速器:
- 很多地区通过 Docker 官方镜像源下载镜像可能会比较慢,使用国内的镜像加速服务可以显著提高下载速度。
- 减少构建上下文大小:
- Docker 在构建镜像时,会将当前目录(即构建上下文)发送到 Docker daemon,如果当前目录很大,会导致构建过程缓慢。减少构建上下文的大小可以提高速度。
- 分阶段构建(Multi-stage builds):
- 如果你的应用构建过程包括编译等多个步骤,可以使用分阶段构建来减少最终镜像的大小,从而减少上传到仓库的时间。
- 预先拉取基础镜像:
- 如果构建过程需要使用到某些特定的基础镜像,提前手动拉取这些镜像可以省去构建过程中的下载时间。
- 关闭 IDE 的不必要插件或服务:
- IntelliJ IDEA 中运行的其他后台任务可能会影响到 Docker 镜像构建的性能,尝试关闭不需要的插件或服务。
- 定期清理 Docker:
- 定期使用
docker system prune
清理无用的 Docker 对象(例如悬空的镜像和停止的容器),可以释放磁盘空间,有时也能提高构建速度。
检查这些方面,并根据你的具体情况进行调整,应该能够帮助你提高在 IntelliJ IDEA 中使用 Docker 打包镜像的速度。如果这些方法都不奏效,可能需要更详细地分析构建过程中的瓶颈。