为Java应用创建Docker镜像的三种方式

这篇具有很好参考价值的文章主要介绍了为Java应用创建Docker镜像的三种方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为Java应用创建Docker镜像的三种方式

为Java应用创建Docker镜像的三种方式

在 Dockerfiles 出现的很久之前,Java 开发者大多使用单体应用方式部署(WARs, JARs, EARs, 等等)。现在如你所知,最好的做法是为每个小业务单独部署的微服务方式。你构建的不是一个巨大的单体应用程序,而是使多个可以独立运行的小服务。

这正是 Docker 的用武之地。如果你想升级一个服务,只需要针对需要升级的服务单元重新构建一个新的 Docker 镜像,二不是像之前那样针对整个应用重新部署 jar/war/ear 包。

这篇文章我将介绍3种不同的方式为 Java 应用创建 Docker 镜像。如果你希望跟着介绍体验这3种方式,请在这里 clone 的我仓库代码 https://github.com/annabaker/docker-with-java-demos 。

先决条件

  • Docker 已安装
  • Maven 已安装(针对第一种方式)
  • 一个简单的 Spring Boot 应用(我已经使用 Spring Initializr创建了一个 Spring Web 项目)

方式一:只构建部署包

这种方式我们会先使用 Maven(或者其他构建工具)控制部署包的过程。

首先解压之前使用 Spring Initializr 工具生成的构建包。在 Spring Boot 应用的项目目录中,创建一个 Dockerfile 文件。在控制台中执行以下命令:

$ unzip demo.zip
$ cd demo
$ nano Dockerfile

复制以下内容并保存:

# we will use openjdk 8 with alpine as it is a very small linux distro
FROM openjdk:8-jre-alpine3.9
# copy the packaged jar file into our docker image
COPY target/demo-0.0.1-SNAPSHOT.jar /demo.jar
# set the startup command to execute the jar
CMD ["java", "-jar", "/demo.jar"]
  • FROM 语句指明我们使用的父镜像
  • COPY 语句会把本地 Maven 构建的的 jar 包复制到镜像中
  • CMD 语句告诉 Docker 一旦容器启动后内部执行这个命令

现在,我们先使用 Maven 构建我们的 .jar 包:

mvn clean package

然后开始构建 Docker 镜像。下面的命令告诉 Docker 在当前目录去获取 Dockerfile。虽然不是强制的,但是作为惯例我们使用用户名/镜像名的方式命名。-t 标识符表示一个 Docker 标签(tag),这里是 1.0-SNAPSHOT。如果不指定一个 tag, Docker 会使用默认的 tag:latest

$ docker build -t anna/docker-package-only-build-demo:1.0-SNAPSHOT .

通过刚才构建的镜像来运行一个容器:

$ docker run -d -p 8080:8080 anna/docker-package-only-build-demo:1.0-SNAPSHOT

-d :在后台运行容器,-p 映射本地 8080 端口到容器中的 8080 端口。

访问 localhost:8080,你应该会看到下面页面的内容:
为Java应用创建Docker镜像的三种方式

当你对测试结果感到满意后,关闭容器:

$ docker stop <container_id>

优点

  • 产生了一个轻量级的镜像
  • 在 Docker 镜像中不需要依赖 Maven
  • 在 Docker 镜像中不依赖我们应用的任何依赖
  • 当应用层面修改后,仍然可以使用本地 Maven 仓库的缓存,这在后面的方式2、方式3中再继续讨论

缺点

  • 宿主机需要依赖 Maven 和 Jdk 环境
  • 如果 Maven 构建失败或者没有事先执行构建,那么 Docker 也将构建失败——如果你希望与其他工具集成,用当前 Dockerfile 来自动构建镜像时将成为一个问题

方式二:普通 Docker 构建

在“普通” Dcoker 构建中,Docker 会控制构建打包过程。

修改之前的 Dockerfile 文件为如下内容:

# select parent image
FROM maven:3.6.3-jdk-8
# copy the source tree and the pom.xml to our new container
COPY ./ ./
# package our application code
RUN mvn clean package
# set the startup command to execute the jar
CMD ["java", "-jar", "target/demo-0.0.1-SNAPSHOT.jar"]

现在,我们和之前一样来构建一个镜像:

$ docker build -t anna/docker-normal-build-demo:1.0-SNAPSHOT .

然后,运行一个容器:

$ docker run -d -p 8080:8080 anna/docker-normal-build-demo:1.0-SNAPSHOT

继续验证容器运行结果,访问 localhost:8080。验证成功后再停止容器。

优点

  • Docker 控制了打包过程,所以这种方式不需要宿主机事先安装构建工具和 Jdk 环境
  • 使用其他工具集成时很友好,可以直接执行 Dockerfile 来构建镜像

缺点

  • 在这三种方式中,生成的镜像文件最大
  • 不仅包含了部署包,还包含了所有的代码依赖和部署工具,这在运行时候是不需要的
  • 如果应用层需要重新构建,maven打包时会从远程仓库重新下载依赖包(不能使用maven的本地缓存)

方式三:多阶段构建(理想方式)

在多阶段构建方式中,我们给每个阶段使用一个 FROM 语句。每个 FROM 语句会创建一个新的基本层,而且会抛弃之前 FROM 阶段我们不需要的所有东西。

修改你的 Dockerfile 为如下内容:

# the first stage of our build will use a maven 3.6.1 parent image
FROM maven:3.6.1-jdk-8-alpine AS MAVEN_BUILD
# copy the pom and src code to the container
COPY ./ ./
# package our application code
RUN mvn clean package
# the second stage of our build will use open jdk 8 on alpine 3.9
FROM openjdk:8-jre-alpine3.9
# copy only the artifacts we need from the first stage and discard the rest
COPY --from=MAVEN_BUILD /docker-multi-stage-build-demo/target/demo-0.0.1-SNAPSHOT.jar /demo.jar
# set the startup command to execute the jar
CMD ["java", "-jar", "/demo.jar"]

构建镜像:

$ docker build -t anna/docker-multi-stage-build-demo:1.0-SNAPSHOT .

然后运行容器:

$ docker run -d -p 8080:8080 anna/docker-multi-stage-build-demo:1.0-SNAPSHOT

优点

  • 生成一个轻量级的 Docker 镜像
  • 不需要宿主机事先安装构建工具和 Jdk 环境(Docker 控制了打包过程)
  • 可以方便集成自动部署工具自动执行当前的 Dockerfile来构建镜像
  • 从第一个阶段到第二个阶段我们只复制了需要的部署包(例如:和方案二相比,使用这种方式,应用的依赖没有被打包到最终的镜像中)
  • 可以按照需求创建足够多的阶段
  • 可以使用 –target 标签在任何特定的阶段停止构建,例如:
docker build — target MAVEN_BUILD -t anna/docker-multi-stage-build-demo:1.0-SNAPSHOT .

缺点

如果应用层需要需要重新构建,maven打包时会从远程仓库重新下载依赖包(不能使用maven的本地缓存)

验证:镜像有多大

在命令行,运行:

docker image ls

你会看到类似下面信息:
为Java应用创建Docker镜像的三种方式
如你所见,多阶段构建的镜像最小,普通构建的镜像最大。这和预料的一样,因为普通构建包含了我们的应用代码,所有依赖包,以及打包工具;但是都阶段构建只包含我们需要的东西。

结论

按照上面介绍的三种 Docker 构建镜像的方法,多阶段构建是最理想的。你可以两全其美——Dockerr控制打包代码,但是你只提取需要的最终部署包。当在云空间存储容器时,这一点变得尤为重要。

  • 你可以花费更少的时间去构建和传输容器,因为镜像更小
  • 费用——镜像越小,占用的存储越小越便宜
  • 更小的表面积,也就是从我们的镜像中移除额外的依赖,使它更不容易受到攻击

谢谢你看到这里,希望这边文章对你有所帮助!你可以在这里查看三种构建方式的源码:https://github.com/annabaker/docker-with-java-demos

原文地址(需要梯子):https://medium.com/containers-101/three-ways-to-create-docker-images-for-java-e139805ecb7f文章来源地址https://www.toymoban.com/news/detail-426331.html

到了这里,关于为Java应用创建Docker镜像的三种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java多线程 - 创建的三种方式介绍

    什么是线程 ? 线程(thread)是一个程序内部的一条执行路径。 我们之前启动程序执行后,main方法的执行其实就是一条单独的执行路径。 程序中如果只有一条执行路径,那么这个程序就是单线程的程序。 什么是多线程 ? 多线程是指从软硬件上实现多条执行流程的技术。 方式一

    2024年02月20日
    浏览(44)
  • 【Java面试题】线程创建的三种方式及区别?

    继承Thread类,子类重写run()方法,调用子类的strat()启动线程。 实现Runnable接口,实现run()方法,调用对象start()启动线程。 实现Callable接口,实现call()方法,用FutureTask()封装实现类。使用FutureTask对象作为Thread对象调用start()启动线程,调用FutureTask对象的get()

    2024年02月12日
    浏览(49)
  • Java并发(三)----创建线程的三种方式及查看进程线程

    例如: 输出 注意:这里通过 @Slf4j 注解打印的日志 把【线程】和【任务】(要执行的代码)分开 Thread 代表线程 Runnable 可运行的任务(线程要执行的代码) 例如: 输出 Java 8 以后可以使用 lambda 精简代码 小结 方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开

    2023年04月24日
    浏览(47)
  • 线程创建的三种方式

    目录 1. Thread类 2. Runnable接口 3. Callable接口 4. 线程的生命周期 新建  就绪 运行 阻塞 等待 结束 继承Thread类的方式创建线程 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务 创建Thread类的子类,即创建了线程对象 调用线程对象的

    2024年02月09日
    浏览(42)
  • Spring 创建 Bean 的三种方式

    在使用 Spring 框架后,对象以 Bean 的形式统一交给 IOC 容器去创建和管理。现阶段主流的方式是基于 SpringBoot 框架,基于注解的方式实现 Bean 的创建,但在原生 Spring 框架中其实存在三种创建 Bean 的方式。 BeanProcess 实体类,虽然加了 @Component 等三个注解,但只在注解方式创建

    2024年02月14日
    浏览(49)
  • C++创建线程的三种方式

    早期的C++并不支持多线程的创建,如果要创建多线程,依赖的是系统提供的一些方法(例如linux的 pthread). 从C++11以后开始,提供了std::thread线程库,因此我们可以创建std::thread类对象的方式来创建线程。创建的方式主要有三种: 通过函数指针 通过函数对象 通过lambda函数 使用

    2024年02月16日
    浏览(44)
  • RabbitMQ 简单实现创建队列的三种方式

    //1. 手动创建,需在RabbitMQ中手动创建myQueue1 队列,否则报错 @RabbitListener(queues = “myQueue1”) public void process1(String message){ log.info(“MqReceiver1: {}”, message); } //2. 自动创建队列 @RabbitListener(queuesToDeclare = @Queue(“myQueue2”)) public void process2(String message){ log.info(“MqReceiver2: {}”, messa

    2024年02月15日
    浏览(41)
  • React创建组件的三种方式及其区别是什么?

    在React中,创建组件的三种主要方式是函数式组件、类组件和使用React Hooks的函数式组件。以下是对每种方式的详细解释以及它们之间的区别: 1、函数式组件: 函数式组件是使用纯粹的JavaScript函数来定义的。它接收一个props对象作为参数,并返回一个用于描述组件UI的React元

    2024年02月11日
    浏览(42)
  • idea社区版本创建springboot项目的三种方式

      文章目录 一、前言 一、方式1:spring 官方创建 springboot项目 1、打开在线的 spring initializr 2、选择项目的语言、版本、依赖等 3、 解压源码包,并使用IDEA打开 4、测试接口 二、方式2:社区idea安装Spring插件 1、添加插件 三、方式3:(麻烦)手动maven 创建springboot项目 一、前言

    2023年04月09日
    浏览(81)
  • Docker安装的三种方式

    下载解压: 配置镜像加速: 作为服务启动: 启动: 下载地址: https://download.docker.com/linux/centos/7/x86_64/stable/Packages/ 从 docker 安装包版本下载对应所有依赖包: 将依赖包移动到安装包目录下: 安装: 开启docker并将其设置成开机自启动: 配置镜像加速: 卸载: 卸载旧版本:

    2024年02月11日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包