前提

Python、Node.js、Go项目使用docker构建镜像的时候,在有 Docker cache 的情况下,连续构建镜像的速度是可以很快的。

一般的优化方式是先安装依赖模块,然后再编译打包代码库。这样安装依赖的 image layer 可以被 Docker 缓存,下次再构建就不用安装依赖。

但目前主流的java打包(这里不说像GraalVM等技术)方式都是将源码和依赖包打在一起, 这导致无法充分利用缓存层加快镜像的构建速度

思路

按照node项目打包思路, 先copy package*文件进行构建, 再copy源码

  • 这里以springboot项目举例
  1. 先弄一份初始化springboot start工程放在所需项目里

  2. 然后需要写两份dockerfile文件

    1. 第一份用来copy springboot start工程以及pom文件

    2. 然后执行maven命令打包, 这里我们不需要源码正确与否, 因为我们已经将pom内的jar包打到了本地

    3. 第二份dockerfile以第一份镜像为基准,

    4. 将基础镜像的springboot start工程删掉

    5. 然后把源项目工程copy过来,再进行打包

    6. 这个时候再打包会优先从本地基础镜像下载, 所以速度会很快

操作

这里以gitlab-runner的cicd举例

gitlab-ci.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
build-release:
stage: build
variables:
IMAGE: release/${CI_PROJECT_NAME}:v${CI_PIPELINE_ID}
BASIC_IMAGE: release/basic-${CI_PROJECT_NAME}:v${CI_PIPELINE_ID}
script:
- echo "Building application..."
- echo "$REGISTRY_PASSWORD" | sudo docker login -u ${REGISTRY_USERNAME} --password-stdin ${REGISTRY_ADDRESS}
- echo "registry login success"
- echo "构建基础镜像..."
# 先构建基础镜像, 这里边包含了我们需要的本地jar包
- sudo BUILDKIT_INLINE_CACHE=1 docker build -f Dockerfile-light -t ${REGISTRY_ADDRESS}/${BASIC_IMAGE} .
- echo "构建项目"
# IMG变量为基础镜像的tag,我们通过build-arg传过去
- sudo BUILDKIT_INLINE_CACHE=1 docker build --build-arg IMG="${REGISTRY_ADDRESS}/${BASIC_IMAGE}" -t ${REGISTRY_ADDRESS}/${IMAGE} .
- sudo docker push ${REGISTRY_ADDRESS}/${IMAGE}
- echo "docker push && push success"

第一份dockerfile: Dockerfile-light

1
2
3
4
5
6
7
8
9
# 构建基础镜像, 仅需要pom文件, 这里使用maven-3.8.3,settings.xml是些配置私服nexus的信息
# light是我们的springboot start工程,里面有用的部分只有一个main函数类
FROM maven:3.8.3-openjdk-8
WORKDIR /user/src/app
COPY settings.xml ~/.m2/settings-docker.xml
COPY settings.xml /usr/share/maven/conf/settings.xml
COPY ./pom.xml pom.xml
COPY ./light/src ./src
RUN mvn dependency:resolve && mvn clean package -DskipTests

第二份dockerfile: Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 这个dockerfile主要是构建我们源项目的
# 然后使用多阶段构建方式将多余的部分去掉
ARG IMG
FROM ${IMG} as build
WORKDIR /user/src/app
RUN rm -rf ./src
COPY . .
RUN mvn clean package -DskipTests
FROM openjdk:8-alpine as runtime
WORKDIR /user/src/app
RUN set -eux \
&& apk add --no-cache tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apk del tzdata
ARG JAR_FILE=/user/src/app/target/*.jar
ENV JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
COPY --from=build ${JAR_FILE} app.jar
CMD ["sh","-c","java -jar app.jar ${JAVA_OPTS}"]