前言
随着微服务概念的持续演进,容器化技术的火爆,对于广大基于 Java 语言开发的程序员而言,将开发好的项目快速构建为 Docker 镜像推送至服务器也成为了一项必会的技能。
伴随着技术的不断更新,基于 Spring Boot 构建 Docker 镜像的方式也是五花八门,大致分为以下几种:
Cloud Native Buildpacks
(Spring Boot 2.3+ 版本开始支持)
Google
的 jib-maven-plugin
fabric8
和 spotify
的 docker-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 版本开始又进行了进一步优化:官网文档。
说明
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
|
推送成功效果如下:
运行命令启动容器:
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 服务如下:
推送至私有镜像仓库如下:
运行命令启动容器:
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 服务如下:
推送至私有镜像仓库如下:
运行命令启动容器:
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 镜像所有的知识点就讲解结束了。
本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议
。
大家可以通过 分类
查看更多关于 Spring Boot
的文章。
🤗 您的点赞
和转发
是对我最大的鼓励和支持。
📢 扫码关注 哈喽沃德先生
「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~