igozhang

——

    SAP JCo 报错:UnsatisfiedLinkError

    文档编号: igo-20260701-001
    服务: igo-mes-auth-center
    命名空间: ns-igo-prod-a
    集群: 基地-A K8s
    报告日期: 2026-07-01


    ENV

    类别 项目 版本/信息
    操作系统 K8s 节点 Ubuntu 20.04.6 LTS,Kernel 5.4.0-216-generic
    容器运行时 基地-A 集群 docker://25.0.5
    Kubernetes 基地-A 集群 v1.24.6(Client/Server)
    对照集群 基地-B K8s v1.26.7,docker://23.0.6,Ubuntu 20.04.6 LTS
    应用运行时 Java OpenJDK 8u342(容器内 JAVA_HOME=/usr/local/openjdk-8
    故障镜像 基地-A harbor-a.igozhang.cn/igo-project-a/igo-mes-auth-center:20260423_085858-eb02109
    修复镜像 基地-A harbor-a.igozhang.cn/igo-project-a/igo-mes-auth-center:20260630_182454-eb02109d
    对照镜像 基地-B(正常) harbor-b.igozhang.cn/igo-project-b/igo-mes-auth-center:20260620_171442-4e53f021
    硬件/节点 基地-A 控制面 k8s-master-01/02/03(4C/16G)
    基地-A 工作节点 k8s-worker-01~06(16C/64G)
    故障 Pod 节点 k8s-worker-02
    SAP JCo 原生库 标准文件 /usr/lib/libsapjco3.so(约 5,379,810 字节)

    本问题与 GPU/网卡驱动、固件无关,属应用镜像构建层缺失原生库。


    1. 现象

    XXL-Job 触发 SAP 部门同步时,igo-mes-auth-center 报错,Readiness 探针偶发超时,Pod 多次重启。

    关键报错:

    java.lang.UnsatisfiedLinkError: no sapjco3 in java.library.path
        at com.sap.conn.jco.rt.DefaultJCoRuntime.loadJCoLibrary(...)
        at com.igo.mes.auth.center.starter.sap.conn.SapConnection.connect(...)
        at com.igo.mes.auth.center.starter.sap.sync.SyncDepartmentHandler.sync(...)
    

    当时 Pod 状态:

    Pod 镜像 tag 重启次数 状态
    igo-mes-auth-center-xxxxx-yyyyy 20260423_085858-eb02109 14 Running(不健康)

    K8s 事件(节选):

    Warning  Unhealthy  Readiness probe failed: Get "http://10.0.0.12:8080/management/health": context deadline exceeded
    

    2. 结论与根因

    2.1 结论

    项目 结论
    故障状态 已修复(2026-06-30 发版后验证通过)
    Deploy 变更 仅更新镜像 tag(revision 22 → 23),无其他 K8s 配置变更
    修复方式 开发发版新镜像,非 initContainer / java.library.path 临时方案
    当前状态 /usr/lib/libsapjco3.so 存在,依赖正常,日志无 UnsatisfiedLinkError

    2.2 根因

    20260423 镜像构建回退: libsapjco3.so 仅打包在 Spring Boot fat jar(app.jar)内,未复制到文件系统 /usr/lib/

    JCo 通过 System.loadLibrary("sapjco3") 加载原生库,Linux 在 /usr/lib 等系统路径查找,无法从 jar 内部读取,因此报 UnsatisfiedLinkError: no sapjco3 in java.library.path

    镜像版本对比:

    镜像 tag /usr/lib/libsapjco3.so
    20260416_132446-eb02109d 有(正常)
    20260423_085858-eb02109 无(故障)
    20260630_182454-eb02109d 有(已修复)

    2.3 问题形成原因推测

    开发/CI 构建负责人20260423 发版时,基于代码提交 eb02109 重新构建镜像,Dockerfile 或 CI 脚本中复制 libsapjco3.so/usr/lib/ 的步骤被遗漏或覆盖20260416 版本该步骤正常,基地-B 正常镜像亦有此文件)。运维按流程仅更新了 Deployment 镜像 tag,未变更 K8s 其他配置。结果:基地-A 集群运行的 20260423 镜像文件系统缺少原生库,SAP 同步任务触发 JCo 初始化时报错,Pod 反复重启、Readiness 探针超时。


    3. 定位推导过程

    步骤 1:确认 K8s 配置是否特殊

    执行位置: k8s-master-01

    kubectl get pod igo-mes-auth-center-xxxxx-yyyyy -n ns-igo-prod-a -o jsonpath='节点={.spec.nodeName}{"\n"}镜像={.spec.containers[0].image}{"\n"}' && echo '---环境变量---' && kubectl get pod igo-mes-auth-center-xxxxx-yyyyy -n ns-igo-prod-a -o jsonpath='{range .spec.containers[0].env[*]}{.name}{"="}{.value}{"\n"}{end}' | grep -iE 'JAVA|LIBRARY|SAP|LD_|PATH' && echo '---挂载点---' && kubectl get pod igo-mes-auth-center-xxxxx-yyyyy -n ns-igo-prod-a -o jsonpath='{range .spec.containers[0].volumeMounts[*]}{.name}{" -> "}{.mountPath}{"\n"}{end}'
    

    关键输出:

    节点=k8s-worker-02
    镜像=harbor-a.igozhang.cn/igo-project-a/igo-mes-auth-center:20260423_085858-eb02109
    JAVA_OPTS=-Dspring.profiles.active=igo-site-a-prod -Dserver.port=8080  -Xms2G -Xmx2G -XX:+UseG1GC  -Xss256k
    挂载: vol1-properties -> /flydiy/bootstrap.properties
    

    推导: 无 initContainer、无 java.library.path、无 JCo Volume → 排除 K8s 层面临时配置问题,转向镜像/容器内排查。


    步骤 2:对比基地-B 正常环境

    执行位置: k8s-master-ref(基地-B 集群)

    基地-B Pod 配置与基地-A 一致,均无 JCo 相关 K8s 配置,差异在镜像版本

    kubectl exec -n ns-igo-prod-b igo-mes-auth-center-aaaaa-bbbbb -- sh -c 'find /flydiy /app /usr/lib /opt -maxdepth 5 -name "*sapjco*" 2>/dev/null'
    

    关键输出:

    /usr/lib/libsapjco3.so
    

    推导: 正常环境文件系统有原生库,进一步确认基地-A 应检查 /usr/lib/


    步骤 3:基地-A 故障 Pod 容器内检查

    执行位置: k8s-master-01

    kubectl exec -n ns-igo-prod-a igo-mes-auth-center-xxxxx-yyyyy -- sh -c 'find /flydiy /app /usr/lib /opt -maxdepth 5 -name "*sapjco*" 2>/dev/null; ls -la /usr/lib/libsapjco3.so 2>&1'
    

    关键输出:

    ls: /usr/lib/libsapjco3.so: No such file or directory
    

    补充:确认 jar 内有库

    kubectl exec -n ns-igo-prod-a igo-mes-auth-center-xxxxx-yyyyy -- sh -c 'jar tf /flydiy/app.jar | grep -i sapjco | head -10'
    

    关键输出:

    BOOT-INF/lib/sapjco3.jar
    BOOT-INF/classes/lib/libsapjco3.so
    

    推导: 库在 jar 内、文件系统缺失 → 定位到镜像构建问题


    步骤 4:确认 Deploy 修复项

    执行位置: k8s-master-01

    echo '=== revision 22 (故障版) ===' && kubectl rollout history deployment/igo-mes-auth-center -n ns-igo-prod-a --revision=22 | grep -iE 'Image:|JAVA_OPTS|initContainer|Replicas|volume|mountPath' && echo '=== revision 23 (修复版) ===' && kubectl rollout history deployment/igo-mes-auth-center -n ns-igo-prod-a --revision=23 | grep -iE 'Image:|JAVA_OPTS|initContainer|Replicas|volume|mountPath'
    

    关键输出:

    revision 22: Image: .../igo-mes-auth-center:20260423_085858-eb02109
    revision 23: Image: .../igo-mes-auth-center:20260630_182454-eb02109d
    JAVA_OPTS / Volume / initContainer: 无变化
    

    推导: Deploy 仅换镜像 tag,修复在镜像构建层;发版后 initContainer 方案未保留。


    步骤 5:修复后验证

    执行位置: k8s-master-01

    kubectl exec -n ns-igo-prod-a igo-mes-auth-center-zzzzz-wwwww -- sh -c 'ls -la /usr/lib/libsapjco3.so 2>&1'
    

    关键输出:

    -rw-rw-rw- 1 root root 5379810 Jun 30 12:25 /usr/lib/libsapjco3.so
    
    kubectl logs igo-mes-auth-center-zzzzz-wwwww -n ns-igo-prod-a 2>&1 | grep -iE 'UnsatisfiedLinkError|no sapjco3' | tail -10
    

    关键输出: 无匹配(修复后无 JCo 加载错误)


    4. 方案

    4.1 已实施修复(开发发版)

    Deployment revision 22 → 23,仅更新镜像

    # 变更前
    image: harbor-a.igozhang.cn/igo-project-a/igo-mes-auth-center:20260423_085858-eb02109
    
    # 变更后
    image: harbor-a.igozhang.cn/igo-project-a/igo-mes-auth-center:20260630_182454-eb02109d
    

    发版命令(运维侧,参考):

    kubectl set image deployment/igo-mes-auth-center igo-mes-auth-center=harbor-a.igozhang.cn/igo-project-a/igo-mes-auth-center:20260630_182454-eb02109d -n ns-igo-prod-a
    kubectl rollout status deployment/igo-mes-auth-center -n ns-igo-prod-a
    

    4.2 镜像构建修复(开发侧,永久方案)

    Dockerfile 固化:

    RUN mkdir -p /usr/lib && \
        cd /tmp && \
        jar xf /flydiy/app.jar BOOT-INF/classes/lib/libsapjco3.so && \
        cp BOOT-INF/classes/lib/libsapjco3.so /usr/lib/libsapjco3.so && \
        rm -rf /tmp/BOOT-INF
    

    CI 构建后校验:

    test -f /usr/lib/libsapjco3.so || (echo "FAIL: libsapjco3.so missing" && exit 1)
    

    4.3 临时应急方案(未采用,备查)

    initContainer 解压 + java.library.path会变更 Deployment,仅应急):

    kubectl patch deployment igo-mes-auth-center -n ns-igo-prod-a --type=strategic --patch '
    spec:
      template:
        spec:
          initContainers:
          - name: extract-sapjco3
            image: <当前镜像>
            command: ["sh","-c","cd /tmp && jar xf /flydiy/app.jar BOOT-INF/classes/lib/libsapjco3.so && cp BOOT-INF/classes/lib/libsapjco3.so /jco/libsapjco3.so"]
            volumeMounts:
            - name: sapjco-lib
              mountPath: /jco
          containers:
          - name: igo-mes-auth-center
            env:
            - name: JAVA_OPTS
              value: "<原JAVA_OPTS> -Djava.library.path=/jco"
            volumeMounts:
            - name: sapjco-lib
              mountPath: /jco
          volumes:
          - name: sapjco-lib
            emptyDir: {}
    '
    

    4.4 验证命令

    A. kubectl 服务器完整检查(推荐)

    执行位置: k8s-master-01

    echo "========== igo-mes-auth-center JCo 诊断 ==========" && \
    echo "" && \
    echo "[1/4] Pod 基本信息" && \
    kubectl get pod -n ns-igo-prod-a -l app=igo-mes-auth-center -o custom-columns='POD:.metadata.name,NODE:.spec.nodeName,STATUS:.status.phase,RESTARTS:.status.containerStatuses[0].restartCount,IMAGE:.spec.containers[0].image' && \
    echo "" && \
    echo "[2/4] Deployment 镜像" && \
    kubectl get deployment igo-mes-auth-center -n ns-igo-prod-a -o jsonpath='镜像={.spec.template.spec.containers[0].image}{"\n"}' && \
    echo "" && \
    POD=$(kubectl get pod -n ns-igo-prod-a -l app=igo-mes-auth-center -o jsonpath='{.items[0].metadata.name}') && \
    echo "[3/4] 容器内 JCo 原生库检查 (Pod: ${POD})" && \
    kubectl exec -n ns-igo-prod-a ${POD} -- sh -c 'if [ -f /usr/lib/libsapjco3.so ]; then ls -la /usr/lib/libsapjco3.so; ldd /usr/lib/libsapjco3.so 2>&1 | grep "not found" && echo "容器内结论: FAIL — 依赖库缺失" || echo "容器内结论: OK — 原生库存在且依赖正常"; else echo "容器内结论: FAIL — /usr/lib/libsapjco3.so 不存在"; fi' && \
    echo "" && \
    echo "[4/4] 最近日志 JCo 错误检查 (tail 60)" && \
    kubectl logs ${POD} -n ns-igo-prod-a --tail=60 2>&1 | grep -iE 'UnsatisfiedLinkError|no sapjco3|SapConnection|SyncDepartment' || echo "日志结论: 未发现 JCo 加载错误" && \
    echo "" && \
    echo "========== 总 结 ==========" && \
    kubectl exec -n ns-igo-prod-a ${POD} -- sh -c 'test -f /usr/lib/libsapjco3.so && ldd /usr/lib/libsapjco3.so 2>&1 | grep -q "not found" && echo "最终结论: FAIL — JCo 原生库异常,需修复镜像" || (test -f /usr/lib/libsapjco3.so && echo "最终结论: OK — JCo 原生库正常,SAP 同步不应因缺库报错") || echo "最终结论: FAIL — 缺少 /usr/lib/libsapjco3.so,需更新镜像"'
    

    正常输出:

    容器内结论: OK — 原生库存在且依赖正常
    最终结论: OK — JCo 原生库正常,SAP 同步不应因缺库报错
    

    B. 容器内检查

    进入容器(kubectl 服务器):

    POD=$(kubectl get pod -n ns-igo-prod-a -l app=igo-mes-auth-center -o jsonpath='{.items[0].metadata.name}') && kubectl exec -it -n ns-igo-prod-a ${POD} -- sh
    

    容器内执行:

    echo "========== 容器内 JCo 检查 ==========" && \
    echo "[1/3] 原生库文件" && ls -la /usr/lib/libsapjco3.so 2>&1 && \
    echo "[2/3] 动态库依赖" && ldd /usr/lib/libsapjco3.so 2>&1 && \
    echo "[3/3] JVM 库搜索路径" && java -XshowSettings:properties -version 2>&1 | grep "java.library.path" && \
    if [ -f /usr/lib/libsapjco3.so ]; then ldd /usr/lib/libsapjco3.so 2>&1 | grep -q "not found" && echo "最终结论: FAIL" || echo "最终结论: OK"; else echo "最终结论: FAIL — 文件不存在"; fi
    

    5. 后续预防措施

    措施 责任方 说明
    Dockerfile 固化 JCo 库复制 开发/CI 构建阶段 COPYRUN jar xf ... cp /usr/lib/
    CI 构建后校验 CI/DevOps test -f /usr/lib/libsapjco3.so,缺失则构建失败
    镜像发布前冒烟 测试/运维 部署后执行 4.4 验证命令
    多基地构建脚本统一 开发 避免各基地 Dockerfile 分叉导致回退
    发版 Checklist 运维 SAP 相关服务发版时,确认 JCo 库检查通过

    6. 建议数值

    配置项 当前值 建议值 原因/后果
    Deployment 镜像 20260630_182454-eb02109d(已修复) 保持 ≥ 20260630 且含 /usr/lib/libsapjco3.so 低于此版本或构建未校验可能再次缺库
    /usr/lib/libsapjco3.so 存在,5,379,810 字节 必须存在,大小约 5.1~5.4MB 缺失则 UnsatisfiedLinkError,SAP 同步失败
    JAVA_OPTS -Dspring.profiles.active=igo-site-a-prod -Dserver.port=8080 -Xms2G -Xmx2G -XX:+UseG1GC -Xss256k 无需加 -Djava.library.path(镜像正确时) 库在 /usr/lib/ 时 Linux 可正常加载;仅应急 initContainer 方案才需加
    initContainer 无(正常情况) 永久修复应靠镜像,不应依赖 initContainer
    replicas 1 按业务负载配置(历史曾用 2~3) 与 JCo 无关
    CI 构建校验 无(推测) 增加 test -f /usr/lib/libsapjco3.so 防止 20260423 类构建回退再次发生
    Readiness 探针 timeout 2s 保持 2s;SAP 初始化慢时可评估 5s JCo 缺库会导致健康检查超时,根因仍是镜像

    附录 A:时间线

    时间 事件
    2026-04-16 镜像 20260416/usr/lib/libsapjco3.so(正常)
    2026-04-23 镜像 20260423 发版,丢失 /usr/lib/libsapjco3.so
    2026-06-27 基地-A 运行 20260423,Pod 多次重启,SAP JCo 报错
    2026-06-30 开发发版 20260630_182454-eb02109d,恢复原生库
    2026-07-01 验证通过,JCo 原生库正常

    附录 B:igo 标识对照

    公开版标识 含义
    基地-A 故障发生集群
    基地-B 对照正常集群
    k8s-master-01 基地-A kubectl 控制节点
    k8s-master-ref 基地-B kubectl 控制节点
    k8s-worker-02 故障 Pod 所在工作节点
    ns-igo-prod-a 基地-A 应用命名空间
    ns-igo-prod-b 基地-B 应用命名空间
    igo-mes-auth-center-xxxxx-yyyyy 故障 Pod 名称(示例占位)
    igo-mes-auth-center-zzzzz-wwwww 修复后 Pod 名称(示例占位)
    harbor-a.igozhang.cn/igo-project-a 基地-A 镜像仓库
    harbor-b.igozhang.cn/igo-project-b 基地-B 镜像仓库
    igo-site-a-prod 基地-A Spring Profile
    10.0.0.12 Pod IP(示例占位)

    文档编号 igo-20260701-001

    MP3