Quarkus 2升级3后本地正常 但Kubernetes容器抛IllegalAccessError
将Quarkus从2版本升级到3.1.0.Final后,应用本地可正常构建运行,但部署到Kubernetes Pod时抛出RuntimeException,具体为IllegalAccessError。Quarkus 2版本时部署到Kubernetes可正常运行。
错误栈
Exception in thread "main" java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at io.quarkus.bootstrap.runner.QuarkusEntryPoint.doRun(QuarkusEntryPoint.java:61) at io.quarkus.bootstrap.runner.QuarkusEntryPoint.main(QuarkusEntryPoint.java:32) Caused by: java.lang.ExceptionInInitializerError at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) at io.quarkus.runtime.Quarkus.run(Quarkus.java:70) at io.quarkus.runtime.Quarkus.run(Quarkus.java:44) at io.quarkus.runtime.Quarkus.run(Quarkus.java:124) at io.quarkus.runner.GeneratedMain.main(Unknown Source) ... 6 more Caused by: java.lang.RuntimeException: Failed to start quarkus at io.quarkus.runner.ApplicationImpl.<clinit>(Unknown Source) ... 15 more Caused by: java.lang.IllegalAccessError: class jakarta.validation.Validation$ProviderSpecificBootstrapImpl tried to access private method 'void jakarta.validation.Validation$GenericBootstrapImpl.<init>()' (jakarta.validation.Validation$ProviderSpecificBootstrapImpl and jakarta.validation.Validation$GenericBootstrapImpl are in unnamed module of loader io.quarkus.bootstrap.runner.RunnerClassLoader @73c6c3b2), (Type jakarta.validation.Validation$ProviderSpecificBootstrapImpl (loader: io.quarkus.bootstrap.runner.RunnerClassLoader @73c6c3b2) is not a nest member of type jakarta.validation.Validation (loader: io.quarkus.bootstrap.runner.RunnerClassLoader @73c6c3b2): current type is not listed as a nest member, Type jakarta.validation.Validation$GenericBootstrapImpl (loader: io.quarkus.bootstrap.runner.RunnerClassLoader @73c6c3b2) is not a nest member of type jakarta.validation.Validation (loader: io.quarkus.bootstrap.runner.RunnerClassLoader @73c6c3b2): current type is not listed as a nest member) at jakarta.validation.Validation$ProviderSpecificBootstrapImpl.configure(Validation.java:209) at io.quarkus.hibernate.validator.runtime.HibernateValidatorRecorder$2.created(HibernateValidatorRecorder.java:64) at io.quarkus.arc.runtime.ArcRecorder.initBeanContainer(ArcRecorder.java:79) at io.quarkus.deployment.steps.ArcProcessor$generateResources844392269.deploy_0(Unknown Source) at io.quarkus.deployment.steps.ArcProcessor$generateResources844392269.deploy(Unknown Source) ... 16 more
pom.xml相关配置
<properties> <jakarta.jakartaee-api.version>10.0.0</jakarta.jakartaee-api.version> <jakarta.resource-api.version>2.1.0</jakarta.resource-api.version> <jakarta.ws.rs-api.version>3.1.0</jakarta.ws.rs-api.version> <maven.compiler.release>17</maven.compiler.release> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.version>3.1.0.Final</quarkus.platform.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>${quarkus.platform.artifact-id}</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>jakarta.ws.rs</groupId> <artifactId>all</artifactId> <version>3.1.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>jakarta.resource</groupId> <artifactId>jakarta.resource-api</artifactId> <version>${jakarta.resource-api.version}</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-validator</artifactId> </dependency> <dependency> <groupId>jakarta.platform</groupId> <artifactId>jakarta.jakartaee-api</artifactId> <version>${jakarta.jakartaee-api.version}</version> </dependency> <dependency> <groupId>jakarta.ws.rs</groupId> <artifactId>jakarta.ws.rs-api</artifactId> <version>${jakarta.ws.rs-api.version}</version> </dependency> </dependencies>
环境信息
- Dockerfile.jvm使用镜像:
registry.access.redhat.com/ubi8/openjdk-17:1.20 - 构建方式:通过GitLab CI的
buildah/buildah镜像执行buildah bud --format docker -f src/main/docker/Dockerfile.jvm构建镜像
已尝试的排查步骤
- 验证依赖版本匹配(IntelliJ未检测到冲突,jakarta.validation-api与quarkus-hibernate-validator版本一致)
- 执行清理构建与重新部署
解决建议
1. 移除手动指定的Jakarta API依赖
Quarkus BOM(quarkus-bom)已经统一管理所有Jakarta API的版本,手动引入jakarta.jakartaee-api和jakarta.ws.rs-api并指定版本可能导致依赖冲突。修改pom.xml的<dependencies>块,删除这两个依赖:
<dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-validator</artifactId> </dependency> <!-- 移除下面两个手动指定的依赖 --> <!-- <dependency> <groupId>jakarta.platform</groupId> <artifactId>jakarta.jakartaee-api</artifactId> <version>${jakarta.jakartaee-api.version}</version> </dependency> <dependency> <groupId>jakarta.ws.rs</groupId> <artifactId>jakarta.ws.rs-api</artifactId> <version>${jakarta.ws.rs-api.version}</version> </dependency> --> </dependencies>
同时,移除<dependencyManagement>中的jakarta.ws.rs:all依赖导入,因为Quarkus BOM已经包含相关配置。
2. 检查依赖树确认无冲突
执行以下命令查看完整依赖树,确认jakarta.validation-api只有一个来源且版本与Quarkus 3.1.0.Final匹配:
mvn dependency:tree -Dincludes=jakarta.validation:jakarta.validation-api
如果发现多个版本,通过<dependencyManagement>强制指定Quarkus BOM中的版本。
3. 确保容器构建使用Quarkus标准打包方式
Quarkus应用应该使用quarkus build命令构建,而不是普通的mvn package。修改GitLab CI的构建命令,确保执行:
./mvnw quarkus:build -DskipTests
同时检查Dockerfile是否正确复制了Quarkus构建的产物,参考标准的Quarkus JVM模式Dockerfile:
FROM registry.access.redhat.com/ubi8/openjdk-17:1.20 as builder WORKDIR /workspace COPY mvnw . COPY .mvn .mvn COPY pom.xml . RUN ./mvnw dependency:go-offline -DskipTests COPY src src RUN ./mvnw quarkus:build -DskipTests FROM registry.access.redhat.com/ubi8/openjdk-17:1.20 WORKDIR /app COPY --from=builder /workspace/target/quarkus-app/lib/ /app/lib/ COPY --from=builder /workspace/target/quarkus-app/*.jar /app/ COPY --from=builder /workspace/target/quarkus-app/app/ /app/app/ COPY --from=builder /workspace/target/quarkus-app/quarkus/ /app/quarkus/ ENTRYPOINT ["java", "-jar", "/app/quarkus-run.jar"]
4. 验证JDK版本一致性
检查本地开发环境和容器中的JDK小版本是否一致,执行以下命令查看容器中的JDK版本:
docker run --rm registry.access.redhat.com/ubi8/openjdk-17:1.20 java -version
如果本地JDK版本与容器差异较大,可能导致字节码兼容性问题,尽量保持两者版本一致。
5. 临时绕过Nest成员访问检查(用于验证问题)
在Kubernetes的Pod启动命令中添加JVM参数-XX:-UseNestBasedAccessControl,禁用基于Nest的访问控制,看是否能启动应用:
# 在Deployment的container配置中添加 args: ["-XX:-UseNestBasedAccessControl", "-jar", "/app/quarkus-run.jar"]
如果应用能正常启动,说明问题确实是JDK的Nest成员访问控制导致的,此时需要确保依赖的字节码是用兼容的JDK版本编译的。
内容的提问来源于stack exchange,提问作者OhJohnny

