一、背景
随着服务越来越多,以及项目越来越复杂,在上线发布高峰时段镜像上传、容器创建时间很长。Jar包越来越大,编译打包、上传下载镜像会占用大量带宽,造成拥堵。因此改善这种情况,一方面可以通过合理的依赖配置减小jar包体积,另一方面还可以从镜像入手。
二、容器、镜像、Docker
容器是一种虚拟化技术,最早可以追溯到1979年,Unix版本7引入的Chroot Jail及Chroot系统调用,它可以实现进程及其子进程与系统隔离。2007年谷歌发布了CGroups,支持限制和隔离进程的资源使用(CPU,内存,磁盘I / O,网络等)。2013年Docker发布了第一个版本,容器化进入迸发期,Docker核心的创新是容器镜像,一种新型的应用打包、分发和运行机制。
容器镜像将应用运行环境,包括代码、依赖库、工具、资源文件和元信息等,打包成一种操作系统发行版无关的不可变更软件包。
三、构建镜像
3.1、通过容器打包
1 2 3 4 5 6
| docker run -it --name=base -p 8080:8080 openjdk:8
docker cp ./server-0.0.0-SNAPSHOT.jar base:/
docker commit --change='CMD ["java", "-jar", "./server-0.0.0-SNAPSHOT.jar", "--spring.profiles.active=qa"]' base server:commit
|
3.2、Dockerfile
创建一个Dockerfile
文件,添加如下内容:
1 2 3 4 5 6 7 8 9 10
| FROM openjdk:8
ENV spring.profiles.active=qa
COPY ./server-0.0.0-SNAPSHOT.jar /
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/server-0.0.0-SNAPSHOT.jar"]
|
把Dockerfile
和server-0.0.0-SNAPSHOT.jar
放在同一个目录,然后在这个目录下执行docker build -t server:dockerfile .
,就可以成功构建一个镜像了。
3.3、Jib
Jib是一个开源的快速而简单的容器镜像构建工具,可以脱离Docker环境,也不需要编写Dockerfile,只需要添加一个Maven插件(同时提供了Gradle插件)就将Java程序打包成一个容器镜像,并支持Docker和OCI格式。
Jib充分利用镜像分层的原理,将程序、资源、依赖打包到不同的层,每次只重新构建、推送发生变化的层,加快构建、部署速度。
如何使用Jib构建镜像呢?首先,在项目中增加Jib的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
| <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>3.1.4</version> <dependencies> <dependency> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-layer-filter-extension-maven</artifactId> <version>0.1.0</version> </dependency> </dependencies>
<configuration> <from> <image>openjdk:8</image> </from> <to> <image>server:jib</image> </to> <container> <environment> <spring.profiles.active>qa</spring.profiles.active> </environment> <ports> <port>8080</port> </ports> </container> </configuration> </plugin>
|
然后,执行mvn compile jib:build
打包,就可以构建容器并推送到远程仓库了。
也可以将构建过程绑定到其他命令,在Maven插件配置中增加如下配置,将打包镜像绑定到package
命令中:
1 2 3 4 5 6 7 8
| <executions> <execution> <phase>package</phase> <goals> <goal>build</goal> </goals> </execution> </executions>
|
在实际使用过程中,我们的项目是多模块,并且有模块间的依赖的,而Jib默认会把jar包放在同一层,还会有一些引用的jar包会频繁的变动,如果这些包和其他不常变的依赖放在同一层的话,每次变动还是要上传大量文件。
Jib的Maven插件从2.3.0版本(Gradle插件为2.4.0)开始提供了一个可扩展的能力,并且提供了一个镜像层过滤的插件(Jib Layer-Filter Extension
),通过这个插件就按一定规则可以将一部分包单独打在一个镜像层,从而将频繁变动的包和不常变的基础包分离。
在Jib插件的configuration
节点下增加如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <pluginExtensions> <pluginExtension> <implementation> com.google.cloud.tools.jib.maven.extension.layerfilter.JibLayerFilterExtension </implementation> <configuration implementation="com.google.cloud.tools.jib.maven.extension.layerfilter.Configuration"> <filters> <filter> <glob>**/xxx-*.jar</glob> <toLayer>xxx libraries</toLayer> </filter> <filter> <glob>**/xxx-aaa-*.jar</glob> <toLayer>aaa dependencies</toLayer> </filter> </filters> </configuration> </pluginExtension> </pluginExtensions>
|
四、Jib实现分层的基本原理
使用Jib默认打包后的应用结构在容器中的结构为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| . |____classes | |____com | | |____peppa | | |____... |____resources | |____application.properties | |____application-online.properties | |____... |____libs | |____accessors-smart-1.2.jar | |____activation-1.1.jar | |____android-json-0.0.20131108.vaadin1.jar | |____...
|
可以使用maven-dependency-plugin
和maven-resources-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
| <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> <overWriteReleases>false</overWriteReleases> <overWriteSnapshots>false</overWriteSnapshots> <overWriteIfNewer>true</overWriteIfNewer> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <executions> <execution> <id>copy-resources</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/resources</outputDirectory> <resources> <resource>src/main/resources</resource> </resources> </configuration> </execution> </executions> </plugin>
|
打包之后在target目录使用命令java -cp /app/resources:/app/classes:/app/libs/* win.wellcoding.jib.Application
就可以启动了。