Featured image of post 麒麟系统tomcat无法启动

麒麟系统tomcat无法启动

Tomcat

事情缘由

公司软件的主要客户是政府和企事业单位,这两年来已经对国产化要求替代的需求越来越强烈,这个问题就是发生在银河麒麟操作系统下面的问题:

我们的程序是通过容器包装的tomcat应用,这是前提,有一天某项目组的运维找到我说,新版本的程序在Kylin V10 sp1上面跑不起来,容器在无限重启。有用的日志只有两行:

1
2
Cannot find /srv/tomcat8/bin/setclasspath.sh
This file is needed to run this program

看到这个的时候我是很不理解的,这玩意是不会有人改动的,这是tomcat自己的脚本,而且这个输出是catalina.sh的,谁会动那玩意。然后我去找了下catalina.sh脚本对应的逻辑:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Get standard Java environment variables
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

os400是IBM的系统,所以我们肯定是走的else,然后第二个if 明显是获取的false,才会走的else输出那个日志,退出tomcat导致容器退出。

我进容器试了下:

1
2
$ ln -alh "$CATALINA_HOME"/bin/setclassp
-rwxr-x---. 1 root root 3.7K 2月  24  2022 /srv/tomcat8/bin/setclasspath.sh

明显没有问题。所以这个地方不应该是false。

到这一步我已经意识到是linux kernel或者runc有问题了。

煎熬的排查过程

我首先上stackoverflow看看有没有人遇到了同样的问题,还真别说,还真有

shell - Tomcat 9 running on docker - Cannot find /usr/local/tomcat/bin/setclasspath.sh - Stack Overflow

他们的结论也是类似的,这是

Researching the faccessat2 system call shows that it should not return EPERM [1]. I could not quite pinpoint where this behavior is introduced - somewhere between glibc and seccomp, but it all boils down to the runtime being too old for this new syscall.

简单说就是-x -r 这些都是依赖faccessat,由于 runc 中的错误,如果你的内核不支持 faccessat2,那它会失败。

那路线总共有三条:

  • 特权模式启动,让容器直接使用系统权限。(这个被我pass了,不安全)

  • 升级kernel以支持faccessat2(国产化系统客户不同意升级内核,小版本升级了下不顶用)

  • 换掉runc,我大概查了一下runc >= 1.0.0-rc93,这个问题就可以解决。

然后最狗血的事情来了,我把runc换了(实际上本身我的runc就很高)重启dockerd,发现问题依旧,反复折腾了我好久。最后在同事的提醒下,说你换完以后再执行下runc -v,这我是真的万万没想到,这个鬼垃圾系统自己带了个runc在/usr/local/bin!!

1
2
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin

并且/usr/local/bin还在/usr/bin前面。。。然后就是换掉这玩意,问题就好了。

经验主义害死人,以后一定一定每一步都验证。