Java Kubernetes 容器中无法执行 jsp jstat 等诊断命令
Kubernetes 诊断工具 JVM About 5,449 words现象
Java的jps、jstat等诊断命令在Kubernetes的Pod中失效。
jps
jps没有任何输出。
bash-4.4$ jps
bash-4.4$
jstat
jstat显示无法找到进程为1的Java服务。
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的进程ID为1,所以可以直接使用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
因为jps、jstat、jcmd等命令都使用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还是在2022年8月创建的,猜想是该镜像发布的时间。
所以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 ..
再次使用jps、jstat、jcmd等诊断命令,能正常使用。
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$
————        END        ————
Give me a Star, Thanks:)
https://github.com/fendoudebb/LiteNote扫描下方二维码关注公众号和小程序↓↓↓