最小化 Java 镜像的常用技巧

背景

随着容器技术的普及,越来越多的应用被容器化。人们使用容器的频率越来越高,但常常忽略一个基本但又非常重要的问题 - 容器镜像的体积。本文将介绍精简容器镜像的必要性并以基于 spring boot 的 java 应用为例描述最小化容器镜像的常用技巧。

精简容器镜像的必要性

精简容器镜像是非常必要的,下面分别从安全性和敏捷性两个角度进行阐释。

安全性

基于安全方面的考虑,将不必要的组件从镜像中移除可以减少攻击面、降低安全风险。虽然 docker 支持用户通过 Seccomp 限制容器内可以执行操作或者使用 AppArmor 为容器配置安全策略,但它们的使用门槛较高,要求用户具备安全领域的专业素养。

敏捷性

精简的容器镜像能提高容器的部署速度。假设某一时刻访问流量激增,您需要通过增加容器副本数以应对突发压力。如果某些宿主机不包含目标镜像,需要先拉取镜像,然后启动容器,这时使用体积较小的镜像能加速这一过程、缩短扩容时间。另外,镜像体积越小,其构建速度也越快,同时还能减少存储和传输的成本。

常用技巧

将一个 java 应用容器化所需的步骤可归纳如下:

  1. 编译 java 源码并生成 jar 包。
  2. 将应用 jar 包和依赖的第三方 jar 包移动到合适的位置。

本章所用的样例是一个基于 spring boot 的 java 应用 spring-boot-docker,所用的未经优化的 dockerfile 如下:

FROM maven:3.5-jdk-8
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package
ENTRYPOINT ["java","-jar","/usr/src/app/target/spring-boot-docker-1.0.0.jar"]

由于应用使用 maven 构建,dockerfile 中指定maven:3.5-jdk-8作为基础镜像,该镜像的大小为 635MB。通过这种方式最终构建出的镜像非常大,达到了 719MB,这是因为一方面基础镜像本身就很大,另一方面 maven 在构建过程中会下载许多用于执行构建任务的 jar 包。

多阶段构建

Java 程序的运行只依赖 JRE,并不需要 maven 或者 JDK 中众多用于编译、调试、运行的工具,因此一个明显的优化方法是将用于编译构建 java 源码的镜像和用于运行 java 应用的镜像分开。为了达到这一目的,在 docker 17.05 版本之前需要用户维护 2 个 dockerfile 文件,这无疑增加了构建的复杂性。好在自 17.05 开始,docker 引入了多阶段构建的概念,它允许用户在一个 dockerfile 中使用多个 From 语句。每个 From 语句可以指定不同的基础镜像并将开启一个全新的构建流程。您可以选择性地将前一阶段的构建产物复制到另一个阶段,从而只将必要的内容保留在最终的镜像里。优化后的 dockerfile 如下:

FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean packageFROM openjdk:8-jre
ARG DEPENDENCY=/usr/src/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

该 dockerfile 选用maven:3.5-jdk-8作为第一阶段的构建镜像,选用openjdk:8-jre作为运行 java 应用的基础镜像并且只拷贝了第一阶段编译好的.claass文件和依赖的第三方 jar 包到最终的镜像里。通过这种方式优化后的镜像大小为 459MB。

使用 distroless 作为基础镜像

虽然通过多阶段构建能减小最终生成的镜像的大小,但 459MB 的体积仍相对过大。经调查发现,这是因为使用的基础镜像openjdk:8-jre体积过大,到达了 443MB,因此下一步的优化方向是减小基础镜像的体积。

Google 开源的项目 distroless 正是为了解决基础镜像体积过大这一问题。Distroless 镜像只包含应用程序及其运行时依赖项,不包含包管理器、shell 以及在标准 Linux 发行版中可以找到的任何其他程序。目前,distroless 为依赖 java、python、nodejs、dotnet 等环境的应用提供了基础镜像。

使用 distroless 的 dockerfile 如下:

FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean packageFROM gcr.io/distroless/java
ARG DEPENDENCY=/usr/src/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

该 dockerfile 和上一版的唯一区别在于将运行阶段依赖的基础镜像由openjdk:8-jre(443 MB)替换成了gcr.io/distroless/java(119 MB)。经过这一优化,最终镜像的大小为 135MB。

使用 distroless 的唯一不便是您无法 attach 到一个正在运行的容器上排查问题,因为镜像中不包含 shell。虽然 distroless 的 debug 镜像提供 busybox shell,但需要用户重新打包镜像、部署容器,对于那些已经基于非 debug 镜像部署的容器无济于事。 但从安全角度来看,无法 attach 容器并不完全是坏事,因为攻击者无法通过 shell 进行攻击。

使用 alpine 作为基础镜像

如果您确实有 attach 容器的需求,又希望最小化镜像的大小,可以选用 alpine 作为基础镜像。Alpine 镜像的特点是体积非常下,基础款镜像的体积仅 4 MB 左右。

使用 alpine 后的 dockerfile 如下:

FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean packageFROM openjdk:8-jre-alpine
ARG DEPENDENCY=/usr/src/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

这里并未直接继承基础款 alpine,而是选用从 alpine 构建出的包含 java 运行时的openjdk:8-jre-alpine(83MB)作为基础镜像。使用该 dockerfile 构建出的镜像体积为 99.2MB,比基于 distroless 的还要小。

执行命令docker exec -ti <container_id> sh可以成功 attach 到运行的容器中。

distroless vs alpine

既然 distroless 和 alpine 都能提供非常小的基础镜像,那么在生产环境中到底应该选择哪一种呢?如果安全性是您的首要考虑因素,建议选用 distroless,因为它唯一可运行的二进制文件就是您打包的应用;如果您更关注镜像的体积,可以选用 alpine。

其他技巧

除了可以通过上述技巧精简镜像外,还有以下方式:

  1. 将 dockerfile 中的多条指令合并成一条,通过减少镜像层数的方式达到精简镜像体积的目的。
  2. 将稳定且体积较大的内容置于镜像下层,将变动频繁且体积较小的内容置于镜像上层。虽然该方式无法直接精简镜像体积,但充分利用了镜像的缓存机制,同样可以达到加快镜像构建和容器部署的目的。

想了解更多优化 dockerfile 的小窍门可参考教程 Best practices for writing Dockerfiles。

总结

  1. 本文通过一系列的优化,将 java 应用的镜像体积由最初的 719MB 缩小到 100MB 左右。如果您的应用依赖其他环境,也可以用类似的原则进行优化。
  2. 针对 java 镜像,google 提供的另一款工具 jib 能为您屏蔽镜像构建过程中的复杂细节,自动构建出精简的 java 镜像。使用它您无须编写 dockerfile,甚至不需要安装 docker。
  3. 对于类似 distroless 这样无法 attach 或者不方便 attach 的容器,建议您将它们的日志中心化存储,以便问题的追踪和排查。具体方法可参考文章面向容器日志的技术实践。

 


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/520001.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux上传下载文件命令rz、sz

要使用rz、sz命令传输文件需要给服务器安装lrzsz: yum -y install lrzsz命令sz、rz的使用方法 rz中的r意为received(接收)&#xff0c;输入rz时&#xff0c;意为服务器接收文件&#xff0c;即将文件从本地上传到服务器。 sz中的s意为send(发送)&#xff0c;输入sz时&#xff…

linux block的含义,Block Prefetching含义

以下是翻译Christian Antognini的《Troubleshooting Oracle Performance》P422&#xff0c;关于block prefetching介绍&#xff1a;在正常情况下&#xff0c;每个基于单个数据块的处理(如rowid访问和Index range scan)&#xff0c;如果缓存中不存在该数据块&#xff0c;将导致单…

从概念到技术,打通「中台」的任督二脉,别再说不知道中台是什么

2019 年&#xff0c;「中台」这个词火了&#xff01;随着阿里等头部互联网企业搭建和推动中台业务&#xff0c;让越来越多的企业关注中台&#xff0c;纷纷提出「中台战略」&#xff0c;帮助企业自身加速实现数字化转型。不少企业还在观望「中台」&#xff1a;1、我的企业里需要…

Mars 如何分布式地执行

先前&#xff0c;我们已经介绍过 Mars 是什么。如今 Mars 已在 Github 开源并对内上线试用&#xff0c;本文将介绍 Mars 已实现的分布式执行架构&#xff0c;欢迎大家提出意见。 架构 Mars 提供了一套分布式执行 Tensor 的库。该库使用 mars.actors 实现的 Actor 模型编写&am…

maven编译 Process terminated【已解决】

在idea中打开了settings文件&#xff0c;找到提示的报错位置&#xff0c;删除或者调整即可

揭秘人工智能(系列):深度学习是否过分夸大?

2012年左右&#xff0c;多伦多大学的研究人员首次使用深度学习来赢下了ImageNet&#xff0c;它是一项非常受欢迎的计算机图像识别竞赛。对于那些参与AI行业的人来说&#xff0c;这是一个大问题&#xff0c;因为计算机视觉是使计算机能够理解图像背景的学科&#xff0c;也是人工…

互联网诞生记:风起于青萍之末

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 老姜出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;“起初阿帕创造阿帕网络。阿帕网络是空虚混沌。渊面黑暗。阿帕的灵运行在网络里面。阿帕说&#xff1a;‘要有一个协议。’就有了一个协议。阿帕看它是…

powerpc 汇编linux,PowerPc下的寻址模式

本篇文章主要描述了Powerpc的寻址模式&#xff0c;让自己对内存映射、寻址的概念理解深入些。在开始讨论寻址模式之前&#xff0c;让我们首先来回顾一下计算机内存的概念。可能之前已经了解了关于内存和编程的一些事实&#xff0c;但是由于现代编程语言正试图淡化计算机中的一些…

org/springframework/util/backoff/BackOff

因为在项目要使用队列&#xff0c;昨天整合spring和rabbitmq&#xff0c;当在配置消费者时,就是下面一段代码。 <rabbit:listener-containerconnection-factory"connectionFactory" acknowledge"auto"><rabbit:listener queues"queue_one&q…

2018最佳GAN论文回顾(上)

我很高兴今年参加了一个研究项目&#xff0c;这要求我必须熟悉大量用于计算机视觉方面的深度学习领域的资料。我对过去两、三年内取得的进展感到惊讶&#xff0c;这真的非常令人兴奋和鼓舞&#xff0c;所有不同的子领域&#xff0c;如图像修复、对抗性样本、超分辨率或是三维重…

被神话的大数据——从大数据(big data)到深度数据(deep data)思维转变

自从阿法狗战胜人类顶级棋手之后&#xff0c;深度学习、人工智能变得再一次火热起来。有些人认为&#xff0c;深度学习的再一次兴起是源于硬件的提升、数据量的增多以及高效算法的研究。这并不完全精确&#xff0c;有一个基本的误解是更大的数据会产生更好的机器学习结果。然而…

c语言define定义全局变量,webpack中使用DefinePlugin定义全局变量

webpack中使用DefinePlugin来传递构建的环境变量给源代码使用最近在思考如何提供一种前后端开发功能测试既高效又安全的方案,因为对于我平时的项目是前后端同时进行的,后端我已经有了完备的权限管理,前端不能的角色会有不同的访问数据权限.而在vue前后端分离开发情况下, ...多个…

spring整合rabbitMQ最新版

文章目录一、简单对象1. 依赖2. 生产者3. 消费者4. 配置文件5. spring版本二、复杂对象2.1. 生产者2.2. 消费者一、简单对象 1. 依赖 <!--spring整合rabbitmq--><dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-ra…

搞定面试算法系列 | 分治算法三步走

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 江子抑转自 | 编程拯救世界主要思想分治算法&#xff0c;即分而治之&#xff1a;把一个复杂问题分成两个或更多的相同或相似子问题&#xff0c;直到最后子问题可以简单地直接求解&#xff0c;最后将子问题的解合并为原问题的…

通过FD耗尽实验谈谈使用HttpClient的正确姿势

一段问题代码实验 在进行网络编程时&#xff0c;正确关闭资源是一件很重要的事。在高并发场景下&#xff0c;未正常关闭的资源数逐渐积累会导致系统资源耗尽&#xff0c;影响系统整体服务能力&#xff0c;但是这件重要的事情往往又容易被忽视。我们进行一个简单的实验&#xf…

求一个数的阶乘值c语言代码,求10000的阶乘(c语言代码实现)

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼/*程序功能&#xff1a;计算一个正整数n的阶乘&#xff0c;目前最大能运算10000的阶乘&#xff0c;可秒杀。程序意义&#xff1a;加强自己对于大数的处理。说明&#xff1a;此程序对乘法和除法还未做任何优化&#xff0c;如果用上位…

与“十“俱进 阿里数据库运维10年演进之路

导语 阿里巴巴集团拥有超大的数据库实例规模&#xff0c;在快速发展的过程中我们在运维管理方面也在不断的面临变化&#xff0c;从物理器到容器、从独占到混布、从本地盘到存储计算分离、从集团内到大促云资源&#xff0c;从开源的MySQL到自研分布式数据库&#xff0c;运维管控…

jmeter 压测 RabbitMQ_单机

文章目录一、MQ压测1. 资料列表2. jmeter软件包3. 插件列表二、远程服务器监控2.1. 监控声明2.2. 监控场景的区别2.3. 软件列表2.4. 插件操作2.5. 软件操作三、jmeter编写MQ脚本3.1.创建线程组3.2. 创建MQ生产者3.3. 创建MQ消费者四、监听器4.1. 聚合报告4.2. 观察树4.3. 监控五…

云+X案例展 | 民生类:纷享销客助力沃得农机构筑智能化、信息化之路

本案例由纷扬科技投递并参与评选&#xff0c;CSDN云计算独家全网首发&#xff1b;更多关于【云X 案例征集】的相关信息&#xff0c;点击了解详情丨挖掘展现更多优秀案例&#xff0c;为不同行业领域带来启迪&#xff0c;进而推动整个“云行业”的健康发展。​​​​“2004年到20…

如何“神还原”数据中心? 阿里联合NTU打造了工业级精度的仿真沙盘!

如何保障数据中心的稳定运行&#xff0c;是多年来一直困扰业界的难题。机房环境如果发生未预期变化&#xff0c;可能造成难以估计的损失。所以我们希望能构建一个“变更沙盘”&#xff0c;在真实变更之前&#xff0c;操作人员可以先在沙盘中进行试变更&#xff0c;若变更效果在…