1. 程式人生 > >openjdk-alpine映象無法列印執行緒堆疊和記憶體堆疊問題

openjdk-alpine映象無法列印執行緒堆疊和記憶體堆疊問題

基於openjdk:8u171-alpine構建的java映象,使用jstack命令列印執行緒的時候會提示以下錯誤:

/opt # ps -ef
PID USER TIME COMMAND
1 root 0:28 /usr/lib/jvm/java-1.8-openjdk/bin/java -jar /test/lib/test.jar
66 root 0:00 /bin/sh
70 root 0:00 ps

/opt # jstack 1
1: Unable to get pid of LinuxThreads manager thread

使用jmap命令嘗試了一下,也是一樣的錯誤。
換了一種啟動方式,使用/bin/sh啟動docker,然後進入docker手動啟動java程序,然後再用jstack命令,就能正常列印。
換了一個centos映象,手動安裝openjdk,在啟動docker的時候直接啟動java程序,然後進入docker,使用jstack命令,也可以正常列印。

通過這幾種嘗試,得出的結論是:
使用centos+java映象,可以正常列印執行緒堆疊,但是這種方式的缺陷就是映象太大,大約600M左右;
使用alpine+java映象,以/bin/sh方式啟動docker,然後手動啟動java程序,這時java程序的PID不為1,這種方式能夠正常列印執行緒堆疊。這種方式缺點就是java程序如果異常退出了,docker不會檢測到,所以無法做自動重啟等操作;
使用alpine+java映象,如果是以直接執行java程序的方式啟動docker,也就是說java程序的PID為1,這種方式無法正常列印執行緒堆疊。

查看了一下github上openjdk官方的問題答覆,確實是存在這種情況,並且openjdk的維護成員看上去也無法解決這個問題。
但是有一個曲線解決方法,就是在啟動docker的時候先執行一個tini程序,然後通過tini程序去執行java程序。
這種方式java程序的PID不為1,能夠列印堆疊,同時如果java程序退出,tini也能檢測到,並通知到docker,docker來做相關的處理,完美的解決了這個問題。

參考Dockerfile如下:

FROM openjdk:8u171-alpine
RUN apk add tini
ENTRYPOINT ["tini"]

這裡的RUN命令指定在製作映象的時候通過apk管理工具安裝了tini程式。ENTRYPOINT命令指定在執行docker的時候要執行tini程式,具體的java程式作為引數傳給tini。
我們有多個java微服務,所以不能把tini裝在每個java微服務映象中,而是裝在我們自己的基礎映象中。

具體服務的Dockerfile如下:

FROM java
ADD build/bootScripts /test/bin
ADD build/libs /test/lib
WORKDIR /test
EXPOSE 8080
CMD /test/bin/test

這裡的/test/bin/test就是傳給tini的實際要執行的啟動命令。

參考資料:
https://github.com/docker-library/openjdk/issues/76
https://github.com/krallin/tini/issues/8