背景:

基础环境centos8+kubeadm1.20.5+cilium+hubble环境搭建,线上主要跑的php nodejs java的环境。
java的pod昨天频繁出现了cpu 90%的占用率告警:
image.png
虽然cpu是可压缩资源(compressible resources ),应用只会饥饿,不会像是内存爆了一样OOM.但是也需要进行一下性能分析,看一眼是代码逻辑有问题,还是资源分配的大小不合理。

就想传统的方式进入容器查看pid,运行jstack命令进行分析了:
Kubernetes 运行java程序无法使用jmap,jstack的解决方案 - 图2

  1. kubectl exec -it xxx-xxxx-8556c7f98b-9nh28 sh -n official
  2. / # ps
  3. PID USER TIME COMMAND
  4. 1 root 2h38 java -Djava.security.egd=file:/dev/./urandom -jar /xxx-1.0-SNAPSHOT.jar
  5. 4178 root 0:00 sh
  6. 4193 root 0:00 ps
  7. / # jstack 1
  8. 1: Unable to get pid of LinuxThreads manager thread

what jstack命令无法分析应用……

解决过程:

百度搜索了一下,参见:
https://blog.csdn.net/qq_16887777/article/details/107417059

1. 关于pid1

发现服务的pid=1,网上查询得知pid1-5为Linux的特殊进程。
pid=1 :init进程,系统启动的第一个用户级进程,是所有其它进程的父进程,引导用户空间服务。

pid=2 :kthreadd:用于内核线程管理。

pid=3 :migration,用于进程在不同的CPU间迁移。

pid=4 :ksoftirqd,内核里的软中断守护线程,用于在系统空闲时定时处理软中断事务。

pid=5 :watchdog,此进程是看门狗进程,用于监听内核异常。当系统出现宕机,可以利用watchdog进程将宕机时的一些堆栈信息写入指定文件,用于事后分析宕机的原因。
根据排除法最简单的方式就是让java启动的进程pid不是1-5就可以了?嗯启动命令不是第一个。

2. 修改Dcokerfile java启动方式

上下我的Dockerfile

  1. FROM openjdk:8-jdk-alpine
  2. VOLUME /tmp
  3. ENV TZ=Asia/Shanghai
  4. RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
  5. ADD target/diversion-0.0.1-SNAPSHOT.jar diversion-0.0.1-SNAPSHOT.jar
  6. ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/xxxx-0.0.1-SNAPSHOT.jar"]

嗯个人认为可以将 java 启动文件写入脚本?然后ENTRYPOINT sh脚本?偶然看到一个tini的方法:docker运行java程序 使用jmap,jstack命令 tini运行的程序获取进程.修改Dockerfile如下:

  1. FROM openjdk:8-jdk-alpine
  2. VOLUME /tmp
  3. ENV TZ=Asia/Shanghai
  4. RUN apk add --no-cache tini
  5. RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
  6. ADD target/diversion-0.0.1-SNAPSHOT.jar diversion-0.0.1-SNAPSHOT.jar
  7. ENTRYPOINT ["tini","java","-Djava.security.egd=file:/dev/./urandom","-jar","/xxxx-0.0.1-SNAPSHOT.jar"]

3. 构建并上传镜像到镜像仓库

  1. docker build -t ccr.ccs.tencentyun.com/xxxx/xxxx:xxxx
  2. docker push ccr.ccs.tencentyun.com/xxxx/xxxx:xxxx

4. 部署应用并测试

  1. cat > test.yaml << EOF
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: test
  6. spec:
  7. replicas: 1
  8. strategy:
  9. rollingUpdate:
  10. maxSurge: 1
  11. maxUnavailable: 0
  12. selector:
  13. matchLabels:
  14. app: test
  15. template:
  16. metadata:
  17. labels:
  18. app: test
  19. spec:
  20. containers:
  21. - name: test
  22. image: ccr.ccs.tencentyun.com/xxxx/xxxx:xxxx
  23. env:
  24. - name: SPRING_PROFILES_ACTIVE
  25. value: "official"
  26. ports:
  27. - containerPort: test
  28. resources:
  29. requests:
  30. memory: "256M"
  31. cpu: "250m"
  32. limits:
  33. memory: "1024M"
  34. cpu: "500m"
  35. imagePullSecrets:
  36. - name: tencent
  37. ---
  38. apiVersion: v1
  39. kind: Service
  40. metadata:
  41. name: test
  42. labels:
  43. app: test
  44. spec:
  45. ports:
  46. - port: 8081
  47. protocol: TCP
  48. targetPort: 8081
  49. selector:
  50. app: test
  51. EOF
  1. kubectl apply -f test.yaml -n test
  2. kubectl exec -it xxxxxxx sh -n test
  3. top

image.png
可以看到进程号是1的进程是tini的 有额外一个单独的进程号为7的java 进程,运行jstack进行测试:
jstack 7
image.png
嗯能运行jstack就算是实现了自己需要的了。其他的先忽略。

总结:

1. 关于linux的pid

2. tini的命令,可参照https://zhuanlan.zhihu.com/p/59796137

3. docker的启动方式与进程隔离实现方式