最新公告
  • 新注册用户请前往个人中心绑定邮箱以便接收相关凭证邮件!!!点击前往个人中心
  • 不要在 Docker 镜像中使用 Fat Jar

    Docker 容器中存放 fat jar 是一种对存储空间、带宽和时间的浪费。幸运的是,可以利用 Docker 镜像分层和 registry cache 实现增量构建和小型 artifact。例如,可以把新建 artifact 的大小从75MB缩减到只有1MB!最好有一个 Maven 和 Gradle 插件可以把这些搞定。

     

     

    摘要 

     

    • 通常,一个 fat jar 包含的所有依赖在不同的 release 之间不会改变。但是,这些依赖项会反复拷贝到每个 fat jar 中,浪费了存储空间、带宽和时间。
    • 例如,某个 Spring Boot 应用的 fat jar 大小为72 MB,其中只包含2MB代码。通常,只有代码会发生改变。
    • 幸运的是,可以利用 Docker 镜像分层技术:通过把依赖和资源放在不同的层级可以实现重用,并且实现只对 artifact、release 更新代码。
    • Jib插件可以方便地实现上述功能,支持 Maven 和 Gradle。无需手动编写 Dockerfile。

     

    问题:Fat Jar中的依赖 

     

    Docker 的分层机制功能非常强大。如果所有应用使用相同 base image(例如 openjdk:11.0.4-jre-slim),那么 Docker 会复用 OS 层和 JRE 层。在 Docker registry 中保存内容,由于传输的内容更少(Docker 只传输 registry 中新注册的 layer),可以加快 registry 上传和下载速度。

     

    糟糕的是,许多应用并没有充分利用这种强大的机制,因为它们在 Docker 容器中使用了 fat jar。

     

    每个 release 都会创建一个新的 Docker layer 大小72MB。

     

    假设 Spring Boot 应用打包为一个 fat jar。这个 fat jar 大小为72MB,被添加到 Dockerfile 的最后一行。这意味着每个新 release 会占用72MB存储空间,并且会上传到 registry 然后下载。

     

    现在,仔细看一下72MB的内容:

     

    fat jar 的内容,其中大部分很少更改,但是会被反复拷贝到每个 artifact 中。

     

    一个 fat jar 包含三部分:

     

    • 依赖项:大部分内容为 library 且很少变化。大多数情况下,创建 release 只修改代码,不涉及依赖项。尽管如此,依赖项仍然会被拷贝到每个 release 中。
    • 资源:基本上与上面的问题类似。尽管资源(像 HTML、CSS、图片、配置文件等)改变的频率比依赖项高,但是不会受到代码修改的影响。资源在每个 release 中也会重复。
    • 代码:虽然代码只占 fat jar 很小一部分(300KB – 2MB),但却是修改最频繁的部分。

     

    因此,通常一个新的 release 代码修改只有几MB。每个 artiface 会重复拷贝所有资源和依赖项,这是对存储空间、带宽和时间的浪费。

     

    如果为每个 commit 创建唯一、可部署的 artifact (使用 git commit hash 作为 artifact 的版本号),浪费会更加严重。对持续交付来说这种方式很有意义,但由于每次 commit 都会额外占用72MB内存,消耗的存储空间越来越大。

     

    有哪些工具可以用来分析 docker 镜像,把 fat jar 在 docker 镜像中的影响可视化?Dive 和 docker history

     

    Dive是一个交互式命令行工具,可以显示是一个交互式命令行工具,可以展示 fat jar layer。

     

    docker history 也可以展示 fat jar layer:

    ~ ❯❯❯ docker history registry.domain.com/neptune:latest

    IMAGE CREATED CREATED BY SIZE

    44e77fa110e5 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

    解决方案:为依赖、资源和代码使用不同 layer

     

    所幸可以利用 Docker 的分层机制,类似操作系统和 JRE 层。为依赖、资源和代码引入不同的 layer 进行拓展,然后根据变化频率对 layer 进行排序。

     

    按照依赖、资源和代码把应用程序拆分成三个不同的 Docker layer。这样 release 只占2MB而不是之前的72MB。

     

    现在,由于可以重复利用资源和依赖,如果创建的 release 只包含代码更改,只需要2MB存储空间。这些资源和依赖已经存储在 registry 中,不需要重复提交。

     

    使用Google Jib插件实现 

     

    好消息:不需要为 Java 应用程序手动编写 Dockerfile,使用 Google Jib 即可。Jib 插件让 Java 容器化更容易,支持 Maven 和 Gradle。这篇 Google 博客介绍了 Jib,其中最重要的一个特性:Jib 会扫描 Java 项目并为依赖、资源和代码创建不同的 layer。这种开箱即用的形式很棒。

     

    实现步骤:

     

    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>

    <!– optional: create a tag based on the git commit id(via the git-commit-id plugin): –>

    <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)用法

     

    # 执行完整构建,然后把镜像推送到 registry

    mvn package

    # 只创建并推送镜像

    mvn jib:build

    # 注意 `jib:build` 没有守护进程,不会在机器上创建镜像

    # 直接与 registry 交互使用 `docker pull` 获取已创建的镜像

    # 只通过 Docker daemon 创建和推送镜像

    mvn jib:dockerBuild

     

    3)好处Dive和码docker history充分展示了优秀的分层结构。

     

    使用 Jib 构建的 docker 镜像中依赖、资源和代码三个不同的 layer

     

    ~ ❯❯❯ docker history registry.domain.com/neptune:latest

    IMAGE CREATED CREATED BY SIZE COMMENT

    a759771eb008 49 years ago jib-maven-plugin:1.6.11.22MB classes

    <missing> 49 years ago jib-maven-plugin:1.6.1297kB resources

    <missing> 49 years ago jib-maven-plugin:1.6.164.6MB dependencies

    <missing> 8 months ago /bin/sh -c set -ex; ...217MB

    <missing> 8 months ago /bin/sh -c #(nop) ADD...55.3MB

     

    Clean-Up(可选) 

     

    Clean-up 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>

    <!– 注意,这里不支持 skip flag

    解决方法: 将 default-jar 绑定到一个不存在的 phase 执行–>

    <executions>

    <execution>

    <id>default-jar</id>

    <phase>none</phase>

    </execution>

    </executions>

    </plugin>

    Clean-up 2)如果使用 Spring Boot,需要移除 spring-boot-maven-plugin。不需要再创建一个 fat jar。

     

    运行部署相关配置 

     

    Jib 支持在 pom.xml 中配置 JVM flag 和程序参数,但是,通常我们不想在构建时设置,而是根据部署环境(本地、QA、生产)进行配置。可以在这里设置 Spring 配置和 JVM 堆大小。

     

    • JVM flag:使用 JAVA_TOOL_OPTIONS 环境变量添加 heap size 这样的 JVM flag。
    • 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 \

    <span style=”color: rgb(171, 178, 191); font-family: Consolas, Menlo, Courier, monospace; font-weight: 400;”–<spring.config.additional-location=/app-config.yml

    在看

    本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
    极客文库 » 不要在 Docker 镜像中使用 Fat Jar

    常见问题FAQ

    如果资源链接失效了怎么办?
    本站用户分享的所有资源都有自动备份机制,如果资源链接失效,请联系本站客服QQ:2580505920更新资源地址。
    如果用户分享的资源与描述不符怎么办?
    可以联系客服QQ:2580505920,如果要求合理可以安排退款或者退赞助积分。
    如何分享个人资源获取赞助积分或其他奖励?
    本站用户可以分享自己的资源,但是必须保证资源没有侵权行为。点击个人中心,根据操作填写并上传即可。资源所获收益完全归属上传者,每周可申请提现一次。
    如果您发现了本资源有侵权行为怎么办?
    及时联系客服QQ:2580505920,核实予以删除。

    参与讨论

    • 125会员总数(位)
    • 3725资源总数(个)
    • 5本周发布(个)
    • 0 今日发布(个)
    • 295稳定运行(天)

    欢迎加入「极客文库」,成为原创作者从这里开始!

    立即加入 了解更多
    成为赞助用户享有更多特权立即升级