使用Dockerfile将源代码构建成容器镜像,进行镜像分发、部署。相比于Golang或Python项目,Java项目因企业一般会选择自建依赖仓库(如Maven)导致容器化构建难度高,因不熟悉Dockerfile缓存机制导致构建速度较慢。本文从典型用户场景(云上自建GitLab代码仓库、自建Maven仓库)出发,介绍如何利用Dockerfile构建Java项目,如何提速构建过程以及如何利用ACR-EE构建服务来进行自动化镜像构建。
前提条件
项目介绍
本文使用具有依赖的两个Java项目进行演示。分别有以下两个项目:
Provider:提供服务,供调用。
Core模块:提供公共接口。
Service模块:服务实现模块。
Consumer:调用Provider服务。
Service模块:需要依赖Provide项目里的Core模块。参考代码如下:
. ├── consumer │ ├── Dockerfile │ ├── consumer.iml │ ├── pom.xml │ └── service │ ├── pom.xml │ ├── src │ └── target └── provider ├── Dockerfile ├── core │ ├── pom.xml │ ├── src │ └── target ├── pom.xml ├── provider.iml └── service ├── pom.xml ├── src └── target
构建产物:
基于Provider工程构建出生产者应用镜像。
基于Consumer工程构建出消费者应用镜像。
步骤一:确定公共依赖包已上传
项目引用的公共依赖包需要提前上传到自建Maven仓库。以这里的Provider为例,您可以在Provider目录下执行以下上传命令:
mvn clean install org.apache.maven.plugins:maven-deploy-plugin:2.8:deploy -DskipTests
步骤二:制作专属Maven基础镜像
为了在容器化构建环境内能访问到自建Maven仓库,需要把Maven仓库配置放到基础镜像内。这里建议基于官方Maven镜像来打造您的企业专属公共Maven基础镜像,应用项目直接引用该基础镜像即可访问Maven仓库。
将以下文件保存成Dockerfile并和Maven仓库的《settings.xml》文件放到同一目录。
FROM maven:3.8-openjdk-8 #指定与项目匹配的Maven镜像,该示例环境为3.8版本的Maven。 ADD settings.xml /root/.m2/ #将自建Maven仓库配置放到对应位置。
执行以下命令构建并推送到远程镜像仓库。
ls Dockerfile settings.xml docker build -t demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8 -f Dockerfile . Sending build context to Docker daemon 7.68kB Step 1/2 : FROM maven:3.8-openjdk-8 ---> a3f42bfde036 Step 2/2 : ADD settings.xml /root/.m2/ ---> db0d5a5192e3 Successfully built db0d5a5192e3 Successfully tagged demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8 docker push demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8
步骤三:构建Consumer应用镜像(Provider项目略过)
执行以下命令。(建议将构建过程中需要的基础镜像全部推送到阿里云镜像仓库。)
FROM demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8 AS builder
# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./service service/
# package jar
RUN mvn clean package
# Second stage: minimal runtime environment
From demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/openjdk:8-jre-alpine
# copy jar from the first stage
COPY --from=builder service/target/service-1.0-SNAPSHOT.jar service-1.0-SNAPSHOT.jar
EXPOSE 8080
CMD ["java", "-jar", "service-1.0-SNAPSHOT.jar"]
步骤四:优化构建速度
在步骤三:构建Consumer应用镜像(Provider项目略过)中,您已经能构建出镜像,但是当您修改代码、重复构建时会发现每次都会重复拉取JAR包,而无法利用到JAR包缓存,构建速度比较缓慢。这是因为Dockerfile有自己的缓存生效机制,当修改源代码后,ADD指令内的文件内容hash后出现了值变化导致RUN指令需要重新构建,没办法利用到先前的构建结果缓存。更多内容,请参见关于缓存和Dockerfile最佳实践。
我们优化构建速度的思路是将Maven依赖能缓存并重复利用:
首先将项目的《pom.xml》文件拷贝进容器,下载依赖。只要项目不改动《pom.xml》文件,后续构建都能利用到缓存。
拷贝项目的源码并编译。
一个改进的Dockerfile如下所示,项目首次构建耗时43s,后续如果仅仅是改动源代码构建时间可以缩短到7s。
FROM demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8 AS builder
# To resolve dependencies in a safe way (no re-download when the source code changes)
ADD ./pom.xml pom.xml
ADD ./service/pom.xml service/pom.xml
RUN mvn install
ADD ./service service/
# package jar
RUN mvn clean package
# Second stage: minimal runtime environment
From demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/openjdk:8-jre-alpine
# copy jar from the first stage
COPY --from=builder service/target/service-1.0-SNAPSHOT.jar service-1.0-SNAPSHOT.jar
EXPOSE 8080
CMD ["java", "-jar", "service-1.0-SNAPSHOT.jar"]
步骤五:基于ACR-EE的自动化构建流程
ACR-EE提供了企业级构建服务能力,推荐企业客户使用,更多内容,请参见使用企业版实例构建镜像。
以下将为您介绍几个在使用过程中会用到的最佳实践。
使用VPC内网构建模式
针对云上自建GitLab,建议使用VPC内网安全构建模式来构建镜像, 避免向公网暴露服务。更多内容,请参见使用VPC安全构建模式构建容器镜像。
使用镜像版本不可变功能
推荐仓库打开版本不可变功能,以防止线上版本被覆盖。
创建基于Commit ID的构建规则
每次构建生成两个镜像版本,一个版本使用代码Commit ID来表示,方便镜像版本和代码版本对应起来;另一个版本使用latest表示最新版本。
提交代码并触发构建。下图是提交两次代码后自动触发的两次构建过程,每次都生成了两个镜像,并且因为缓存命中加速原因,第二次构建更快。