Spring Boot 构建docker镜像

Spring官网提供了一个简明的基于SpringBoot项目构建docker镜像的教程(Getting Started | Spring Boot with Docker)。这个教程给我了很大的启发,结合我现在实际项目,又进行一些改造。

引入多阶段构建

原始教程都是基于构建好的jar包做操作,这样就要求宿主机配置好JDK和Maven,这样就存在在不同的宿主机下,可能构建出不同的镜像,不够The Docker Way

通过多阶段构建我们一个在一个 Dockerfile 里使用多个FROM。每一个FROM都可以使用不同的基础镜像,没一个都开启一个构建的新阶段。 我们可以选择性的复制构建物从一个阶段到另一个,这样在最终的镜像里就不会有我们不需要的中间产物了。

减少文件复制

构建时.git,target等目录是不需要的,创建.dockerignore文件:

减少与Maven服务器交互

如果项目严格按照每次部署jar包时版本号都改变,可以认为pom文件没发生变化时maven不需要向服务器更新依赖. mvn dependency:go-offline 可以下载所有的项目依赖,后续的所有mvn操作我们都可以添加-o参数,maven就工作的离线模式,不再需要和maven服务器交互,提高构建速度。

缓存maven repository

现在每个项目的构建都有一个独立的本地maven repository,极大的浪费磁盘空间,同时也浪费网络带宽。共享maven repository有两个选择: 一个是通过挂载VOLUME,或者利用BuildKit(>=18.09)的Cache.
VOLUME:

BuildKit:

启用BuildKit

docker默认是不启用BuildKit的。可以在执行docker build之前设置环境变量DOCKER_BUILDKIT=1,例如:

如果想默认启用BuildKit,可以通过配置/etc/docker/daemon.json,然后重启daemon。

更精细的镜像分层

原始教程镜像分为/BOOT-INF/lib,/META-INF,/BOOT-INF/classes 三层。对于普通项目来说是够用的,但是我司有一些自有jar包,更新频率高于三方的外部库,同时低于业务代码。为最大程序利用 docker 构建缓存及镜像层,我把/BOOT-INF/lib分为两层,一层是外部依赖的jars,一层是公司的jars。

jar包的分层我是通过maven的dependency:copy-dependencies并指定excludeGroupIdsincludeGroupIds来实现的。

额外要注意:
1. 如果项目有更精细的分层需求,有一点要注意,COPY 虽然支持模糊匹配,但是在复制目录的时候会把匹配上的目录内的内容复制到目的文件夹下。这样文件夹目录少了一层。
2. Maven 3.8.1禁止了HTTP repository, 必须使用HTTPS协议。 可以升级HTTP为HTTPS,或者添加一个mirror,或者版本降到3.6.3。
3. 不要追求镜像的绝对最小,像alpine这种镜像很多命令、基础库都和我们日常使用的Linux不一样,虽然一共能省了不到100M空间(docker基础层是共享的)但留下很多隐患。

最终的Dockerfile: