如何获取宿主机上容器中的jvm数据

How to get the jvm data in the container on the host

有这样的需要检测容器中的jvm代码是否被恶意修改。我google一些相关问题,我们可以通过Java Instrumentation动态监控jvm。但是第一步失败了,无法获取到容器中的jvm。如果是host jvm就ok了
例如

Test.java

import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import java.io.IOException;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException, AttachNotSupportedException {
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        for (VirtualMachineDescriptor virtualMachineDescriptor : list) {
            System.out.println("virtualMachineDescriptor = " + virtualMachineDescriptor);
        }
    }
}

我在后台启动了一个springboot项目。 javac 和 运行 它。

➜  java javac Main.java
➜  java java Main
virtualMachineDescriptor = sun.tools.attach.BsdAttachProvider@7a81197d: 20819 springboot.jar
virtualMachineDescriptor = sun.tools.attach.BsdAttachProvider@7a81197d: 21492 Main
virtualMachineDescriptor = sun.tools.attach.BsdAttachProvider@7a81197d: 41035

有效。我打算在容器中实现同样的效果

这是我的开发环境


OS: Ubuntu 18.04  

JDK: openjdk8 

docker: 
Client: Docker Engine - Community
 Version:           20.10.6
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        370c289
 Built:             Fri Apr  9 22:46:01 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.6
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       8728dd2
  Built:            Fri Apr  9 22:44:13 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.4
  GitCommit:        05f951a3781f4f2c1911b05e61c160e9c30eaa8e
 runc:
  Version:          1.0.0-rc93
  GitCommit:        12644e614e25b05da6fd08a38ffa0cfe1903fdec
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0


  1. 在后台启动一个网络容器 docker run -d -p 8080:8080 springboot_demo(springboot_demo那个镜像是我自己建的)
FROM openjdk:8 

COPY springboot.jar app.jar 

EXPOSE 8080 

ENTRYPOINT ["java","-jar","/app.jar"]

  1. docker top container_id
    我得到容器 pid 24820

  2. javacode

import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import java.io.IOException;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException, AttachNotSupportedException {
        ¦ VirtualMachine vm =  VirtualMachine.attach("24820");
        ¦ System.out.println(vm);

    }
}

javac Main.java && java Main 我遇到了这些错误。


Exception in thread "main" com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded
    at sun.tools.attach.LinuxVirtualMachine.<init>(LinuxVirtualMachine.java:106)
    at sun.tools.attach.LinuxAttachProvider.attachVirtualMachine(LinuxAttachProvider.java:63)
    at com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:208)
    at Main.main(Main.java:17)

是否可以在同一台机器上从外部获取容器中的jvm数据?

JDK 8 不支持命名空间,因此无法附加到不同挂载命名空间(即容器)中的 JVM 进程。

Attach API 已修复,可以与 JDK 10 中的容器一起使用。

同时有 jattach 项目解决了这个问题。
jattach 允许连接到容器中的任何 HotSpot JVM(包括 JDK 8 甚至更早的 JDKs),即使主机上没有安装 JDK(这是一个很小的纯 C 语言程序)。您可以使用 jattach 代替 Java 附加 API 以执行任何操作:

  • 加载Java代理;
  • 获取线程转储和堆转储;
  • 调用 jcmd 命令等。