Fork me on GitHub

如何构建SpringBoot应用的Docker镜像


title: 如何构建SpringBoot应用的Docker镜像
date: 2019-12-04 22:17:54
categories:

  • SpringBoot
    tags:
  • Docker
  • SpringBoot

背景

SpringBoot大道其行,微服务也是大道其行,越来越多的SpringBoot应用使用Docker容器来运行。既然要用Docker容器运行,那首先得把SpringBoot应用打包成Docker镜像。

构建方法

目前主要的方法有三种:

  • 1.使用dockerfile-maven-plugin插件构建
  • 2.使用Google Jib插件构建
  • 3.手动构建

下面我们一一来介绍,首先来介绍

使用dockerfile-maven-plugin插件构建

项目地址:https://github.com/spotify/dockerfile-maven

项目根目录下创建Dockerfile

以下给出了多个Dockerfile,根据实际情况选择

  1. 简洁版

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    FROM openjdk:8-jdk-alpine
    MAINTAINER jaychang <jaychang1987@gmail.com>
    VOLUME /tmp
    ARG FILE
    ARG APP_NAME
    ADD ${FILE} /app.jar
    ENV JAVA_OPTS="-XX:MaxRAMFraction=2"
    # Configure ustc alpine software source and timezone
    RUN sed -i "s#http://dl-cdn.alpinelinux.org#https://mirrors.aliyun.com#g" /etc/apk/repositories \
    && apk update \
    && apk --no-cache add tzdata curl tini \
    && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone \
    && rm -rf /var/cache/apk/*
    # To reduce Tomcat startup time we added a system property pointing to "/dev/urandom" as a source of entropy.
    ENTRYPOINT ["/sbin/tini", "--"]
    CMD ["sh","-c","java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar"]
  2. 有带-D的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FROM openjdk:8-jdk-alpine
MAINTAINER jaychang <jaychang1987@gmail.com>
VOLUME /tmp
ARG FILE
ARG APP_NAME
ADD ${FILE} /app.jar
# PINPOINT_VERSION Default 1.6.2 it can be replaced at docker run use -e or docker service -e or environment in docker-compose.yml
ENV PINPOINT_VERSION=1.6.2
ENV JAVA_OPTS="-XX:MaxRAMFraction=2"
ENV PINPOINT_OPTS="-Dpinpoint.agentId=${APP_NAME} -Dpinpoint.applicationName=$APP_NAME"
# Configure ustc alpine software source and timezone
RUN sed -i "s/http:\/\/dl-cdn.alpinelinux.org/https:\/\/mirrors.aliyun.com/g" /etc/apk/repositories \
&& apk update \
&& apk --no-cache add tzdata curl tini \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& rm -rf /var/cache/apk/*
# To reduce Tomcat startup time we added a system property pointing to "/dev/urandom" as a source of entropy.
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["sh","-c","java $JAVA_OPTS -javaagent:/pinpoint_data/pinpoint-agent-${PINPOINT_VERSION}/pinpoint-bootstrap-$PINPOINT_VERSION.jar $PINPOINT_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar"]
  1. 带字体版

    常见的场景如excel导出,验证码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FROM openjdk:8-jdk-alpine
MAINTAINER jaychang <jaychang1987@gmail.com>
VOLUME /tmp
ARG FILE
ARG APP_NAME
ADD ${FILE} /app.jar
ENV JAVA_OPTS="-XX:MaxRAMFraction=2"
# Configure ustc alpine software source and timezone
RUN sed -i "s/http:\/\/dl-cdn.alpinelinux.org/https:\/\/mirrors.aliyun.com/g" /etc/apk/repositories \
&& apk update \
&& apk --no-cache add tzdata curl tini \
&& apk --no-cahce add font-adobe-100dpi ttf-dejavu fontconfig \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& rm -rf /var/cache/apk/*
# To reduce Tomcat startup time we added a system property pointing to "/dev/urandom" as a source of entropy.
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["sh","-c","java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar"]

在pom.xml中配置maven插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.6.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.12</version>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
<execution>
<id>tag-image-version</id>
<phase>package</phase>
<goals>
<goal>tag</goal>
<goal>push</goal>
</goals>
<configuration>
<tag>${project.version}</tag>
</configuration>
</execution>
<execution>
<id>tag-image-latest</id>
<phase>package</phase>
<goals>
<goal>tag</goal>
<goal>push</goal>
</goals>
<configuration>
<tag>latest</tag>
</configuration>
</execution>
</executions>
<configuration>
<tag>${project.version}</tag>
<repository>${docker.image.prefix}/${project.artifactId}</repository>
<buildArgs>
<FILE>target/${project.build.finalName}.jar</FILE>
<APP_NAME>${project.artifactId}</APP_NAME>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>

docker.image.prefix可以预先在properties标签中定义好,如果要区分不同环境传到不同的镜像中心可使用profiles标签来定义不同环境的docker.image.prefix

开始构建镜像

前提是要先安装好docker服务

1
mvn clean package -Dmaven.test.skip=true -U

使用Google Jib插件构建

项目地址:https://github.com/GoogleContainerTools/jib

Google Jib的优势是无需事先安装docker,无需定义Dockerfile文件,而且Google Jib可以分层,依赖的jar构成一层,resource构成一层,然后项目本身的classes构建一层。这样如果只是改变项目本身代码,那么构建是非常快的。且推送的镜像也是非常小的,可以大大节省网络流量。

在pom.xml中配置maven插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>-Amapstruct.defaultComponentModel=default</compilerArg>
<compilerArg>-Amapstruct.unmappedTargetPolicy=WARN</compilerArg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>1.7.0</version>
<configuration>
<from>
<image>harbor.chaomeifan.com/library/openjdk:8-jdk-alpine-v1</image>
</from>
<allowInsecureRegistries>true</allowInsecureRegistries>
<to>
<image>${docker.image.prefix}/${project.artifactId}</image>
<tags>
<tag>${project.version}</tag>
<tag>latest</tag>
</tags>
</to>
<container>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<environment>
<PINPOINT_VERSION>1.8.4</PINPOINT_VERSION>
<PINPOINT_OPTS>-Dpinpoint.agentId=${project.artifactId} -Dpinpoint.applicationName=${project.artifactId}</PINPOINT_OPTS>
<NACOS_OPTS>-Dregistry.nacos.namespace=bbae7e8e-29cb-48e3-8c36-c8a9759975a9</NACOS_OPTS>
</environment>
<user>zcckj:zcckj</user>
<entrypoint>
<arg>/sbin/tini</arg>
<arg>--</arg>
<arg>/bin/sh</arg>
<arg>-c</arg>
<arg>java ${JAVA_OPTS} -javaagent:/pinpoint_data/pinpoint-agent-${PINPOINT_VERSION}/pinpoint-bootstrap-$PINPOINT_VERSION.jar $PINPOINT_OPTS $NACOS_OPTS -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* com.zcckj.jingtian.biz.server.JingtianBizApplication</arg>
</entrypoint>
</container>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
  • lombok.version为1.18.8

  • mapstruct.version为1.3.0.Final

如果您的项目没有用到Pinpoint,Nacos可以完全精简为如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<build>
<plugins>
<plugin>编译插件省略(同上)...</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>1.7.0</version>
<configuration>
<from>
<image>harbor.chaomeifan.com/library/openjdk:8-jdk-alpine-v1</image>
</from>
<allowInsecureRegistries>true</allowInsecureRegistries>
<to>
<image>${docker.image.prefix}/${project.artifactId}</image>
<tags>
<tag>${project.version}</tag>
<tag>latest</tag>
</tags>
</to>
<container>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<user>zcckj:zcckj</user>
<entrypoint>
<arg>/sbin/tini</arg>
<arg>--</arg>
<arg>/bin/sh</arg>
<arg>-c</arg>
<arg>java -cp /app/resources/:/app/classes/:/app/libs/* com.zcckj.jingtian.biz.server.JingtianBizApplication</arg>
</entrypoint>
</container>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

这里我们构建了一个基镜像harbor.chaomeifan.com/library/openjdk:8-jdk-alpine-v1,以下给出基镜像的Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
FROM openjdk:8-jdk-alpine
MAINTAINER zhangjie <jaychang1987@gmail.com>

ARG user=zcckj
ARG group=zcckj
ARG uid=1739
ARG gid=1739

ENV JAVA_OPTS="-Xmx1024M -Xms1024M -Xmn192M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M -XX:+UseParallelGC -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMillis=100 -XX:ErrorFile=/tmp/hs_err_pid%p.log -Xloggc:/tmp/gc.log -XX:HeapDumpPath=/tmp -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError"

# Configure aliyun alpine software source and timezone
RUN set -x \
&& sed -i "s/http:\/\/dl-cdn.alpinelinux.org/https:\/\/mirrors.aliyun.com/g" /etc/apk/repositories \
&& apk update \
&& apk --no-cache add bash bash-completion bash-doc \
&& apk --no-cache add tzdata curl tini \
&& apk --no-cahce add font-adobe-100dpi ttf-dejavu fontconfig \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& rm -rf /var/cache/apk/* \
&& addgroup --gid ${gid} ${group} \
&& adduser --uid ${uid} -G ${group} ${user} -s /bin/bash -D

由于基镜像用了非root用户启动进程,故如果有映射到容器的目录需要将目录所有者所属组改为Dockerfile中定义的用户id,组id,chown -R 1739:1739 /path/directory

如果觉得嫌麻烦,可以在基镜像中直接用root

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM openjdk:8-jdk-alpine
MAINTAINER zhangjie <jaychang1987@gmail.com>

ENV JAVA_OPTS="-Xmx1024M -Xms1024M -Xmn192M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M -XX:+UseParallelGC -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMillis=100 -XX:ErrorFile=/tmp/hs_err_pid%p.log -Xloggc:/tmp/gc.log -XX:HeapDumpPath=/tmp -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError"

# Configure aliyun alpine software source and timezone
RUN set -x \
&& sed -i "s/http:\/\/dl-cdn.alpinelinux.org/https:\/\/mirrors.aliyun.com/g" /etc/apk/repositories \
&& apk update \
&& apk --no-cache add bash bash-completion bash-doc \
&& apk --no-cache add tzdata curl tini \
&& apk --no-cahce add font-adobe-100dpi ttf-dejavu fontconfig \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& rm -rf /var/cache/apk/*

这样就无需在jib插件配置中设置zcckj:zcckj

开始构建镜像

1
mvn clean compile jib:build -Dmaven.test.skip=true -DsendCredentialsOverHttp=true -U

手工构建