FAQ docker執行tomcat提示找不到檔案

2023-03-07 12:02:03

docker執行tomcat提示找不到檔案

問題描述

  • docker課程中,老師是用tomcat映象來演示docker的一些操作
  • 但同樣的操作有的同學是ok的,有的同學就會遇到如下錯誤

  • 核心資訊

    Exited(1)
    
    Cannot find /usr/local/tomcat/bin/setclasspath.sh
    This file is needed to run this program
    
  • 一開始也挺費解的,我雖然不上這個課,但也比較好奇,自己始終無法復現,但不斷有學員問,我看到就回復,在docker run命令後加一個--privileged即可

  • 但為何呢,不能說的很清楚,因為--privileged這個引數就是讓你容器內的root使用者具備擁有真正的root許可權。否則容器內的root只是外部的一個普通使用者許可權。

線索一:容器退出碼

  • 從上面的提示可以看到容器退出了,ExitCode是1
  • 1的意思是:
    • 程式錯誤,或者Dockerfile中參照不存在的檔案,如 entrypoint中參照了錯誤的包
    • 程式錯誤可以很簡單,例如「除以0」,也可以很複雜,比如空參照或者其他程式 crash
  • ExitCode1: Indicates failure due to application error
  • Indicates that the container stopped due to either an application error or an incorrect reference in Dockerfile to a file that is not present in the container.
  • An application error can be as simple as 「divide by 0」 or as complex as 「Reference to a bean name that conflicts with existing, non-compatible bean definition of same name and class.」
  • An incorrect reference in Dockerfile to a file not present in the container can be as simple as a typo (the example below has sample.ja instead of sample.jar)

  • 知道了這個似乎幫助不大,不過有的容器退出碼是非常能指向原因的,比如ExitCode 0

線索二:無法找到檔案

  • 這個線索就非常重要了

  • 那為何會無法找到,真的有嗎?有的

  • 比如在我這個正常的容器中

    [root@hecs-67651 ~]# docker ps -a
    CONTAINER ID   IMAGE                                        COMMAND                  CREATED          STATUS          PORTS                                                                                      NAMES
    59463bed0fd7   tomcat                                       "catalina.sh run"        35 minutes ago   Up 35 minutes   8080/tcp                                                                                   mytomcat5
    
    [root@hecs-67651 ~]# docker exec -it 594 ls /usr/local/tomcat/bin/setclasspath.sh
    /usr/local/tomcat/bin/setclasspath.sh
    
    
  • 那遇到問題的學員為何找不到呢?

  • 我們的這個tomcat映象在啟動的時候會執行一個指令碼

    [root@hecs-67651 ~]# docker inspect -f '{{.Config.Cmd}}' tomcat:latest
    [catalina.sh run]
    
    
  • 來看看catalina.sh做了啥

順藤摸瓜:catalina.sh

  • 這個shell指令碼比較大646行,我就摘錄關鍵部分

  • 你看懂需要懂一些shell

  • 第一部分:報錯在哪裡

    if $os400; then
      # -r will Only work on the os400 if the files are:
      # 1. owned by the user
      # 2. owned by the PRIMARY group of the user
      # this will not work if the user belongs in secondary groups
      . "$CATALINA_HOME"/bin/setclasspath.sh
    else
      if [ -r "$CATALINA_HOME"/bin/setclasspath.sh ]; then
        . "$CATALINA_HOME"/bin/setclasspath.sh
      else
        echo "Cannot find $CATALINA_HOME/bin/setclasspath.sh"
        echo "This file is needed to run this program"
        exit 1
      fi
    fi
    
    
    • 可以看到我們的報錯就在這裡

    • 執行的時候[ -r "$CATALINA_HOME"/bin/setclasspath.sh ]這個分支為假就走到了我們的報錯中,exit 1

    • 這句話的意思是看 "$CATALINA_HOME"/bin/setclasspath.sh檔案是否有read許可權

      root@59463bed0fd7:/usr/local/tomcat/bin# ll setclasspath.sh
      -rwxr-xr-x 1 root root 3342 Mar  6 23:33 setclasspath.sh*
      
      
    • 在我這個OK的環境中的許可權如上,read是有的

    • 那可能的問題就是在CATALINA_HOME這個變數是否存在

    • 而再往前看我們走到第一個else是因為$os400為假

  • 第二部分:os400(僅供學習,對本問題沒有作用,無需分析)

    cygwin=false
    darwin=false
    os400=false
    hpux=false
    case "`uname`" in
    CYGWIN*) cygwin=true;;
    Darwin*) darwin=true;;
    OS400*) os400=true;;
    HP-UX*) hpux=true;;
    esac
    
    
    • 從這裡可以看到os400初始值為false,只有你的uname是OS400的時候才為true

    • 而我們這個環境的uname的值是

      [root@59463bed0fd7 ~]# uname
      Linux
      
  • 第三部分:[ -r "$CATALINA_HOME"/bin/setclasspath.sh ]

    • 等價於 test -r "$CATALINA_HOME"/bin/setclasspath.sh

    • 我這個OK的環境執行效果

      root@59463bed0fd7:/usr/local/tomcat/bin# [ -r "$CATALINA_HOME"/bin/setclasspath.sh ]
      root@59463bed0fd7:/usr/local/tomcat/bin# echo $?
      0
      
      
    • 可以看到,是為0的返回值,那自然就不會報錯,報錯的環境肯定是非0 的

    • 問題的焦點似乎就集中到了$CATALINA_HOME上

  • 第四部分:$CATALINA_HOME怎麼來的

    # 下面的意思是如果沒有CATALINA_HOME這個變數就設定為cd "$PRGDIR/.." >/dev/null; pwd 這個pwd的結果
    [ -z "$CATALINA_HOME" ] && CATALINA_HOME=`cd "$PRGDIR/.." >/dev/null; pwd`
    
    # 而PRGDIR是這麼來的
    PRGDIR=`dirname "$PRG"`
    
    # PRG來自
    PRG="$0"  # 就是catalina.sh所在目錄
    
    # 下面的我也有點看不懂了,大致就是獲取目錄
    while [ -h "$PRG" ]; do
      ls=`ls -ld "$PRG"`
      link=`expr "$ls" : '.*-> \(.*\)$'`
      if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
      else
        PRG=`dirname "$PRG"`/"$link"
      fi
    done
    
    
    
  • 找了半天找了個寂寞?好像是的。那問題到底在哪裡呢?我也沒法復現。捋一捋

  • 線索:[ -r "$CATALINA_HOME"/bin/setclasspath.sh ] 執行為非0是肯定的

  • 如果檔案存在,變數存在,那問題就只能是-r了,許可權問題!

解決方法

  • 在docker run命令後加一個--privileged即可

  • --privileged這個引數就是讓你容器內的root使用者具備擁有真正的root許可權。否則容器內的root只是外部的一個普通使用者許可權。

  • 往上有個檔案說是:與faccessat2系統呼叫有關,由於 runc 中的 bug,如果您的核心不支援 faccessat2,它將失敗。這有點難了~看不懂

    https://syntaxbug.com/6d684d2afe/