个人 二维码




公众号二维码

目录

Spring Boot 多样化构建 Docker 镜像

/resources/articles/spring/spring-boot/docker/springboot_docker.jpg

  

前言

  

  随着微服务概念的持续演进,容器化技术的火爆,对于广大基于 Java 语言开发的程序员而言,将开发好的项目快速构建为 Docker 镜像推送至服务器也成为了一项必会的技能。

  伴随着技术的不断更新,基于 Spring Boot 构建 Docker 镜像的方式也是五花八门,大致分为以下几种:

  • Cloud Native Buildpacks(Spring Boot 2.3+ 版本开始支持)
  • Googlejib-maven-plugin
  • fabric8spotifydocker-maven-plugin

  下面我们一起学习如何通过这几种方式把 Spring Boot 应用构建成 Docker 镜像。

  

Spring Boot 项目

  

  先准备一个简单的基于 Maven 的 Spring Boot 项目,方便实践。

  

pom

  

  继承 spring-boot-starter-parent,引入 Spring Boot 父类依赖。

1
2
3
4
5
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
</parent>

  添加 spring-boot-starter-web 依赖。

1
2
3
4
5
6
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

  

配置文件

  

  配置文件无需添加任何内容,基于约定优于配置,使用默认配置即可。

  

启动类

  

1
2
3
4
5
6
7
8
@SpringBootApplication
public class SpringBootDockerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDockerApplication.class, args);
    }

}

  

控制层

  

1
2
3
4
5
6
7
8
9
@RestController
public class DockerController {

    @RequestMapping("/")
    public String index() {
        return "Hello Spring Boot Docker!";
    }

}

  

启动访问

  

  启动项目,浏览器访问:http://localhost:8080/,页面返回:Hello Spring Boot Docker!,说明项目OK,准备工作已完成。

  

Docker 安装

  

  关于 Docker 的环境准备请参考:Docker 安装及配置镜像加速

  

Cloud Native Buildpacks

  

简介

  

  正所谓哪里有需求,哪里就有市场,传统的方式使用 Spring Boot 构建 Docker 镜像需要我们自己去维护 Dockerfile。

  而从 Spring Boot 2.3 版本开始,借助 Buildpacks 的支持,无需 Dockerfile 即可将任何 Spring Boot 2.3 及更高版本的应用程序进行容器化。Spring Boot 2.4 版本开始又进行了进一步优化:官网文档

  

/resources/articles/spring/spring-boot/docker/aHR0cHM6Ly93d3cuamF2YWNvZGVnZWVrcy5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMjAvMDYvc3ByaW5nYm9vdC1idWlsZHBhY2tzLTEtMTAyNHg0MTMuanBn.jpg

  

说明

  

  Spring Boot 可以快速将 Java 应用程序构建为 Docker 镜像推送至指定远程仓库:

  • DockerHub 官方公共仓库(DockerHub 官方公共仓库国内访问速度堪忧,不推荐)
  • 阿里云镜像仓库(需要在阿里云登录账号自行创建仓库)
  • 自建私有镜像仓库(本文演示方案)私有镜像仓库搭建请参考:Docker 私有镜像仓库的搭建及认证

  

使用

  

  完整配置如下:

 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
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <!-- 分层配置 -->
                <layers>
                    <enabled>true</enabled>
                </layers>
                <!-- Docker 配置 -->
                <docker>
                    <!-- 推送至 Docker 服务器,配置远程 Docker 守护进程 url -->
                    <host>http://192.168.10.10:2375</host>
                    <!-- 是否启用 TLS 校验 -->
                    <tlsVerify>false</tlsVerify>
                    <!-- 开启 TLS 校验需要设置证书地址 -->
                    <!--<certPath>/home/user/.minikube/certs</certPath>-->

                    <!-- 推送至指定镜像仓库(公共仓库、阿里云仓库、私有仓库等) -->
                    <publishRegistry>
                        <username>用户名</username>
                        <password>密码</password>
                        <url>仓库地址</url>
                    </publishRegistry>
                </docker>
                <!-- 镜像配置 -->
                <image>
                    <!-- 镜像名:版本号 -->
                    <name>${project.artifactId}:${project.version}</name>
                    <!-- 执行完 build 自动 push -->
                    <publish>true</publish>
                </image>
            </configuration>
        </plugin>
    </plugins>
</build>

  执行以下命令进行镜像构建和推送:

1
mvn spring-boot:build-image -Dmaven.test.skip=true

  

网络问题

  

  构建过程中需要从 github 下载相关的依赖,这个过程大概率会失败。建议通过配置代理或者使用国外 ECS 来解决。

 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
[INFO] Building image 'docker.io/library/spring-boot-docker:1.0-SNAPSHOT'
[INFO]
[INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:base' 100%
[INFO]  > Pulled builder image 'paketobuildpacks/builder@sha256:c9141bd56f3e837a9d618c9e6748b8d5ab0783728a4924bbca41c7e8f
b21fca5'
[INFO]  > Pulling run image 'docker.io/paketobuildpacks/run:base-cnb' 100%
[INFO]  > Pulled run image 'paketobuildpacks/run@sha256:79bd9986ae32d62e8339e602b61ca6288df8434d5e2972bed5d7644d6b8ed0d0'

[INFO]  > Executing lifecycle version v0.10.1
[INFO]  > Using build cache volume 'pack-cache-7e973e39396c.build'
[INFO]
[INFO]  > Running creator
[INFO]     [creator]     ===> DETECTING
[INFO]     [creator]     5 of 18 buildpacks participating
[INFO]     [creator]     paketo-buildpacks/ca-certificates   1.0.1
[INFO]     [creator]     paketo-buildpacks/bellsoft-liberica 6.0.0
[INFO]     [creator]     paketo-buildpacks/executable-jar    3.1.3
[INFO]     [creator]     paketo-buildpacks/dist-zip          2.2.2
[INFO]     [creator]     paketo-buildpacks/spring-boot       3.5.0
[INFO]     [creator]     ===> ANALYZING
[INFO]     [creator]     Previous image with name "docker.io/library/spring-boot-docker:1.0-SNAPSHOT" not found
[INFO]     [creator]     ===> RESTORING
[INFO]     [creator]     ===> BUILDING
[INFO]     [creator]
[INFO]     [creator]     Paketo CA Certificates Buildpack 1.0.1
[INFO]     [creator]       https://github.com/paketo-buildpacks/ca-certificates
[INFO]     [creator]       Launch Helper: Contributing to layer
[INFO]     [creator]         Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
[INFO]     [creator]         Writing profile.d/helper
[INFO]     [creator]
[INFO]     [creator]     Paketo BellSoft Liberica Buildpack 6.0.0
[INFO]     [creator]       https://github.com/paketo-buildpacks/bellsoft-liberica
[INFO]     [creator]       Build Configuration:
[INFO]     [creator]         $BP_JVM_VERSION              8.*             the Java version
[INFO]     [creator]       Launch Configuration:
[INFO]     [creator]         $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
[INFO]     [creator]         $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculat
ion
[INFO]     [creator]         $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
[INFO]     [creator]         $JAVA_TOOL_OPTIONS                           the JVM launch flags
[INFO]     [creator]       BellSoft Liberica JRE 8.0.275: Contributing to layer
[INFO]     [creator]         Downloading from https://github.com/bell-sw/Liberica/releases/download/8u275+1/bellsoft-jre8
u275+1-linux-amd64.tar.gz
[INFO]     [creator]     read tcp 172.17.0.3:36802->185.199.108.154:443: read: connection reset by peer
[INFO]     [creator]     ERROR: failed to build: exit status 1
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

  

jib-maven-plugin

  

简介

  

  Jib 是 Google 开发的可以直接构建 Java 应用程序的 Docker 和 OCI 镜像的类库,以 Maven 和 Gradle 插件形式提供。

  通过 Jib,Java 开发者可以使用他们熟悉的 Java 工具来构建容器。Jib 是一个快速而简单的容器镜像构建工具,它负责处理将应用程序打包到容器镜像中所需的所有步骤。它不需要你编写 Dockerfile 或安装 Docker,而且可以直接集成到 Maven 和 Gradle 中 —— 只需将插件添加到构建中,就可以立即将 Java 应用程序容器化。

  Spring Boot 项目添加 jib-maven-plugin 插件。

1
2
3
4
5
<dependency>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>2.8.0</version>
</dependency>

  

说明

  

  Jib 可以快速将 Java 应用程序构建为 Docker 镜像推送至指定远程仓库:

  • DockerHub 官方公共仓库(DockerHub 官方公共仓库国内访问速度堪忧,不推荐)
  • 阿里云镜像仓库(需要在阿里云登录账号自行创建仓库)
  • 自建私有镜像仓库(本文演示方案)私有镜像仓库搭建请参考:Docker 私有镜像仓库的搭建及认证

  

使用

  

  完整配置如下:

 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
<build>
    <plugins>
        <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
            <version>2.8.0</version>
            <configuration>
                <!-- 拉取所需的基础镜像 -->
                <from>
                    <!-- 默认从官方公共仓库拉取镜像,速度较慢 -->
                    <image>openjdk:alpine</image>
                    <!-- 从指定仓库拉取镜像提速(需提前将镜像 push 至仓库) -->
                    <!--<image>192.168.10.10:5000/openjdk:alpine</image>-->
                </from>
                <!-- push 到哪个镜像仓库(公共仓库、阿里云仓库、私有自建仓库等) -->
                <to>
                    <!-- 使用 DockerHub 的官方公共仓库:仓库地址/用户名/镜像名 -->
                    <!--<image>registry.hub.docker.com/mrhelloworld/${project.name}</image>-->
                    <!-- 使用自建的私有仓库:仓库地址/镜像名 -->
                    <image>192.168.10.10:5000/${project.artifactId}</image>
                    <!-- 镜像版本号 -->
                    <tags>
                        <tag>${project.version}</tag>
                    </tags>
                    <!-- 连接仓库的账号密码 -->
                    <auth>
                        <username>用户名</username>
                        <password>密码</password>
                    </auth>
                </to>
                <!-- 使 jib 插件支持 http 协议连接镜像仓库(安全起见,默认是关闭的) -->
                <allowInsecureRegistries>true</allowInsecureRegistries>
                <container>
                    <!-- 启动类 -->
                    <mainClass>org.example.SpringBootDockerApplication</mainClass>
                </container>
            </configuration>
        </plugin>
    </plugins>
</build>

  执行以下命令进行镜像构建和推送:

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

  推送成功效果如下:

/resources/articles/spring/spring-boot/docker/image-20210225181054588.png

/resources/articles/spring/spring-boot/docker/image-20210225193756756.png

  运行命令启动容器:

1
docker run -di --name spring-boot-docker -p 8080:8080 192.168.10.10:5000/spring-boot-docker:1.0-SNAPSHOT

  浏览器访问:http://192.168.10.10:8080/,页面返回:Hello Spring Boot Docker!,表示一切 OK。

  

docker-maven-plugin

  

  使用这种方式就需要我们自己维护 Dockerfile 了,这也是之前传统的构建方式,分为两个版本:

  • fabric8:开源的集成开发平台,为基于 Kubernetes、Docker 和 Jenkins 的微服务提供持续发布
  • spotify:Spotify(声田)一个正版流媒体音乐服务平台内部团队开发的插件

  

fabric8

  

准备工作

  

Docker 暴露 2375 端口

1
2
3
4
5
6
7
8
# 修改配置文件
vim /lib/systemd/system/docker.service
# 注释原有的 ExecStart 添加以下内容
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375
# 重新加载配置
systemctl daemon-reload
# 重启 Dcoker
systemctl restart docker

  

启动 Docker registry 私有仓库

私有镜像仓库搭建请参考:Docker 私有镜像仓库的搭建及认证

搭建成功以后,打开浏览器输入:http://192.168.10.104:5000/v2/_catalog 看到 {"repositories":[]} 表示私有仓库搭建成功。

  

pom

  

  添加 spring-boot-maven-plugin 打包插件和 docker-maven-plugin 构建镜像插件。

 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
<build>
    <plugins>
        <!-- spring-boot-maven-plugin -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!-- fabric8 的 docker-maven-plugin -->
        <plugin>
            <groupId>io.fabric8</groupId>
            <artifactId>docker-maven-plugin</artifactId>
            <version>0.34.1</version>
            <!-- 全局配置 -->
            <configuration>
                <!-- 配置远程 Docker 守护进程 url -->
                <dockerHost>http://192.168.10.10:2375</dockerHost>
                <!-- 镜像相关配置,支持多镜像 -->
                <images>
                    <!-- 单个镜像配置 -->
                    <image>
                        <!-- 镜像名:版本号 -->
                        <name>${project.artifactId}:${project.version}</name>
                        <!--
                            镜像仓库(公共仓库、阿里云仓库、私有自建仓库)配置,用于推送/拉取镜像
                            如果不想推送至镜像仓库则无需配置
                        -->
                        <registry>192.168.10.10:5000</registry>
                        <!-- 镜像 build 相关配置 -->
                        <build>
                            <!-- 使用 Dockerfile 文件,默认地址是 src/main/docker -->
                            <dockerFile>Dockerfile</dockerFile>
                            <!-- 或者指定地址例如:从项目根路径开始找 项目名/docker -->
                            <!--<dockerFile>${project.basedir}/docker/Dockerfile</dockerFile>-->
                            <!--
                                配置构建镜像时所需要的资源
                                配置项说明:http://maven.fabric8.io/#build-assembly-descriptor
                            -->
                            <assembly>
                                <descriptorRef>artifact</descriptorRef>
                            </assembly>
                        </build>
                    </image>
                </images>
                <!-- 认证配置,用于镜像仓库认证 -->
                <!--
                <authConfig>
                    <username>用户名</username>
                    <password>密码</password>
                </authConfig>
                -->
            </configuration>
        </plugin>
    </plugins>
</build>

  

Dockerfile

  

  在项目 src/main/docker 目录下添加 Dockerfile 文件,添加以下内容。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 基础镜像
FROM openjdk:alpine
# 作者信息
LABEL maintainer="mrhelloworld.com"
# 容器开放端口
EXPOSE 8080
# 将 target/docker/镜像名/版本号/build/maven 目录的 jar 包拷贝到 docker 中,并重命名为 xxx.jar
ADD maven/spring-boot-docker-1.0-SNAPSHOT.jar spring-boot-docker.jar
# 容器启动执行命令
ENTRYPOINT ["java", "-jar", "spring-boot-docker.jar"]

  

打包并推送

  

  执行以下命令进行镜像构建并推送至 Docker 服务:

1
mvn clean package docker:build -Dmaven.test.skip=true

  执行以下命令进行镜像构建并推送至仓库(默认推送公共仓库,除非配置了阿里云仓库或者私有自建仓库):

1
mvn clean package docker:build docker:push -Dmaven.test.skip=true

  推送至 Docker 服务如下:

/resources/articles/spring/spring-boot/docker/image-20210225180956551.png

  推送至私有镜像仓库如下:

/resources/articles/spring/spring-boot/docker/image-20210225181054588.png

/resources/articles/spring/spring-boot/docker/image-20210225194516188.png

  运行命令启动容器:

1
2
3
4
# 直接通过推送至 Docker 的镜像启动容器
docker run -di --name spring-boot-docker -p 8080:8080 spring-boot-docker:1.0-SNAPSHOT
# 通过私有镜像仓库启动容器
docker run -di --name spring-boot-docker -p 8080:8080 192.168.10.10:5000/spring-boot-docker:1.0-SNAPSHOT

  浏览器访问:http://192.168.10.10:8080/,页面返回:Hello Spring Boot Docker!,表示一切 OK。

  

spotify

  

准备工作

  

Docker 暴露 2375 端口

1
2
3
4
5
6
7
8
# 修改配置文件
vim /lib/systemd/system/docker.service
# 注释原有的 ExecStart 添加以下内容
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375
# 重新加载配置
systemctl daemon-reload
# 重启 Dcoker
systemctl restart docker

  

启动 Docker registry 私有仓库

私有镜像仓库搭建请参考:Docker 私有镜像仓库的搭建及认证

搭建成功以后,打开浏览器输入:http://192.168.10.104:5000/v2/_catalog 看到 {"repositories":[]} 表示私有仓库搭建成功。

  

pom

  

  添加 spring-boot-maven-plugin 打包插件和 docker-maven-plugin 构建镜像插件。

 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
<build>
    <plugins>
        <!-- spring-boot-maven-plugin -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!-- spotify 的 docker-maven-plugin -->
        <plugin>
            <groupId>com.spotify</groupId>
            <artifactId>docker-maven-plugin</artifactId>
            <version>1.2.2</version>
            <!-- 全局配置 -->
            <configuration>
                <!-- 配置远程 Docker 守护进程 url -->
                <dockerHost>http://192.168.10.10:2375</dockerHost>
                <!-- Dockerfile 文件路径 -->
                <dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
                <!-- 覆盖相同标签镜像 -->
                <forceTags>true</forceTags>
                <!--
                    镜像仓库(公共仓库、阿里云仓库、私有自建仓库等)配置
                    需要在 Maven 的 settings.xml 文件中 servers 节点下
                    配置 serverId 对应的服务相关信息
                -->
                <serverId>registry</serverId>
                <!-- 仓库地址/镜像名:版本号 -->
                <imageName>192.168.10.10:5000/${project.artifactId}:${project.version}</imageName>
                <!-- 配置构建镜像时所需要的资源 -->
                <resources>
                    <resource>
                        <!-- 指定要复制的目录路径,pom.xml 文件所在的当前目录 -->
                        <targetPath>/</targetPath>
                        <!-- 指定要复制的根目录,这里是 target 目录 -->
                        <directory>${project.build.directory}</directory>
                        <!-- 指定需要拷贝的文件,这里指最后生成的 jar 包 -->
                        <include>${project.build.finalName}.jar</include>
                    </resource>
                </resources>
            </configuration>
        </plugin>
    </plugins>
</build>

  如果添加了其他仓库,对应的服务相关信息在 Maven 的 settings.xml 文件中 servers 节点下添加以下代码。

1
2
3
4
5
6
7
8
<server>
    <id>registry</id>
    <username>用户名</username>
    <password>密码</password>
    <configuration>
        <email>邮箱地址</email>
    </configuration>
</server>

  

Dockerfile

  

  在项目 src/main/docker 目录下添加 Dockerfile 文件,添加以下内容。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 基础镜像
FROM openjdk:alpine
# 作者信息
LABEL maintainer="mrhelloworld.com"
# 容器开放端口
EXPOSE 8080
# 将 target/docker 目录(打包以后 Dockerfile 和 jar 包在同一目录下)的 jar 包拷贝到 docker 中,并重命名为 xxx.jar
ADD spring-boot-docker-1.0-SNAPSHOT.jar spring-boot-docker.jar
# 容器启动执行命令
ENTRYPOINT ["java", "-jar", "spring-boot-docker.jar"]

  

打包并推送

  

  执行以下命令进行镜像构建并推送至 Docker 服务:

1
mvn clean package docker:build -Dmaven.test.skip=true

  执行以下命令进行镜像构建并推送至仓库(默认推送公共仓库,除非配置了阿里云仓库或者私有自建仓库):

1
mvn clean package docker:build docker:push -Dmaven.test.skip=true

  推送至 Docker 服务如下:

/resources/articles/spring/spring-boot/docker/image-20210225191503989.png

  推送至私有镜像仓库如下:

/resources/articles/spring/spring-boot/docker/image-20210225181054588.png

/resources/articles/spring/spring-boot/docker/image-20210225194516188.png

  运行命令启动容器:

1
docker run -di --name spring-boot-docker -p 8080:8080 192.168.10.10:5000/spring-boot-docker:1.0-SNAPSHOT

  浏览器访问:http://192.168.10.10:8080/,页面返回:Hello Spring Boot Docker!,表示一切 OK。

  

dockerfile-maven-plugin

  

  除此之外,spotify 还提供了 dockerfile-maven-plugin 插件,感兴趣的同学请自行学习。

  该插件要求必须提供 Dockerfile 文件,而且必须放在项目根目录下,即与 pom.xml 文件同级。且不需要像 docker-maven-plugin 插件那样指定 Dockerfile 文件存放路径的 dockerDirectory 参数。还可以在 Dockerfile 中以 target 开头的相对路径来引用 maven 构建的资源,其他方面则大同小异都差不多。

  至此 Spring Boot 多样化构建 Docker 镜像所有的知识点就讲解结束了。

/resources/articles/articles_bottom/end02.gif

本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议

大家可以通过 分类 查看更多关于 Spring Boot 的文章。

  

🤗 您的点赞转发是对我最大的鼓励和支持。

📢 扫码关注 哈喽沃德先生「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~

/resources/mrhelloworld/qrcode/OfficialAccounts500-500.gif

「 感谢支持 」
 评论