当前位置: 首页 > article >正文

记一次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,给出的一个比较靠谱的解释是这样的:
在这里插入图片描述

反思

  1. Q:为啥本地可以启动成功,测试环境不行?
    A:使用的JDK版本不一样。只有这一个服务最特殊,使用JDK 8版本,其他应用都是使用OpenJDK 11版本。
  2. Q:为啥测试环境的报错信息是UnsupportedClassVersionError,也就是使用的JDK版本引入一个更高JDK版本编译的依赖;而本地开发环境的报错是:NoClassDefFoundError?
    A:不知道。
  3. 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]。好像可以自圆其说。
  4. Q:Spring应用和Spring Boot应用的区别是什么?分界线是什么?@SpringBootApplication注解吗?
    A:不知道,此问题并不是没有意义的。
    在这里插入图片描述

参考

  • ChatGPT
  • DeepSeek

http://www.kler.cn/a/470110.html

相关文章:

  • sunrays-framework(太阳射线框架搭建)
  • asammdf python库解析MF4文件(一)cut and filter
  • Tableau数据可视化与仪表盘搭建-数据可视化原理
  • php.ini配置中有10处设置不当,会使网站存在安全问题哦
  • 缓存-Redis-缓存更新策略-主动更新策略-Cache Aside Pattern(全面 易理解)
  • iOS - 自定义引用计数(MRC)
  • 日志记录:追踪你的Java行动轨迹
  • 微软 2024 最新技术全景洞察
  • NO.1 《机器学习期末复习篇》以题(问答题)促习(人学习),满满干huo,大胆学大胆补!
  • sql server cdc重启监控新加表字段
  • asp.net core mvc的 ViewBag , ViewData , Module ,TempData
  • JS数组转字符串(3种方法)
  • 字母异位分组力扣--49
  • 福建省乡镇界面数据arcgis格式shp乡镇名称和编码无偏移坐标内容测评
  • 什么是Spring Boot?深度解析其核心概念与优势
  • 从MySQL迁移到PostgreSQL的完整指南
  • Golang设计模式目录
  • CSS语言的软件开发工具
  • Easysearch Java SDK 2.0.x 使用指南(三)
  • 【微服务】5、服务保护 Sentinel
  • AWS Auto Scaling基础知识
  • 【AI日记】25.01.06
  • Ruby语言的语法
  • SVN简单使用教程
  • linux 查找redis 的配置文件 (`redis.conf`)
  • Kubernetes Gateway API-4-TCPRoute和GRPCRoute