Jib building image problem from the location to the in-depth analysis

Description of problem

By Jib plug-SpringBoot project made into a successful Docker mirror, but a mirror running time error (Could not the Find the Load main class $ {or} Start-class) , today to analyze the problem together, hoping to help the reader to skip the pits.

About Jib plug

Jib can use Maven plug-in project will be built into the current Java project Docker mirroring, please refer to:

  1. "Docker and Jib (maven plug-in version) combat" ;
  2. "Jib Use Summary (Maven plug-in version)" ;

Environmental Information

  1. Operating System: macOS Mojave 10.14.6 (18G103)
  2. JDK:10.14.6 (18G103)
  3. Docker:10.14.6 (18G103)
  4. SpringBoot:2.1.8.RELEASE
  5. Jib Plugin Version: 1.6.1

Source download

To reproduce the problem, I will upload the problem of SpringBoot project to GitHub, address and link information in the following table:

name link Remark
Project Home https://github.com/zq2599/blog_demos The project's home page on GitHub
git repository address (https) https://github.com/zq2599/blog_demos.git The project's source code repository address, https protocol
git repository address (ssh) [email protected]:zq2599/blog_demos.git The project's source code repository address, ssh protocol

The git item has multiple folders in the application of this chapter jib-error-demo folder, as shown in FIG red box:
Here Insert Picture Description

problem:

  1. Pom.xml file directory execute the command mvn Clean the compile -U , mirroring can build successful, but the console output of a warning message, as shown below:
    Here Insert Picture Description
  2. 尝试用此镜像创建容器,行命令docker run --name=test bolingcavalry/hellojib:0.0.1-SNAPSHOT,报错如下:
CN0014005932:~ zhaoqin$ docker run --name=test bolingcavalry/hellojib:0.0.1-SNAPSHOT
Error: Could not find or load main class ${start-class}
  1. docker ps -a查看容器信息如下,只能看到状态是"退出",别的没啥了:
CN0014005932:~ zhaoqin$ docker ps -a
CONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS                     PORTS               NAMES
d618f6588821        bolingcavalry/hellojib:0.0.1-SNAPSHOT   "java -Xms4g -Xmx4g …"   4 minutes ago       Exited (1) 4 minutes ago                       test
  1. 不甘心,用命令docker ps -a --no-trunc查看未截断的容器信息:
CN0014005932:~ zhaoqin$ docker ps -a --no-trunc
CONTAINER ID                                                       IMAGE                                   COMMAND                                                                           CREATED             STATUS                     PORTS               NAMES
d618f6588821f00d3bd0b67a85ff92988b90dfff710370c9d340d5c544c550af   bolingcavalry/hellojib:0.0.1-SNAPSHOT   "java -Xms4g -Xmx4g -cp /app/resources:/app/classes:/app/libs/* ${start-class}"   7 minutes ago       Exited (1) 7 minutes ago                       test
  1. 这次有新发现,容器启动时执行命令是java -Xms4g -Xmx4g -cp /app/resources:/app/classes:/app/libs/* ${start-class},怪哉!这个${start-class}是什么?我们来看一个正常镜像的启动命令:
java -Xms4g -Xmx4g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.jiberrordemo.JibErrorDemoApplication

如上所示,com.bolingcavalry.jiberrordemo.JibErrorDemoApplication是main方法所在类,此命令可以正常运行JibErrorDemoApplication类的main方法;
6. 小结问题:容器启动时执行java命令,把${start-class}作为参数传给java,导致java无法处理此参数,所以进程报错,导致容器退出;

问题原因

此问题的原因很简单:java工程中带有main方法的类不止一个,去查看jib-error-demo工程的代码,发现Utils.java中果然有个main方法:

public class Utils {

    public static String time(){
      return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()).toString();
    }

    public static void main(String[] args){
        System.out.println(time());
    }
}

将上述main方法删除掉,再构建镜像并运行容器,证实问题已经解决。

另一种解决问题的方法

如果不想动Utils类的代码(也许jar包中某个类带有main方法),请打开pom.xml文件,在jib插件的配置中增加mainClass节点,节点内容是指定的class类,如下图红框所示:
Here Insert Picture Description
经过上面的设置,问题也可以解决。

接下来,如果您有兴趣了解更深层次的原因,咱们一起来深度探险吧。

查找问题

  1. 这个问题在Jib的官方GitHub上是有记录的,先看第一条,地址是:https://github.com/GoogleContainerTools/jib/issues/1601 ,如下图红框所示,同样的问题,最后issue的发起人自己关闭了这个issue,因为他发现这自己的项目中有两个带有main方法的类:
    Here Insert Picture Description
  2. 再来看看这个issue, https://github.com/GoogleContainerTools/jib/issues/170 ,Jib的作者Q Chen推测是Spring将${start-class}这个字符串设置为Main-Class属性的值(个人感觉,这里说的Spring应该是spring boot的mave插件吧),于是Jib插件在使用Main-Class的值得时候,拿到的就是${start-class}这个字符串了:
    Here Insert Picture Description
  3. 170这个issue的后续情节很有意思,Jib作者Q Chen对这个问题也很纠结,如果Java工程中发现了多个带有main方法的类,Jib究竟该如何处理呢?Q Chen最后决定输出警告,如下图:
    Here Insert Picture Description
  4. 一起来看看这段代码吧,也就是上图中#288,地址是:https://github.com/GoogleContainerTools/jib/pull/228/files/c8757e1f9ea47edd78df18142de7836a68f22034 ,如果mainClass不像一个class类的名称,就输出警告,这个逻辑在Gradle和Maven插件中都写入了:
    Here Insert Picture Description
  5. 最后一个问题:上面代码中的mainClass从哪来的?打开上图的源码,地址是:https://github.com/GoogleContainerTools/jib/blob/c8757e1f9ea47edd78df18142de7836a68f22034/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java ,如下图红框,从方法名可以推测,该值来自构建SpringBoot工程的maven插件,所以前面Q Chen提到main-class变量的值是Spring修改的,应该是来自这段代码:
    Here Insert Picture Description
    此时的您,如果依然意犹未尽,咱们再来巩固一下SpringBoot的start-class

关于start-class

  1. 熟悉SpringBoot的同学其实对${start-class}并不陌生,当工程中多个类中都有main方法时,使用该参数来指定SpringBoot的启动类;
  2. 先看SpringBoot官方文档熟悉一下start-class,地址是:https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/ ,下图内容比较关键:我们设置的启动类被指定到Start-Class属性中,而Main-Class属性变成了org.springframework.boot.loader.JarLauncher,这才是SpringBoot真正的启动类:
    Here Insert Picture Description
  3. 如下图,这是个补充说明,Main-Class属性的值被转移到Start-Class属性这个动作,是maven插件在构建jar的时候做的:
    Here Insert Picture Description
  4. 所以start-class的值是来自main-class,再看main-class的值从哪里来,如下图红框所示,maven插件会去查找带有public static void main(String[] args)的类:
    Here Insert Picture Description
    至此,Jib构建的镜像问题分析完毕,一个小小的问题引发了这么多学习和探索,虽然有点费时间,但是可以让人再次感受到"技术是相通的"感觉,不知道您有没有这种感觉呢?
发布了328 篇原创文章 · 获赞 946 · 访问量 117万+

Guess you like

Origin blog.csdn.net/boling_cavalry/article/details/101606958