自从Docker“革命”以来,我一直对为Spring应用程序创建Dockefile
感兴趣。 我不是一位热心的从业者,但是创建Dockerfile
的原理非常简单。 与Java-或可能是任何编程语言一样,虽然容易实现有效的工作,但是要创建良好的工作要困难得多。
多阶段构建
在Docker中,主要问题之一是最终映像的大小。 即使对于简单的Java应用程序,最终获得超过1 GB的图像也很常见。 从Docker版本17.05开始,可以在单个Dockerfile
包含多个构建,并可以将先前构建的输出访问到当前构建中。 这些称为多阶段构建。 最终图像将基于最后的构建阶段。
假设代码托管在Github上,并且它基于Maven。 构建阶段如下:
- 从Github克隆代码
- 复制上一阶段的文件夹; 使用Maven构建应用
- 复制上一个阶段的JAR; 用
java -jar
运行它
这是一个开始的构建文件:
FROM alpine/git
WORKDIR /app
RUN git clone https://github.com/spring-projects/spring-petclinic.git (1)
FROM maven:3.5-jdk-8-alpine
WORKDIR /app
COPY --from=0 /app/spring-petclinic /app (2)
RUN mvn install (3)
FROM openjdk:8-jre-alpine
WORKDIR /app
COPY --from=1 /app/target/spring-petclinic-1.5.1.jar /app (4)
CMD ["java -jar spring-petclinic-1.5.1.jar"] (5)
它映射了以上构建阶段:
- 从Github克隆Spring PetClinic git存储库
- 复制上一个构建阶段的项目文件夹
- 编译应用
- 复制上一个构建阶段的JAR
- 运行应用
提高可读性
请注意,在先前的构建文件中,构建阶段通过其索引(从0开始)被引用, 例如 COPY --from=0
。 虽然这不是一个真正的问题,但总有一些有意义的语义总是更好的。 Docker允许标记阶段,并在以后阶段引用这些标记。
FROM alpine/git as clone (1)
WORKDIR /app
RUN git clone https://github.com/spring-projects/spring-petclinic.git
FROM maven:3.5-jdk-8-alpine as build (2)
WORKDIR /app
COPY --from=clone /app/spring-petclinic /app (3)
RUN mvn install
FROM openjdk:8-jre-alpine
WORKDIR /app
COPY --from=build /app/target/spring-petclinic-1.5.1.jar /app
CMD ["java -jar spring-petclinic-1.5.1.jar"]
clone
build
- 使用标签引用第一阶段
选择正确的图像
多阶段构建对图像大小管理有很大帮助,但这不是唯一的标准。 开始的图像对最终图像有很大的影响。 初学者通常会使用成熟的操作系统映像(例如Ubuntu) ,而没有充分的理由夸大最终映像的大小。 但是,有轻量级操作系统,非常适合Docker映像,例如Alpine Linux 。
由于攻击面有限,因此它也非常适合安全目的。
在上面的构建文件中,图像如下:
他们都从高山继承或直接继承。
图像尺寸如下:
REPOSITORY TAG IMAGE ID CREATED SIZE
nfrankel/spring-petclinic latest 293bd333d60c 10 days ago 117MB
openjdk 8-jre-alpine c4f9d77cd2a1 4 weeks ago 81.4MB
JRE和应用程序映像之间的大小差异约为36 MB,这就是JAR本身的大小。
暴露端口
Spring Pet Clinic是一个Web应用程序,因此需要公开将绑定到的HTTP端口。 相关的Docker指令是EXPOSE
。 我选择8080
作为端口号,使其与嵌入式Tomcat容器相同,但是可以是任何东西。 最后阶段应修改为:
FROM openjdk:8-jre-alpine
WORKDIR /app
COPY --from=build /app/target/spring-petclinic-1.5.1.jar /app
EXPOSE 8080
CMD ["java -jar spring-petclinic-1.5.1.jar"]
参数化
此时,似乎该构建文件可用于构建具有以下功能的任何 Webapp:
- 源代码托管在Github上
- 构建工具是Maven
- 结果输出是可执行的JAR文件
当然,这非常适合Spring Boot应用程序,但这并不是硬性要求。
参数包括:
- Github存储库URL
- 项目名称
- Maven的
artifactId
和version
artifactId
名称(可能与artifactId
不同,具体取决于特定的Maven配置)
让我们使用它们来设计参数化的构建文件。 在Docker中,可以使用ENV
或ARG
选项传递参数。 两者都在命令行上使用--build-arg
选项设置。 差异如下:
Type | | |
Found in the image | ||
Default value | Required | Optional |
FROM alpine/git as clone
ARG url (1)
WORKDIR /app
RUN git clone ${url} (2)
FROM maven:3.5-jdk-8-alpine as build
ARG project (3)
WORKDIR /app
COPY --from=clone /app/${project} /app
RUN mvn install
FROM openjdk:8-jre-alpine
ARG artifactid
ARG version
ENV artifact ${artifactid}-${version}.jar (4)
WORKDIR /app
COPY --from=build /app/target/${artifact} /app
EXPOSE 8080
CMD ["java -jar ${artifact}"] (5)
url
- 以设置要克隆的Github存储库
url
- 被传递的值替换
- 与<1>相同
artifact
- 必须是
ENV
- 在运行时使用
artifact
- 值
建造
现在可以使用以下命令行来构建Spring Pet Clinic映像:
docker build--build-argurl= https://github.com/spring-projects/spring-petclinic.git \
--build-argproject= spring-petclinic \
--build-argartifactid= spring-petclinic \
--build-argversion= 1.5.1 \
-t nfrankel/spring-petclinic - < Dockerfile
由于映像不依赖于文件系统,因此无需传递任何上下文,并且可以从标准输入中传递Dockerfile。
要构建另一个应用程序,可以相应地更改参数, 例如 :
docker build--build-argurl= https://github.com/heroku/java-getting-started.git \
--build-argproject= java-getting-started \
--build-argartifactid= java-getting-started \
--build-argversion= 1.0 \
-t nfrankel/java-getting-started - < Dockerfile
跑步
运行使用上述命令构建的映像非常简单:
docker run-ti-p8080 :8080 nfrankel/spring-petclinic
不幸的是,它失败并显示以下错误消息: starting container process caused "exec: \"java -jar ${artifact}\": executable file not found in $PATH
。窍门是使用ENTRYPOINT
指令。更新后的Dockerfile
看起来像根据以下内容:
FROM openjdk:8-jre-alpine
ARG artifactid
ARG version
ENV artifact ${artifactid}-${version}.jar (4)
WORKDIR /app
COPY --from=build /app/target/${artifact} /app
EXPOSE 8080
ENTRYPOINT ["sh", "-c"]
CMD ["java -jar ${artifact}"] (5)
至此,运行容器将(最终)工作。
第二个映像使用不同的端口,因此命令应为:docker run -ti -p8080:5000 nfrankel / java-getting-started。
思想的食物
当前版本会克隆一个存储库,因此不需要将上下文发送到Docker。 一种替代方法是在构建文件外部克隆, 例如在连续集成链中克隆,然后从上下文开始。 这对于在开发人员机器上进行开发期间构建映像很有用。
是否最新版本
对于Git映像,我使用最新版本,而对于JDK和JRE映像,我使用特定的主要版本。 将Java版本固定为主要版本非常重要,而对于Git而言则不那么重要。 这取决于图像的性质。
由master
建造
克隆后没有更改分支的配置。 这是错误的,因为在大多数情况下,构建是在专用标签( 例如 v1.0.0
上执行的。 显然,应该有一个附加的命令-以及一个附加的build参数,以在构建之前签出特定的标签。
跳过测试
Pet Clinic应用程序的构建需要花费大量时间,因为它具有庞大的测试工具。 执行这些测试需要花费时间,Maven必须经历test
阶段才能到达install
阶段。 根据特定的连续集成链,测试可能早些执行,并且在映像构建期间不得再次执行。
Maven资料库下载
Spring Boot应用程序具有很多依赖性。 构建映像需要很长时间,因为每个应用程序每次都需要下载所有依赖项。 有两种解决方案可以解决这个问题。 它可能是另一篇文章的主题。
这篇文章的完整源代码可以在Github上找到。
翻译自: https://blog.frankel.ch/dockerfile-maven-based-github-projects/