晚上8.30即将下班,正准备开始摸会鱼,驻场的同事电话就打过来了,得嘞,这鱼是摸不成了。接起电话,同事反馈现场生产环境某服务起不来了,但是同一个镜像测试环境正常运行,而生产环境已经重启3、4次了,遂开启视频远程支持。
现象
观察pod状态一直处于 running 0/1状态,并且在重启次数可以看到已经被重启3次了。(客户现场没法截图,这里p了一张)
describe下pod,可以看到connect 127.0.0.1:8080 connect refused。显然是服务没起来导致健康检查未通过。
直接查看pod的日志可以看到日志一直卡在了 Root WebApplicationContext: initialization completed in xxxx ms 一行,这里使用本地的日志用作表示。
排查
按照日常的经验,一般在输出完这行日志后是创建数据库连接池的流程,遂怀疑是数据库连接不上,可能连接耗尽了,或者可用连接数小于druid连接池大小,这点也较符合上面的现象,可能测试环境数据库连接占用少,而生产环境占用多导致服务连接不上。但是又觉得奇怪,就算是连接不上,日志中应该也会有类似于connection refused的报错才对。
遂让现场的同事查看数据库连接状况
# 查看当前连接数
show status like 'threads_connected';
# 查看最大连接数
show VARIABLES like 'max_connections';
不出意外地,当前连接数远小于最大连接数。(这里截图也以本地作为示例)
既然不是数据库的问题,也不是代码的问题,毕竟同一个镜像测试环境服务正常运行。看起来似乎毫无头绪了,突然,死去的记忆复苏了,springboot可以配置日志等级到debug(原谅我太久没看debug日志了),于是让同事修改配置重启服务。
logging:
root:
level: debug
重启后日志中果然出现了端倪,关键异常在org.apache.tomcat.jni.LibraryNotFoundError。 Can't load library: /bin/liblibtcnative-2.so, Can't load library:... 。嗯?加载不到动态链接库?遂让同学查看了pod所在节点,发现节点是今天新加入的,遂咨询同事这台机器的操作系统,得到答复麒麟,终于找到问题的根因了。
main] o.a.catalina.core.AprLifecycleListener : The Apache Tomcat Native library could not be found using names [tcnative-2, libtcnative-2, tcnative-1, libtcnative-1] on the java.library.path [/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib]. The errors reported were [Can't load library: /bin/libtcnative-2.so, Can't load library: /bin/liblibtcnative-2.so, Can't load library: /bin/libtcnative-1.so, Can't load library: /bin/liblibtcnative-1.so, no tcnative-2 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib], no libtcnative-2 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib], no tcnative-1 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib], no libtcnative-1 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib]]
org.apache.tomcat.jni.LibraryNotFoundError: Can't load library: /bin/libtcnative-2.so, Can't load library: /bin/liblibtcnative-2.so, Can't load library: /bin/libtcnative-1.so, Can't load library: /bin/liblibtcnative-1.so, no tcnative-2 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib], no libtcnative-2 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib], no tcnative-1 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib], no libtcnative-1 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib]
at org.apache.tomcat.jni.Library.<init>(Library.java:93)
at org.apache.tomcat.jni.Library.initialize(Library.java:234)
at org.apache.catalina.core.AprLifecycleListener.init(AprLifecycleListener.java:201)
at org.apache.catalina.core.AprLifecycleListener.isAprAvailable(AprLifecycleListener.java:112)
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getDefaultLifecycleListeners(TomcatServletWebServerFactory.java:174)
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.<init>(TomcatServletWebServerFactory.java:121)
at org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomc
稍微百度下也能够找到类似的报错
最终,为了防止服务被调度到新加入的节点上,让同事给新加的麒麟系统的节点加上污点,让服务调度到老的节点上,服务正常运行。
回顾事件起始,让我感觉比较坑爹的是这么严重的错误,tomcat居然没有打印error级别的日志。还有就是在发现客观问题的根因前,不要妄加猜测,先通过现有的一切手段找到问题根因,实在找不到再去大胆猜。
添加污点
在 Kubernetes 中,给节点加上污点(taint)可以防止特定的 Pod 被调度到该节点。你可以使用 kubectl taint nodes 命令为节点添加污点。以下是步骤:
kubectl taint nodes <node-name> key=value:taint-effect
<node-name>:你要加污点的节点名称。key=value:污点的键和值。taint-effect:污点的影响,可能的值有:
-
NoSchedule:新的 Pod 不会被调度到该节点。PreferNoSchedule:尽量不调度 Pod 到该节点,但不强制。NoExecute:会驱逐已经存在的 Pod,并阻止新的 Pod 调度。