阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

不要把大型 JAR 包放进 Docker 镜像

  • 2019-10-29
  • 本文字数:5284 字

    阅读完需:约 17 分钟

不要把大型JAR包放进Docker镜像

Docker容器里放大型 JAR 包是对存储空间、网络带宽和时间的一种浪费。所幸的是,我们可以利用 Docker 镜像的分层机制和注册表缓存来创建增量式的小工件,甚至可以将一个工件的大小从 75MB 减到 1MB。值得一提的是,现在有一个 Maven 和 Gradle 插件可以处理这些事情。


问题:大型 JAR 包里的依赖项

Docker镜像的分层机制非常强大。如果你所有的应用程序都使用了相同的基础镜像(比如 openjdk:11.0.4-jre-slim),Docker 重用了 OS 和 JRE 的一些层,这样就可以节省 Docker 注册表的存储空间,上传和下载镜像的速度也更快了,因为只需要传输更少的文件量(Docker 只会将新的层传输到注册表中)。


可惜的是,很多应用程序并没有很好地利用这个强大的机制,因为他们把大型的 JAR 包塞进了 Docker 镜像里。



每个新版本都会创建一个 72MB 的新层


我们假设将一个 Spring Boot 应用程序打成一个大型的 JAR 包。这个 JAR 包有 72MB,并在 Dockerfile 的最后一行将它加到镜像里。也就是说,每个新版本都需要 72MB 的存储空间,而且要上传到注册表中,或则从注册表中下载下来。


现在来看一下这个 72MB 的镜像:



一个大型的 JAR 包,大部分东西很少发生改动,但每次都会被拷贝到新工件中


一个大型的 JAR 包包含三个部分:


  • 依赖项:它们占了很大的一部分比例,但很少发生改动。大多数时候我们只会修改我们的代码,很少会去修改依赖项。但依赖项每次都会被拷贝到发布版本中。

  • 资源文件:这个问题跟依赖项差不多。虽然资源文件(HTML、CSS、图像、配置文件,等等)比依赖项更经常发生改动,但比起代码还是相对少一些。它们也会被拷贝到发布版本中。

  • 代码:代码只占 JAR 包很小的一部分(通常 300KB 到 2MB),但会经常发生改动。


经常发生改动的代码只有几 MB,但每次都需要拷贝所有的依赖项和资源文件,这是对存储空间、带宽和时间的一种浪费。


如果需要为每个 git 提交创建一个可部署的镜像,那浪费的空间就更多了。持续交付可能需要这么做,但这样做浪费了大量的空间,因为每一次提交都会占用额外的 72MB 空间。


有哪些有用的工具可用来分析 Docker 镜像和可视化大型 JAR 对 Docker 镜像的影响?是dive和 docker history。



交互式命令行工具 dive 可用来显示 JAR 包层


docker history 命令也可以用来显示 JAR 包层:


~ ❯❯❯ docker history registry.domain.com/neptune:latestIMAGE           CREATED          CREATED BY                     SIZE44e77fa110e5    2 minutes ago    /bin/sh -c #(nop) COPY dir:…   65.5MB ...<missing>       8 months ago     /bin/sh -c set -ex;   if [ …   217MB...<missing>       8 months ago     /bin/sh -c #(nop) ADD file:…   55.3MB
复制代码

解决方案:依赖项、资源文件和代码放在不同的层

所幸的是,我们可以利用 Docker 镜像的分层机制,就像已经分好的 OS 和 JRE 层那样。我们更进一步,引入了依赖项层、资源文件层和代码层。我们还按照改动的频繁程度来安排这些层的次序。



将应用程序分到依赖项、资源和代码三个层。常规发布版本现在只需要拷贝 2MB 的文件,而不是 72MB


现在,如果新版本只包含代码层的改动,就只需要 2MB 的存储空间,因为我们可以重用依赖项层和资源层。它们在注册表中已经有了,不需要再次上传。

谷歌的 Jib 插件

实际上,我们不需要手动编写 Dockerfile,我们可以使用谷歌的Jib插件。Jib 是一个 Maven 或 Gradle 插件,用于简化 Java 应用程序镜像的打包过程。对于我们来说,Jib 最重要的一个特性是,它会扫描我们的 Java 项目,并为依赖项、资源文件和代码创建不同的层。


使用步骤:


(1)在 pom.xml 中添加插件配置:


<plugin>    <groupId>com.google.cloud.tools</groupId>    <artifactId>jib-maven-plugin</artifactId>    <version>1.6.1</version>    <configuration>        <from>            <image>openjdk:11.0.4-jre-slim</image>        </from>        <to>            <image>domain.com/${project.artifactId}:latest</image>            <!-- 可选项: 基于git提交创建标签 (通过git-commit-id插件): -->            <tags>                <tag>${git.commit.id}</tag>            </tags>        </to>        <container>            <jvmFlags>                <jvmFlag>-server</jvmFlag>            </jvmFlags>        </container>    </configuration>    <executions>        <execution>            <id>build-and-push-docker-image</id>            <phase>package</phase>            <goals>                <goal>build</goal>            </goals>        </execution>    </executions></plugin>
复制代码


(2)使用


# 执行整个build生命周期,并将镜像推送到注册表中mvn package
# 只是创建和推送镜像mvn jib:build# 注意,`jib:build`运行在前台,而且不会在本地创建镜像# 直接与注册表交互。可以使用`docker pull`拉取创建好的镜像
# 通过Docker后台创建和推送镜像mvn jib:dockerBuild
复制代码


(3)使用 dive 和 docker history 显示层结构。



使用 Jib 创建的镜像,镜像中包含了依赖项、资源和代码层


~ ❯❯❯ docker history registry.domain.com/neptune:latestIMAGE          CREATED         CREATED BY                SIZE     COMMENTa759771eb008   49 years ago    jib-maven-plugin:1.6.1    1.22MB   classes<missing>      49 years ago    jib-maven-plugin:1.6.1    297kB    resources<missing>      49 years ago    jib-maven-plugin:1.6.1    64.6MB   dependencies...                         <missing>      8 months ago    /bin/sh -c set -ex; ...   217MB               ...<missing>      8 months ago    /bin/sh -c #(nop) ADD...  55.3MB
复制代码

清理(可选)

清理 1)禁用 maven-deploy-plugin、maven-install-plugin 和 maven-jar-plugin。这些步骤不需要了,即使程序员敲入 mvn deploy 也不会执行这些步骤。


<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-deploy-plugin</artifactId>    <configuration>        <skip>true</skip>    </configuration></plugin><plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-install-plugin</artifactId>    <configuration>        <skip>true</skip>    </configuration></plugin><plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-jar-plugin</artifactId>    <!-- 可惜的是,这里不支持开关。    临时解决方案: 将default-jar绑定到一个不存在的phase -->    <executions>        <execution>            <id>default-jar</id>            <phase>none</phase>        </execution>    </executions></plugin>
复制代码


清理 2)如果使用了 Spring Boot,可以移除 spring-boot-maven-plugin,现在不再需要创建大型 JAR 包了。

使用部署配置

我们可以在 pom.xml 中配置 Jib 的 JVM 和程序参数,但通常不会这么做。相反,我们会根据部署环境(比如本地环境、QA 环境、生产环境)来配置这些参数。我们通过这种方式来配置 Spring 参数和 JVM 堆大小。


  • JVM 参数:我们使用环境变量 JAVA_TOOL_OPTIONS 来添加 JVM 参数,比如堆大小。

  • Spring 配置:我们将外部配置文件挂载到 Docker 容器,并将它的路径作为一个程序参数传递给命令行。当然,你也可以使用环境变量。


docker run -p 1309:1309 --net=host \-e JAVA_TOOL_OPTIONS='-Xms1000M -Xmx1000M' \-v /home/phauer/dev/app/app-config.yml:/app-config.yml \registry.domain.com/app:latest \--spring.config.additional-location=/app-config.yml
复制代码


原文链接:


https://phauer.com/2019/no-fat-jar-in-docker-image/


2019-10-29 10:129776

评论

发布
暂无评论
发现更多内容

动态修改coreThread线程池拓展

FunTester

系统稳定性与高可用保障

得物技术

架构 高可用 稳定性

java多线程总结

Studying_swz

6 月 优质更文活动

初步了解RNN, Seq2Seq, Attention注意力机制

Studying_swz

6 月 优质更文活动

C语言编程语法—文件读写

智趣匠

C语言 二进制 6 月 优质更文活动

以安全为底线 共迎机遇和挑战|2023开放原子全球开源峰会可信基础设施技术分论坛即将启幕

开放原子开源基金会

开源 开放原子全球开源峰会 可信基础设施技术

设计与实现阶段的可靠性工作

阿泽🧸

6 月 优质更文活动

推动开源与商业共生共赢 | 2023开放原子全球开源峰会开源商业化创新发展分论坛即将启幕

开放原子开源基金会

开源 开源商业化 开放原子全球开源峰会 开放原子

AWS CodeWhisperer 上手初体验安装与使用

宇宙之一粟

Python 代码编辑工具 CodeWhisperer 6 月 优质更文活动

【Netty】「NIO」(一)认识 ByteBuffer

sidiot

后端 Netty java‘ 6 月 优质更文活动

2023-06-06:给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。 对位于 (row, col) 的每个结点而言, 其左右子结点分别位于 (row + 1, col -

福大大架构师每日一题

golang rust 福大大

地图实火!断货加印,限时折扣抢购通道开启

融云 RongCloud

社交 融云 泛娱乐 出海 wicc

Vue-事件基本使用

不觉心动

6 月 优质更文活动

2个原因解答:为什么网络安全缺口大,招聘却很少?

网络安全学海

黑客 网络安全 信息安全 渗透测试 WEB安全

最近几年,国内好多家实体企业都开始用上低代码了,它有什么好?

优秀

低代码 数字化

OpenYurt 即将亮相 EdgeX+OpenVINO 开发者生态大会

阿里巴巴云原生

阿里云 开源 云原生

整合开源治理经验,共谋开源社区发展|2023开放原子全球开源峰会开源社区治理与运营分论坛即将启幕

开放原子开源基金会

开源 开放原子全球开源峰会 开源社区治理与运营

Web智慧化工三维可视化管理系统

2D3D前端可视化开发

三维可视化 工业组态 物联网系统 数字孪生技术 智慧化工

如何用Smartproxy住宅代理IP抢购潮牌鞋子?住宅代理抢购限量款式

摘星星的猫

自动驾驶≠速度与激情

白洞计划

自动驾驶

自动驾驶≠速度与激情

脑极体

自动驾驶

Docker里的基础术语

穿过生命散发芬芳

Docker 6 月 优质更文活动

华为云发布面向消费终端的企业云原生白皮书,开辟移动时代的云原生路径

脑极体

云原生

以AI为灯,照亮医疗放射防护监管盲区

飞桨PaddlePaddle

人工智能 深度学习 百度飞桨

浅谈低代码平台的开发优势

这我可不懂

低代码 应用开发 JNPF

从 PMO 的视角,看如何从 0 到 1 搭建研发效能体系?

思码逸研发效能

研发效能

推动体系建设 助推融合发展|2023开放原子全球开源峰会软件物料清单(SBOM)分论坛即将启幕

开放原子开源基金会

开源 开放原子全球开源峰会 软件物料清单(SBOM)

软件测试/测试开发丨学习笔记之Mark标记测试用例

测试人

程序员 软件测试 自动化测试 测试开发 测试用例

奇点云举办“数据进化论”数智科技大会,发布数据云七大场景

奇点云

业务场景 奇点云 数智科技大会

设计模式总结(二):结构型模式

Studying_swz

6 月 优质更文活动

轻量级分布式日志追踪-Tlog快速入门

javalover123

分布式 日志 Skywalking spring-boot Tlog

不要把大型JAR包放进Docker镜像_文化 & 方法_Philipp Hauer_InfoQ精选文章