“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

不要把大型 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:129747

评论

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

IO 模型

无心

Netty

主流分布式文件系统总结

跳蚤

ES6中的新特性:Iterables和iterators

程序那些事

nodejs ES6 程序那些事

树集合总结

我是程序员小贱

3月日更

寻找被遗忘的勇气(二十一)

Changing Lin

3月日更

从Nacos客户端视角来分析配置中心原理

麦洛

nacos SpringCloud Alibaba 动态配置

传统IT部门为什么越来越不受欢迎?

boshi

数字化转型 IT职场 七日更

思呓(2)

型火🔥

学习 架构 分布式 解耦

找到适合自己的睡眠方案

石云升

生活方式 28天写作 睡眠 3月日更

线上问题的一点反思

风翱

复盘 3月日更 线上问题

mybatis 添加日志功能

xiezhr

mybatis 日志

javax.imageio.IIOException: Unsupported Image Type

wjchenge

Gradle无法访问Nexus私服仓库-offline

wjchenge

数据仓库设计

大数据技术指南

大数据 28天写作 3月日更

谈兼职创业

Ryan Zheng

一文搞定Diff算法

执鸢者

Vue 大前端 Diff

《Redis 核心技术与实战》学习笔记 06

escray

redis 学习 28天写作 3月日更 Redis 核心技术与实战

产品经理面试常见问题总结2

lenka

3月日更

在你所在的公司(行业、领域),正在用大数据处理哪些业务?可以用大数据实现哪些价值?

跳蚤

Wireshark数据包分析学习笔记Day18

穿过生命散发芬芳

Wireshark 数据包分析 3月日更

登陆用户身份获取

程序员架构进阶

架构设计 认证授权 28天写作 3月日更

产品训练营第八周作业——用户路径地图

innovator琳

用户研究 用户地图 用户数据 用户模型

用户故事拆分速查手册(译)

Bruce Talk

敏捷 译文 Agile User Story

How to Connect 2 Cisco Switches Together

心在飞

深入剖析 | Java16语法特性

九叔(高翔龙)

Java 架构

《青春有你3》的子弹时间舞台,凝筑了自由视角技术进化史

脑极体

产品0期-第九周

Jxin

uni-app跨端开发H5、小程序、IOS、Android(五):uni-app数据绑定

黑马腾云

html5 微信小程序 uni-app iOS Developer 3月日更

央行数字人民币“可控匿名”会侵犯隐私吗?最新解读来了

CECBC

数字货币

开源与商业产品

ES_her0

3月日更

第11周课后练习-安全稳定

潘涛

架构师训练营 4 期

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