Java Kubernetes 容器中无法执行 jsp jstat 等诊断命令

Kubernetes 诊断工具 JVM About 5,449 words

现象

Javajpsjstat等诊断命令在KubernetesPod中失效。

jps

jps没有任何输出。

bash-4.4$ jps
bash-4.4$ 

jstat

jstat显示无法找到进程为1Java服务。

bash-4.4$ jstat -gccause 1
sun.jvmstat.monitor.MonitorException: 1 not found
        at jdk.internal.jvmstat/sun.jvmstat.perfdata.monitor.protocol.local.PerfDataBuffer.<init>(PerfDataBuffer.java:84)
        at jdk.internal.jvmstat/sun.jvmstat.perfdata.monitor.protocol.local.LocalMonitoredVm.<init>(LocalMonitoredVm.java:68)
        at jdk.internal.jvmstat/sun.jvmstat.perfdata.monitor.protocol.local.MonitoredHostProvider.getMonitoredVm(MonitoredHostProvider.java:77)
        at jdk.jcmd/sun.tools.jstat.Jstat.logSamples(Jstat.java:107)
        at jdk.jcmd/sun.tools.jstat.Jstat.main(Jstat.java:70)
Caused by: java.lang.IllegalArgumentException: Could not map vmid to user Name
        at java.base/jdk.internal.perf.Perf.attach(Native Method)
        at java.base/jdk.internal.perf.Perf.attachImpl(Perf.java:272)
        at java.base/jdk.internal.perf.Perf.attach(Perf.java:202)
        at jdk.internal.jvmstat/sun.jvmstat.perfdata.monitor.protocol.local.PerfDataBuffer.<init>(PerfDataBuffer.java:64)
        ... 4 more

备注

jcmd同样也无法列出Java服务,但可以使用jcmd <pid>

因为容器中Java的进程ID1,所以可以直接使用jcmd 1

排查

查看日志

查看日志可以看到started by ?Java无法知道目前的用户是谁。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.7.0)

Starting TestApplication using Java 11.0.16 on ns-test-677777bcb5-t7c4g with PID 1 (/app/test.jar started by ? in /app)

user.name

使用jshell获取用户名称显示?

jshell> System.getProperty("user.name")
$1 ==> "?"

PerfData

因为jpsjstatjcmd等命令都使用PerfData生成的数据,而PerfData默认是存储在/tmp/hsperfdata_<username>下的,并且监控信息以进程号存储在该文件夹下。

查看/tmp目前发现,hsperfdata_root目录下并没有进程为1的文件。

bash-4.4$ ls -al /tmp
total 20
drwxrwxrwt 1 root root 4096 Jul 21 16:48 .
drwxr-xr-x 1 root root 4096 Jul 21 14:36 ..
drwxr-xr-x 2 root root 4096 Aug  4  2022 hsperfdata_root
drwx------ 2 1000 root 4096 Jul 21 14:36 tomcat-docbase.8081.3799688995221812322
drwx------ 3 1000 root 4096 Jul 21 14:36 tomcat.8081.6049079183157077699

whoami

/tmp目录发现,从hsperfdata_root文件夹得知Java推断是root用户,而日志显示是started by ?。从文件夹的信息看到用户是1000

使用whoami命令查看,得到目前是以1000用户启动。

bash-4.4$ whoami
whoami: cannot find name for user ID 1000

Dockerfile

查看Dockerfile,发现以1000用户启动了Java进程。

FROM openjdk:11-oraclelinux8
ADD target/test-0.0.1-SNAPSHOT.jar /app/test.jar
USER root
RUN chmod -R 777 /app
RUN chown 1000:1000 -R /app
WORKDIR /app
USER 1000
CMD ["java","-jar","test.jar"]

/etc/password

查看容器中的用户有哪些,发现并没有1000这个用户。

Dockerfile中并没有添加用户的操作。

bash-4.4$ cat /etc/passwd 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin

hsperfdata_root

后又发现hsperfdata_root还是在20228月创建的,猜想是该镜像发布的时间。

所以Java进程压根就没有生成PerfData文件夹。

用户 1000

1000用户启动Java进程是为了安全考虑,但这又给诊断带来了麻烦,必须兼顾安全和诊断。

解决

方法一

Dockerfile中增加添加用户的操作。(不做演示了)

方法二

使用nobody

同时给/tmp也改成了nobody所属。

FROM openjdk:11-oraclelinux8
ADD target/test-0.0.1-SNAPSHOT.jar /app/test.jar
USER root
RUN chmod -R 777 /app /tmp
RUN chown nobody:nobody -R /app /tmp
WORKDIR /app
USER nobody:nobody
CMD ["java","-jar","test.jar"]

查看日志,发现服务能正确识别启动用户,started by nobody

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.7.0)

Starting TestApplication using Java 11.0.16 on ns-test-677777bcb5-t7c4g with PID 1 (/app/test.jar started by nobody in /app)

查看/tmp,发现生成了hsperfdata_nobody文件夹。

bash-4.4$ ls -al /tmp
total 24
drwxrwxrwx 1 nobody nobody 4096 Jul 21 16:59 .
drwxr-xr-x 1 root   root   4096 Jul 21 16:59 ..
drwxr-xr-x 2 nobody nobody 4096 Jul 21 17:01 hsperfdata_nobody
drwxrwxrwx 1 nobody nobody 4096 Aug  4  2022 hsperfdata_root
drwx------ 2 nobody nobody 4096 Jul 21 16:59 tomcat-docbase.8081.10018640566863022563
drwx------ 3 nobody nobody 4096 Jul 21 16:59 tomcat.8081.4651009197921297871

查看/tmp/hsprefdata_nobody文件夹,发现生成了以进程号为名称的文件1

bash-4.4$ ls -al /tmp/hsperfdata_*
/tmp/hsperfdata_nobody:
total 40
drwxr-xr-x 2 nobody nobody  4096 Jul 21 17:01 .
drwxrwxrwx 1 nobody nobody  4096 Jul 21 16:59 ..
-rw------- 1 nobody nobody 32768 Jul 21 17:22 1

/tmp/hsperfdata_root:
total 8
drwxrwxrwx 1 nobody nobody 4096 Aug  4  2022 .
drwxrwxrwx 1 nobody nobody 4096 Jul 21 16:59 ..

再次使用jpsjstatjcmd等诊断命令,能正常使用。

bash-4.4$ jps
1 test.jar
58 Jps
bash-4.4$ jstat -gccause 1
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT    LGCC                 GCC                 
 31.54   0.00  95.67  66.39  96.44  88.50     36    0.245     2    0.077     -        -    0.322 Allocation Failure   No GC               
bash-4.4$ jcmd
1 test.jar
84 jdk.jcmd/sun.tools.jcmd.JCmd
bash-4.4$
Views: 293 · Posted: 2023-12-28

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

扫描下方二维码关注公众号和小程序↓↓↓

扫描下方二维码关注公众号和小程序↓↓↓


Today On History
Browsing Refresh