写点什么

Jib 构建镜像问题从定位到深入分析

作者:程序员欣宸
  • 2022-10-24
    广东
  • 本文字数:2842 字

    阅读完需:约 9 分钟

Jib构建镜像问题从定位到深入分析

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

问题简述

  • 通过 Jib 插件将 SpringBoot 工程制作成 Docker 镜像成功,但是运行镜像的时候报错 (Could not find or load main class ${start-class}) ,今天来一起分析这个问题,希望能帮读者跳过小坑。

关于 Jib 插件

  • 在 Maven 工程中可以使用 Jib 插件将当前 Java 工程构建成 Docker 镜像,详情请参考:


  1. 《Docker与Jib(maven插件版)实战》;

  2. 《Jib使用小结(Maven插件版)》;

环境信息

  1. 操作系统: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 插件版本:1.6.1

源码下载

  • 为了重现问题,我将出现问题的 SpringBoot 工程上传到 GitHub,地址和链接信息如下表所示:



  • 这个 git 项目中有多个文件夹,本章的应用在 jib-error-demo 文件夹下,如下图红框所示:

问题:

  • 在 pom.xml 文件所在目录执行命令 mvn clean compile -U ,镜像可以构建成功,但是控制台输出了警告信息,如下图:

  • 尝试用此镜像创建容器,行命令 docker run --name=test bolingcavalry/hellojib:0.0.1-SNAPSHOT ,报错如下:


CN0014005932:~ zhaoqin$ docker run --name=test bolingcavalry/hellojib:0.0.1-SNAPSHOTError: Could not find or load main class ${start-class}
复制代码


  • docker ps -a 查看容器信息如下,只能看到状态是"退出",别的没啥了:


CN0014005932:~ zhaoqin$ docker ps -aCONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS                     PORTS               NAMESd618f6588821        bolingcavalry/hellojib:0.0.1-SNAPSHOT   "java -Xms4g -Xmx4g …"   4 minutes ago       Exited (1) 4 minutes ago                       test
复制代码


  • 不甘心,用命令 docker ps -a --no-trunc 查看未截断的容器信息:


CN0014005932:~ zhaoqin$ docker ps -a --no-truncCONTAINER ID                                                       IMAGE                                   COMMAND                                                                           CREATED             STATUS                     PORTS               NAMESd618f6588821f00d3bd0b67a85ff92988b90dfff710370c9d340d5c544c550af   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
复制代码


  • 这次有新发现,容器启动时执行命令是 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 方法;

  • 小结问题:容器启动时执行 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 类,如下图红框所示:


  • 经过上面的设置,问题也可以解决。

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

查找问题

关于 start-class

  • 熟悉 SpringBoot 的同学其实对 ${start-class} 并不陌生,当工程中多个类中都有 main 方法时,使用该参数来指定 SpringBoot 的启动类;

  • 先看 SpringBoot 官方文档熟悉一下 start-class ,地址是:https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/ ,下图内容比较关键:我们设置的启动类被指定到 Start-Class 属性中,而 Main-Class 属性变成了 org.springframework.boot.loader.JarLauncher ,这才是 SpringBoot 真正的启动类:

  • 如下图,这是个补充说明, Main-Class 属性的值被转移到 Start-Class 属性这个动作,是 maven 插件在构建 jar 的时候做的:

  • 所以 start-class 的值是来自 main-class,再看 main-class 的值从哪里来,如下图红框所示,maven 插件会去查找带有 public static void main(String[] args) 的类:

  • 至此,Jib 构建的镜像问题分析完毕,一个小小的问题引发了这么多学习和探索,虽然有点费时间,但是可以让人再次感受到"技术是相通的"感觉,不知道您有没有这种感觉呢?

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...


发布于: 2022-10-24阅读数: 18
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018-04-19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
Jib构建镜像问题从定位到深入分析_Docker_程序员欣宸_InfoQ写作社区