0%

技术分享:使用Jib构建容器镜像

一、背景

随着服务越来越多,以及项目越来越复杂,在上线发布高峰时段镜像上传、容器创建时间很长。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
# 用命令把jar包复制到容器里
docker cp ./server-0.0.0-SNAPSHOT.jar base:/
# 将容器变动提交,这里在提交的时候指定了镜像的CMD部分
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
# 添加jar包
COPY ./server-0.0.0-SNAPSHOT.jar /
# 开放端口号
EXPOSE 8080
# 启动命令
ENTRYPOINT ["java", "-jar", "/server-0.0.0-SNAPSHOT.jar"]

Dockerfileserver-0.0.0-SNAPSHOT.jar放在同一个目录,然后在这个目录下执行docker build -t server:dockerfile .,就可以成功构建一个镜像了。

3.3、Jib

Jib[1]是一个开源的快速而简单的容器镜像构建工具,可以脱离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)开始提供了一个可扩展的能力[2],并且提供了一个镜像层过滤的插件(Jib Layer-Filter Extension[3]),通过这个插件就按一定规则可以将一部分包单独打在一个镜像层,从而将频繁变动的包和不常变的基础包分离。

在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-pluginmaven-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就可以启动了。


  1. https://github.com/GoogleContainerTools/jib

  2. https://github.com/GoogleContainerTools/jib-extensions

  3. https://github.com/GoogleContainerTools/jib-extensions/tree/master/first-party/jib-layer-filter-extension-maven

旺旺小学酥 微信

微信

旺旺小学酥 支付宝

支付宝