You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Alpine Docker配置LD_PRELOAD防JVM崩溃但执行外部命令报错问题

Alpine 3.15.4容器运行Quarkus调用Firestore的配置冲突解决方案

问题现象

基于Alpine 3.15.4基础镜像构建Docker容器运行Quarkus Java服务时,调用com.google.firebase.cloud.FirestoreClient.getFirestore()方法存在配置冲突,两类常规Dockerfile配置均无法同时满足Firestore初始化、外部命令调用两个场景:

  • 配置1:容器内通过apk安装OpenJDK11、curl、ca-certificates依赖
    RUN apk update
    RUN apk --no-cache add curl ca-certificates openjdk11-jre-headless
    
    运行表现:Firestore初始化流程正常,但通过ProcessBuilder调用ls等系统命令时抛出No such file or directory异常
  • 配置2:注释上述apk安装OpenJDK的步骤
    #RUN apk update
    #RUN apk --no-cache add curl ca-certificates openjdk11-jre-headless
    
    运行表现:Firestore初始化触发JVM SIGSEGV崩溃,崩溃点位于JVM外部原生代码层,但ls等系统命令可正常执行

错误日志

JVM崩溃日志

[de.ibe.DemoResource] (executor-thread-0) getFirestore
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x0000000000003efe, pid=1, tid=142
#
# JRE version: OpenJDK Runtime Environment (11.0.15+10) (build 11.0.15+10-alpine-r0)
# Java VM: OpenJDK 64-Bit Server VM (11.0.15+10-alpine-r0, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)
# Problematic frame:
# C  0x0000000000003efe
#
# No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /deployments/hs_err_pid1.log
#
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.

外部命令调用失败日志

java.io.IOException: Cannot run program "ls": error=2, No such file or directory
        at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1128)
        at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1071)
        at de.ibe.DemoResource.shell(DemoResource.java:72)
        at de.ibe.DemoResource.demo(DemoResource.java:37)

根因分析

  • Firestore底层依赖gRPC Java的原生库,该库基于glibc编译,而Alpine默认使用musl libc,直接运行会触发原生层内存访问错误导致JVM崩溃,需要安装gcompat兼容层做二进制兼容
  • 常规方案中全局配置LD_PRELOAD=/lib/libgcompat.so.0会让该环境变量被所有ProcessBuilder fork出的子进程继承,gcompat的初始化逻辑会干扰musl libc的可执行文件路径搜索流程,导致系统命令无法被找到

单镜像解决方案

不需要构建多个适配镜像,通过调整gcompat加载范围即可同时解决两个问题:

  1. 安装gcompat依赖,但不要配置全局LD_PRELOAD环境变量
  2. 仅在JVM主进程启动时临时加载gcompat库,保证该配置不会被子进程继承
  3. 调整JVM进程启动机制,避免fork过程中兼容层引发的异常

修正后的Dockerfile配置

FROM alpine:3.15.4

ARG RUN_JAVA_VERSION=1.3.8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'

# 合并安装步骤,同时安装openjdk和gcompat兼容层,减少镜像层数
RUN apk update && \
    apk --no-cache add curl ca-certificates openjdk11-jre-headless gcompat && \
    mkdir /deployments && \
    chmod "g+rwX" /deployments && \
    curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh && \
    chmod 540 /deployments/run-java.sh && \
    # 修改启动脚本,仅在执行java命令时临时加载gcompat,不设置全局环境变量
    sed -i 's|exec.*java|LD_PRELOAD=/lib/libgcompat.so.0 exec java|g' /deployments/run-java.sh

# JVM参数增加POSIX_SPAWN进程启动机制,规避vfork模式下的兼容问题
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Djdk.lang.Process.launchMechanism=POSIX_SPAWN"

# 应用分层拷贝逻辑保持不变
COPY target/quarkus-app/lib/ /deployments/lib/
COPY target/quarkus-app/*.jar /deployments/
COPY target/quarkus-app/app/ /deployments/app/
COPY target/quarkus-app/quarkus/ /deployments/quarkus/

EXPOSE 8080
ENTRYPOINT [ "/deployments/run-java.sh" ]

验证说明

该配置下:

  • JVM主进程启动时会加载gcompat兼容层,gRPC原生库可正常运行,Firestore初始化不会触发SIGSEGV崩溃
  • ProcessBuilder启动外部命令时,子进程不会继承LD_PRELOAD配置,系统命令路径搜索逻辑正常,ls等命令可正常执行

附复现Demo代码

package de.ibe;

import java.io.IOException;
import java.io.InputStream;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.jaxrs.PathParam;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.cloud.FirestoreClient;

@Path("/demo")
public class DemoResource {

    private static final Logger LOG = Logger.getLogger(DemoResource.class);

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/{command}")
    public String demo(@PathParam String command) {

        try {
            if (command == null || command.equals("")) {
                return "command null or empty";
            } else if (command.equals("init")) {
                LOG.info("run init");
                return this.init();
            } else if (command.equals("shell")) {
                LOG.info("run shell");
                return this.shell();
            } else {
                return "unkown command";
            }
        } catch (Exception e) {
            LOG.error(e);
            return e.getMessage();
        }
    }

    private String init() throws IOException {
        try (InputStream in = this.getClass().getResourceAsStream("/demo-b9aa8a6.json")) {
            if (in == null) {
                return "init in is null";
            }

            GoogleCredentials credentials = GoogleCredentials.fromStream(in);

            LOG.info("options");
            FirebaseOptions options = new FirebaseOptions.Builder().setCredentials(credentials).build();

            LOG.info("initializeApp");
            FirebaseApp.initializeApp(options);

            LOG.info("getFirestore");
            FirestoreClient.getFirestore();

        }

        return "init ok";
    }

    private String shell() {
        try {
            final ProcessBuilder builder = new ProcessBuilder("ls");
            builder.start();
        } catch (IOException e) {
            e.printStackTrace();
            return "shell error";
        }

        return "shell ok";
    }

}

内容的提问来源于stack exchange,提问作者Michael S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:15:44