记一次k8s下容器启动失败,容器无日志问题排查
问题
背景
本地开发时,某应用增加logback-spring.xml
配置文件,加入必要的依赖:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>8.0</version>
</dependency>
本地可以正常启动。
但是发布到测试环境后,容器启动失败
其状态CrashLoopBackOff,不管是通过执行kubectl logs <pod> -n <ns>
命令查看日志,还是在KubeSphere查看容器日志,都没有任何日志输出。
排查
KubeSphere看到容器的状态为
容器的事件:
信息如下:
Warning Unhealthy 75s (x10 over 5m15s) kubelet Startup probe failed: Get "http://172.20.0.95:8012/health": dial tcp 172.20.0.95:8012: connect: connection refused
Back-off restarting failed container fileview in pod
使用k9s查看日志:
各种无头苍蝇乱排查:
- 删除容器,自动重启,不行;
- 修改探针时间,不行;
kubectl describe pod <pod_name> -n <ns>
,没看出问题。
总之,看不到任何有效信息,无法定位问题。
workaround
通过KubeSphere,打开【应用负载】-【工作负载】,搜索【fileview】,【编辑YAML】,使用一个有效的之前的镜像版本,即回滚:
版本号,在Harbor镜像仓库里,根据代码提交和流水线构建日期,选择一个变更前的版本。然后删除容器组里的现有容器,会自动重启。
然后重启成功!!
说明还是由于代码提交导致测试环境里的容器启动失败。
Git Commit
代码提交记录如下:
新增的配置文件logback-spring.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位毫秒。当scan为true时,此才能属性生效。默认时间间隔为30s。 -->
<configuration scan="true" scanPeriod="30 seconds">
<!-- 彩色日志依赖 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 线上日志文件路径-->
<property name="JSON_FILE_PATH" value="/logs/service/fileview"/>
<!-- 控制台彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %green([traceId=%X{traceId} spanId=%X{spanId} sampled=%X{sampled}]) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 日志文件日志格式 -->
<property name="FILE_LOG_PATTERN"
value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} [%X{traceId}] ${PID:- } --- [%15t] %-40.40logger{39}:[%5.5line] - %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- json日志格式 -->
<property name="JSON_LOG_PATTERN"
value='{"localtime": "%date{yyyy-MM-dd HH:mm:ss.SSS}","level": "%level","pid": "${PID:-}","thread": "%thread","traceId": "%X{traceId}","class": "%logger","method": "%method","line": "%line","message": "%message","stack":"%wEx"}'/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 以JSON格式写入log文件 -->
<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${JSON_FILE_PATH}/log_%d{yyyyMMdd}_%i.log</FileNamePattern>
<!--日志文件最大大小-->
<maxFileSize>200MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>5</maxHistory>
<!-- 每个日志文件到200MB的时候开始切分,最多保留5天,但最大到5GB,哪怕没到5天也要删除多余的日志 -->
<totalSizeCap>5GB</totalSizeCap>
<!--启动项目触发删除检测 -->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<pattern>
<pattern>${JSON_LOG_PATTERN}</pattern>
</pattern>
</providers>
</encoder>
</appender>
<!--本地调试打印控制台彩色日志-->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
<springProfile name="dev,uat,test,pre,gray,future,prd,prod">
<root level="INFO">
<appender-ref ref="JSON_FILE"/>
</root>
</springProfile>
</configuration>
在本地开发环境Debug模式启动应用时,可以正常启动。
另外,上面这个配置文件,引入一个LoggingEventCompositeJsonEncoder,因此需要引入logstash-logback-encoder
依赖。
但没有注意到:Spring Boot并没有加载此文件logback-spring.xml
,也没有重视这一点。
事实上,针对这次提交涉及的4个文件,到底是哪个改动引发问题,定位时,也耗费掉不少时间。
只是这里直接将问题定位为logback配置文件。
Docker
既然本地启动成功,测试环境启动失败。能不能在本地模拟测试环境?
于是花费一点时间,在Windows开发环境下安装Docker Desktop。
IDEA在某个版本后自带Docker开发插件:
稍微摸索一下,就知道如何使用Docker插件,填写镜像地址,点击Pull接口
镜像拉取成功后,点击右侧的Create Container:
容器启动失败,报错日志:
Logging system failed to initialize using configuration from 'null'
2024-12-29T08:31:31.991172257Z java.lang.IllegalStateException: Logback configuration error detected:
ERROR in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Could not create component [encoder] of type [net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder] java.lang.ClassNotFoundException: net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder
2024-12-29T08:31:31.991194340Z ERROR in ch.qos.logback.core.joran.spi.Interpreter@36:24 - no applicable action for [providers], current ElementPath is [[configuration][appender][encoder][providers]]
2024-12-29T08:31:31.991197106Z ERROR in ch.qos.logback.core.joran.spi.Interpreter@37:26 - no applicable action for [pattern], current ElementPath is [[configuration][appender][encoder][providers][pattern]]
2024-12-29T08:31:31.991199506Z ERROR in ch.qos.logback.core.joran.spi.Interpreter@38:30 - no applicable action for [pattern], current ElementPath is [[configuration][appender][encoder][providers][pattern][pattern]]
2024-12-29T08:31:31.991201798Z ERROR in ch.qos.logback.core.ConsoleAppender[JSON] - No encoder set for the appender named "JSON".
这说明什么??
说明在以容器方式启动应用时,应用加载logback配置文件失败。
此时,才开始重视前文提到的:应用启动时,Spring Boot并没有加载logback-spring.xml
文件。
logback
现在来解决,为啥本地开发环境Debug模式启动应用,Spring Boot为啥没有加载logback-spring.xml
文件。
应用的目录结构如下:
因为application.properties
文件在config
目录下,于是不带思考地,把logback-spring.xml
文件也放在config
目录下。
把logback-spring.xml
文件移到resources
目录下,Spring Boot还是不能加载logback-spring.xml
文件。
把文件重命名为为logback.xml
,终于可以加载。
JDK版本
本地启动,应用不再报错,发布到测试环境,容器启动还是失败。
不过,终于可以看到容器日志!!!具体的报错信息(经过换行处理,方便阅读):
Exception in thread "main" java.lang.UnsupportedClassVersionError:
net/logstash/logback/encoder/LoggingEventCompositeJsonEncoder has been compiled by a more recent version of the Java Runtime (class file version 55.0),
this version of the Java Runtime only recognizes class file versions up to 52.0
很熟悉的报错,是不是:
- 52.0对应于JDK 8版本
- 55.0对应于JDK 11版本
此应用的Dockerfile如下:
FROM harbor.aaaa.com/library/fileview:4.4.0-beta
ADD target/kkFileView-*.tar.gz /opt/
ENV KKFILEVIEW_BIN_FOLDER /opt/kkFileView-4.4.0-beta/bin
ENTRYPOINT ["java", "-Dfile.encoding=UTF-8", "-Dspring.config.location=/opt/kkFileView-4.4.0-beta/config/application.properties", "-jar", "/opt/kkFileView-4.4.0-beta/bin/kkFileView-4.4.0-beta.jar"]
基础镜像是fileview:4.4.0-beta
,看不出Java版本,默认应该就是JDK 8。
登录到Harbor镜像仓库,查看镜像信息,果然是JDK8:
{
"ArgsEscaped": true,
"Cmd": [
"/bin/bash"
],
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/jdk1.8.0_251/bin",
"JAVA_HOME=/usr/local/jdk1.8.0_251",
"CLASSPATH=/usr/local/jdk1.8.0_251/lib/dt.jar:/usr/local/jdk1.8.0_251/lib/tools.jar",
"LANG=zh_CN.UTF-8",
"LC_ALL=zh_CN.UTF-8"
],
"Labels": {
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.version": "20.04"
}
}
其构建历史(无关信息)为:
ARG RELEASE
ARG LAUNCHPAD_BUILD_ARCH
LABEL org.opencontainers.image.ref.name=ubuntu
LABEL org.opencontainers.image.version=20.04
ADD file:e7cff353f027ecf0a2cb1cdd51714de3b083a11a0d965f104489f9a7e6925056 in /
CMD ["/bin/bash"]
COPY fonts/* /usr/share/fonts/chinese/ # buildkit
COPY server-jre-8u251-linux-x64.tar.gz /tmp/server-jre-8u251-linux-x64.tar.gz # buildkit
COPY LibreOffice_7.5.3.2_Linux_x86-64_deb.tar.gz /tmp/libreoffice_deb.tar.gz # buildkit
RUN RUN apt-get clean && apt-get update && sed -i 's/http:\/\/archive.ubuntu.com/https:\/\/mirrors.aliyun.com/g' /etc/apt/sources.list && sed -i 's/# deb/deb/g' /etc/apt/sources.list && apt-get install -y --reinstall ca-certificates && apt-get clean && apt-get update && apt-get install -y locales language-pack-zh-hans && localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 && locale-gen zh_CN.UTF-8 && export DEBIAN_FRONTEND=noninteractive && apt-get install -y tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && apt-get install -y fontconfig ttf-mscorefonts-installer ttf-wqy-microhei ttf-wqy-zenhei xfonts-wqy && apt-get install -y wget && cd /tmp && tar -zxf /tmp/server-jre-8u251-linux-x64.tar.gz && mv /tmp/jdk1.8.0_251 /usr/local/ && apt-get install -y libxrender1 libxinerama1 libxt6 libxext-dev libfreetype6-dev libcairo2 libcups2 libx11-xcb1 libnss3 && tar -zxf /tmp/libreoffice_deb.tar.gz && cd /tmp/LibreOffice_7.5.3.2_Linux_x86-64_deb/DEBS && dpkg -i *.deb && rm -rf /tmp/* && rm -rf /var/lib/apt/lists/* && cd /usr/share/fonts/chinese && mkfontscale && mkfontdir && fc-cache -fv # buildkit
ENV JAVA_HOME=/usr/local/jdk1.8.0_251
ENV CLASSPATH=/usr/local/jdk1.8.0_251/lib/dt.jar:/usr/local/jdk1.8.0_251/lib/tools.jar
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/jdk1.8.0_251/bin
ENV LANG=zh_CN.UTF-8
ENV LC_ALL=zh_CN.UTF-8
CMD ["/bin/bash"]
本地开发环境切换到JDK 8,debug模式启动应用,果然启动报错:
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class org.slf4j.impl.StaticLoggerBinder
但是,为啥和测试环境的报错信息不一样呢??
org.slf4j.impl.StaticLoggerBinder
这个类是在logback-classic-1.2.3.jar
里引入的:
不管了,开发任务太重了。没有时间去仔细探究。
降低logstash-logback-encoder
版本8.0到6.6:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
不再报错:Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class org.slf4j.impl.StaticLoggerBinder
。
提交代码,触发流水线构建,发布到测试环境,启动成功。
logback.xml还是logback-spring.xml
问题虽然解决。
但是还留有不少疑问,此应用分明是一个Spring-Boot应用啊,为啥不能加载logback-spring.xml
:
可以加载logback.xml
另外,虽然可以加载文件,但是应用启动时会打印出一些ERROR日志:
报错日志如下:
13:09:22,777 |-ERROR in ch.qos.logback.core.joran.spi.Interpreter@72:65 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]
13:09:22,777 |-ERROR in ch.qos.logback.core.joran.spi.Interpreter@73:28 - no applicable action for [root], current ElementPath is [[configuration][springProfile][root]]
13:09:22,777 |-ERROR in ch.qos.logback.core.joran.spi.Interpreter@74:39 - no applicable action for [appender-ref], current ElementPath is [[configuration][springProfile][root][appender-ref]]
13:09:22,777 |-ERROR in ch.qos.logback.core.joran.spi.Interpreter@75:44 - no applicable action for [appender-ref], current ElementPath is [[configuration][springProfile][root][appender-ref]]
请教DeepSeek,给出的一个比较靠谱的解释是这样的:
反思
- Q:为啥本地可以启动成功,测试环境不行?
A:使用的JDK版本不一样。只有这一个服务最特殊,使用JDK 8版本,其他应用都是使用OpenJDK 11版本。 - Q:为啥测试环境的报错信息是UnsupportedClassVersionError,也就是使用的JDK版本引入一个更高JDK版本编译的依赖;而本地开发环境的报错是:NoClassDefFoundError?
A:不知道。 - Q:为啥不能加载
logback-spring.xml
文件?可以加载logback.xml
文件,但是有报错信息:no applicable action for [springProfile]
?
A:唯一(有可能的)合理的解释,这依旧是一个Spring应用,虽然放在根目录下面的启动类,有@SpringBootApplication注解。
Spring应用默认加载logback.xml
文件,Spring Boot应用默认加载logback-spring.xml
文件?让Spring去加载logback-spring.xml
文件(经过重命名处理后可以加载logback.xml
,但是文件里依旧有一些springProfile
配置项),于是会报错no applicable action for [springProfile]
。好像可以自圆其说。 - Q:Spring应用和Spring Boot应用的区别是什么?分界线是什么?@SpringBootApplication注解吗?
A:不知道,此问题并不是没有意义的。
参考
- ChatGPT
- DeepSeek