Jib構建鏡像的問題分析(Could not find or load main class ${start-class})

問題簡述

經過Jib插件將SpringBoot工程製做成Docker鏡像成功,可是運行鏡像的時候報錯(Could not find or load main class ${start-class}),今天來一塊兒分析這個問題,但願能幫讀者跳太小坑。html

關於Jib插件

在Maven工程中可使用Jib插件將當前Java工程構建成Docker鏡像,詳情請參考:java

  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

名稱 連接 備註
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協議
git倉庫地址(ssh) git@github.com:zq2599/blog_demos.git 該項目源碼的倉庫地址,ssh協議

</br>程序員

這個git項目中有多個文件夾,本章的應用在jib-error-demo文件夾下,以下圖紅框所示: 在這裏插入圖片描述github

問題:

  1. 在pom.xml文件所在目錄執行命令<font color="blue">mvn clean compile -U</font>,鏡像能夠構建成功,可是控制檯輸出了警告信息,以下圖: 在這裏插入圖片描述
  2. 嘗試用此鏡像建立容器,行命令<font color="blue">docker run --name=test bolingcavalry/hellojib:0.0.1-SNAPSHOT</font>,報錯以下:
CN0014005932:~ zhaoqin$ docker run --name=test bolingcavalry/hellojib:0.0.1-SNAPSHOT
Error: Could not find or load main class ${start-class}
  1. <font color="blue">docker ps -a</font>查看容器信息以下,只能看到狀態是"退出",別的沒啥了:
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. 不甘心,用命令<font color="blue">docker ps -a --no-trunc</font>查看未截斷的容器信息:
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. 此次有新發現,容器啓動時執行命令是<font color="blue">java -Xms4g -Xmx4g -cp /app/resources:/app/classes:/app/libs/* ${start-class}</font>,怪哉!這個<font color="red">${start-class}</font>是什麼?咱們來看一個正常鏡像的啓動命令:
java -Xms4g -Xmx4g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.jiberrordemo.JibErrorDemoApplication

如上所示,<font color="blue">com.bolingcavalry.jiberrordemo.JibErrorDemoApplication</font>是main方法所在類,此命令能夠正常運行JibErrorDemoApplication類的main方法; 6. 小結問題:容器啓動時執行java命令,把<font color="blue">${start-class}</font>做爲參數傳給java,致使java沒法處理此參數,因此進程報錯,致使容器退出;spring

問題緣由

此問題的緣由很簡單:<font color="blue">java工程中帶有main方法的類不止一個</font>,去查看jib-error-demo工程的代碼,發現Utils.java中果真有個main方法:docker

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方法刪除掉,再構建鏡像並運行容器,證明問題已經解決。shell

另外一種解決問題的方法

若是不想動Utils類的代碼(也許jar包中某個類帶有main方法),請打開pom.xml文件,在jib插件的配置中增長<font color="blue">mainClass</font>節點,節點內容是指定的class類,以下圖紅框所示: 在這裏插入圖片描述 通過上面的設置,問題也能夠解決。app

接下來,若是您有興趣瞭解更深層次的緣由,我們一塊兒來深度探險吧。ssh

查找問題

  1. 這個問題在Jib的官方GitHub上是有記錄的,先看第一條,地址是:https://github.com/GoogleContainerTools/jib/issues/1601 ,以下圖紅框所示,一樣的問題,最後issue的發起人本身關閉了這個issue,由於他發現這本身的項目中有兩個帶有main方法的類: 在這裏插入圖片描述
  2. 再來看看這個issue, https://github.com/GoogleContainerTools/jib/issues/170 ,Jib的做者Q Chen推測是Spring將<font color="blue">${start-class}</font>這個字符串設置爲Main-Class屬性的值(我的感受,這裏說的Spring應該是spring boot的mave插件吧),因而Jib插件在使用Main-Class的值得時候,拿到的就是<font color="blue">${start-class}</font>這個字符串了: 在這裏插入圖片描述
  3. 170這個issue的後續情節頗有意思,Jib做者Q Chen對這個問題也很糾結,若是Java工程中發現了多個帶有main方法的類,Jib究竟該如何處理呢?Q Chen最後決定輸出警告,以下圖: 在這裏插入圖片描述
  4. 一塊兒來看看這段代碼吧,也就是上圖中#288,地址是:https://github.com/GoogleContainerTools/jib/pull/228/files/c8757e1f9ea47edd78df18142de7836a68f22034 ,若是mainClass不像一個class類的名稱,就輸出警告,這個邏輯在Gradle和Maven插件中都寫入了: 在這裏插入圖片描述
  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修改的,應該是來自這段代碼: 在這裏插入圖片描述 此時的您,若是依然意猶未盡,我們再來鞏固一下SpringBoot的start-class

關於start-class

  1. 熟悉SpringBoot的同窗其實對<font color="blue">${start-class}</font>並不陌生,當工程中多個類中都有<font color="blue">main</font>方法時,使用該參數來指定SpringBoot的啓動類;
  2. 先看SpringBoot官方文檔熟悉一下<font color="blue">start-class</font>,地址是:https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/ ,下圖內容比較關鍵:咱們設置的啓動類被指定到<font color="blue">Start-Class</font>屬性中,而<font color="blue">Main-Class</font>屬性變成了<font color="blue">org.springframework.boot.loader.JarLauncher</font>,這纔是SpringBoot真正的啓動類: 在這裏插入圖片描述
  3. 以下圖,這是個補充說明,<font color="blue">Main-Class</font>屬性的值被轉移到<font color="blue">Start-Class</font>屬性這個動做,是maven插件在構建jar的時候作的: 在這裏插入圖片描述
  4. 因此start-class的值是來自main-class,再看main-class的值從哪裏來,以下圖紅框所示,maven插件會去查找帶有<font color="blue">public static void main(String[] args)</font>的類: 在這裏插入圖片描述 至此,Jib構建的鏡像問題分析完畢,一個小小的問題引起了這麼多學習和探索,雖然有點費時間,可是可讓人再次感覺到"技術是相通的"感受,不知道您有沒有這種感受呢?

歡迎關注個人公衆號:程序員欣宸

在這裏插入圖片描述

相關文章
相關標籤/搜索