Kubernetes(Docker)容器部署加载不到 .so动态链接文件(java.lang.UnsatisfiedLinkError: no xxx in java.library.path)

李法
2023-12-01

找了一下午,网上文章有很多,但都不适用,因为我是通过容器进行部署服务,然后容器加载.so动态链接文件,所以网上大多数文章是设置Linux的动态链接目录,这里统一记录一下解决方法:

1、Linux设置LD_LIBRARY_PATH环境变量

普通的 Jar 包加载 .so 可以通过设置动态链接目录的方式来找到动态链接文件

$ vim /etc/profile
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/oas/
$ source /etc/profile

假如动态链接文件名叫做 libauthd.so,Java代码中有两种方式加载,绝对路径方式可以添加上述动态链接库所在目录的环境变量,相对路径方式则必须指定上述动态链接库所在目录的环境变量

System.load("/usr/local/oas/library/auth/libauthd.so");
System.loadLibrary("authd");

2、容器加载.so文件

如果是容器部署,需要将.so文件加入到容器中,才可以找到这个.so文件,否则死活也不会找到的,k8s deploy资源文件中,我将宿主机和容器的.so所在目录都配置为 /usr/local/oas/library/auth,当然程序所需要的 .so 文件也需要在 /usr/local/oas/library/auth 下,这样就可以将 .so 文件挂载到容器中了,然后我 Java 代码使用 System.load("/usr/local/oas/library/auth/libauthd.so"); 的方式去加载容器中的 .so动态链接文件,下面是我的资源文件配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: openailab-security-auth-service
  namespace: oas-dev
  labels:
    name: openailab-security-auth-service
spec:
  replicas: 1
  selector:
    matchLabels:
      name: openailab-security-auth-service
  template:
    metadata:
      labels:
        name: openailab-security-auth-service
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: env
                operator: In
                values:
                - dev
      containers:
      - name: openailab-security-auth-service
        image: 10.12.1.202:8088/oascloud/openailab-security-auth-service:latest
        env:
        - name: APOLLO_APP_ID
          value: 'openailab-security-auth-service'
        - name: APOLLO_EUREKA_URI
          value: 'http://10.98.100.95:8080/'
        - name: APOLLO_NAMESPACE
          value: 'application'
        - name: SPRING_EUREKA_URI
          value: 'http://10.111.176.152:8080/eureka/'
        - name: LD_LIBRARY_PATH
          value: '$LD_LIBRARY_PATH:/usr/local/oas/library/auth'
        volumeMounts:
        - name: openailab-security-auth-service
          mountPath: /usr/local/logs
        - name: auth-library
          mountPath: /usr/local/oas/library/auth
        command: ['sh','-c','java -server -Xms512m -Xmx1024m -Djava.io.tmpdir=/var/tmp -Duser.timezone=Asia/Shanghai -jar openailab-security-auth-service-*.jar --server.port=8080 --spring.profiles.active=remote']
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: 512Mi
            cpu: 500m
          limits:
            memory: 1024Mi
            cpu: 1000m
      initContainers:
      - name: init-apollo
        image: busybox
        command: ['sh', '-c']
        args:
        - echo Prepare to apollo initialize..;
          until nc -w 1 -z 10.98.100.95 8080;
          do echo Waiting for eureka initialization to complete..; sleep 10; done;
          echo Eureka is ready.;
          until nc -w 1 -z 10.98.100.95 8090;
          do echo Waiting for admin initialization to complete..; sleep 10; done;
          echo Admin is ready.;
          echo Apollo Initialization successful!;
      volumes:
      - name: openailab-security-auth-service
        hostPath:
          path: /usr/local/logs
      - name: auth-library
        hostPath:
          path: /usr/local/oas/library/auth

然后是我的Dockerfile文件,在最开始定位问题的时候,我一度怀疑是因为简易的基础镜像导致目标so依赖Linux资源不存在造成的,后来通过命令进入容器查看.so依赖资源发现并不是这个原因,相关文章传送门如下:

Linux下查看so依赖的库

Docker进入容器的4种方式

Kubernetes进入容器的方法

而是因为容器中环境变量 LD_LIBRARY_PATH 没有包含目标 .so 所在动态链接目录,导致目标 .so 依赖不到同级目录内的其他 .so 文件(如果是通过容器的方式部署服务,并且需要依赖 .so 动态链接文件的,则建议必须在容器中通过  LD_LIBRARY_PATH  添加动态链接文件所在目录,否则如果该 .so 文件依赖容器内同级目录中的其他 .so 文件,虽然是在同级目录,但依然会找不到依赖的同级 .so 文件,这里可以进入容器通过 ldd 命令查看 .so 所需依赖文件是否能够找到) ,解决办法就是在k8s资源文件中(上述配置)加入 $LD_LIBRARY_PATH:/usr/local/oas/library/auth 环境变量(如果刚才没注意,可以到上述配置文件中再仔细看一下),这里我选择简易版Jdk8环境作为基础镜像,若想用完整版Centos、Jdk8的Dockerfile来做基础镜像(制作出来的镜像大概680MB),传送门如下:

Docker制作基于Centos7的Jdk8镜像

制作简易版Jdk8基础镜像Dockerfile(制作出来的镜像大概190MB),代码如下:

FROM frolvlad/alpine-java:jre8-slim
MAINTAINER oas.cloud
ARG JAR_FILE
COPY ${JAR_FILE} /usr/local/oas/
WORKDIR /usr/local/oas/

然后通过Jenkins再重新发布容器就可以了,到此问题解决,如果各位有解决不了的,可以评论区联系我。

 类似资料: