文章目录
为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-p8080:8080 anna/docker-package-only-build-demo:1.0-SNAPSHOT
-d :在后台运行容器,-p 映射本地 8080 端口到容器中的 8080 端口。
访问 localhost:8080,你应该会看到下面页面的内容:
当你对测试结果感到满意后,关闭容器:
$ 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-p8080: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-p8080: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
你会看到类似下面信息:
如你所见,多阶段构建的镜像最小,普通构建的镜像最大。这和预料的一样,因为普通构建包含了我们的应用代码,所有依赖包,以及打包工具;但是都阶段构建只包含我们需要的东西。
结论
按照上面介绍的三种 Docker 构建镜像的方法,多阶段构建是最理想的。你可以两全其美——Dockerr控制打包代码,但是你只提取需要的最终部署包。当在云空间存储容器时,这一点变得尤为重要。
- 你可以花费更少的时间去构建和传输容器,因为镜像更小
- 费用——镜像越小,占用的存储越小越便宜
- 更小的表面积,也就是从我们的镜像中移除额外的依赖,使它更不容易受到攻击
谢谢你看到这里,希望这边文章对你有所帮助!你可以在这里查看三种构建方式的源码:https://github.com/annabaker/docker-with-java-demos
原文地址(需要梯子):https://medium.com/containers-101/three-ways-to-create-docker-images-for-java-e139805ecb7f
版权归原作者 codecly 所有, 如有侵权,请联系我们删除。