Arthas排查Kubernetes中应用频繁挂掉重启异常


Posted in MySQL onFebruary 28, 2022

前言

其实最终定位到的问题还是蛮好解决的,但是因为应用在Kubernetes容器中的特殊性,导致在使用Arthas过程中出现了各种问题,所以单独成文和大家分享下。照例先讲下问题发生的背景,一个很老的web系统部署在tomcat容器里。近期打成了镜像丢到了Kubernetes环境中运行,总是各种挂,在Kubernetes层面定位了很久没找到具体问题,但是初步定位到是因为系统中的报表导出接口导致的问题,最后使用Arthas找到问题并解决。

Kubernetes容器的特殊性

首先说下,我们的Kubernetes容器中运行的应用都是基于自己构建的基础镜像打包的,如JDK,和tomcat基础镜像,为了减小打包后应用的体积,我们对JDK进行了大量的删减,只保留了最小的jre运行时环境。这样导致的后果是在应用出现问题需要使用jdk工具时,如jinfo、jmap、jstack等都没了,没了这些工具就相当于一个战士没了武器,束手无策了,但是最后忽然想到了Arthas,java线上排错利器。

使用到的工具Arthas

Arthas是阿里巴巴开源的一款在线诊断java应用程序的工具,是greys工具的升级版本,深受开发者喜爱。当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  • 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  • 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  • 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  • 是否有一个全局视角来查看系统的运行状况?
  • 有什么办法可以监控到JVM的实时运行状态?
  • Arthas采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

项目地址:https://github.com/alibaba/arthas

Arthas的使用

第一步:下载arthas-boot.jar

wget https://alibaba.github.io/arthas/arthas-boot.jar

第二步:运行

java -jar arthas-boot.jar

运行后,并没有出现熟悉的java进程选择界面,而是一闪而过,如下:

/opt/kl # java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.1.0
[INFO] Can not find java process. Try to pass <pid> in command line.
Please select an available pid.

按官方的说明文档描述,假如出现了找不到pid的情况,在当前目录下应该会输出相关的log,但是我看了并没有日志,那只能尝试是否可以手动指定pid来使用Arthas了。在官网没找到类似说明,只能试试java -jar arthas-boot.jar -help看下,输出如下

/opt/kl # java -jar arthas-boot.jar -help
[INFO] arthas-boot version: 3.1.0
Usage: arthas-boot [-h] [--target-ip <value>] [--telnet-port <value>]
       [--http-port <value>] [--session-timeout <value>] [--arthas-home <value>]
       [--use-version <value>] [--repo-mirror <value>] [--versions] [--use-http]
       [--attach-only] [-c <value>] [-f <value>] [--height <value>] [--width
       <value>] [-v] [pid]

Bootstrap Arthas

EXAMPLES:
  java -jar arthas-boot.jar <pid>
  java -jar arthas-boot.jar --target-ip 0.0.0.0
  java -jar arthas-boot.jar --telnet-port 9999 --http-port -1
  java -jar arthas-boot.jar -c 'sysprop; thread' <pid>
  java -jar arthas-boot.jar -f batch.as <pid>
  java -jar arthas-boot.jar --use-version 3.0.5
  java -jar arthas-boot.jar --versions
  java -jar arthas-boot.jar --session-timeout 3600
  java -jar arthas-boot.jar --attach-only
  java -jar arthas-boot.jar --repo-mirror aliyun --use-http

EXAMPLES的第一条显示出来了,直接在jar后面加上pid即可,执行后,还是不行,输出如下:

/opt/kl # java -jar arthas-boot.jar 1
[INFO] arthas-boot version: 3.1.0
[INFO] Start download arthas from remote server: https://maven.aliyun.com/repository/public/com/taobao/arthas/arthas-packaging/3.1.0/arthas-packaging-3.1.0-bin.zip
[INFO] Download arthas success.
[INFO] arthas home: /root/.arthas/lib/3.1.0/arthas
[INFO] Try to attach process 1
Exception in thread "main" java.lang.IllegalArgumentException: Can not find tools.jar under java home: /usr/java/jdk/jre1.8.0_191, please try to start arthas-boot with full path java. Such as /opt/jdk/bin/java -jar arthas-boot.jar
        at com.taobao.arthas.boot.ProcessUtils.findJavaHome(ProcessUtils.java:195)
        at com.taobao.arthas.boot.ProcessUtils.startArthasCore(ProcessUtils.java:206)
        at com.taobao.arthas.boot.Bootstrap.main(Bootstrap.java:441)

异常解析:

根据异常可以明显看到说找不到tools.jar这个工具包了,还是回归到Kubernetes容器环境中因为精简了jre运行时环境导致jdk很多功能受限了。后面我做了一个非常规的事情,就是在完整的jdk中找到了这个tools.jar,丢到了jre里的lib目录中,继续尝试,但是还有问题,如下:

/opt/kl # java -jar arthas-boot.jar 1
[INFO] arthas-boot version: 3.1.0
[INFO] arthas home: /root/.arthas/lib/3.1.0/arthas
[INFO] Try to attach process 1
[ERROR] Start arthas failed, exception stack trace:
java.lang.UnsatisfiedLinkError: no attach in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
        at java.lang.Runtime.loadLibrary0(Runtime.java:870)
        at java.lang.System.loadLibrary(System.java:1122)
        at sun.tools.attach.LinuxVirtualMachine.<clinit>(LinuxVirtualMachine.java:342)
        at sun.tools.attach.LinuxAttachProvider.attachVirtualMachine(LinuxAttachProvider.java:78)
        at com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:250)
        at com.taobao.arthas.core.Arthas.attachAgent(Arthas.java:73)
        at com.taobao.arthas.core.Arthas.<init>(Arthas.java:26)
        at com.taobao.arthas.core.Arthas.main(Arthas.java:100)
[ERROR] attach fail, targetPid: 1

异常解析:

可以看到在补全了tools.jar之后,出现了新的异常信息java.lang.UnsatisfiedLinkError: no attach in java.library.path,表明可能我们缺的东西不是一点半点,我们知道attach功能是Arthas实现原理的两大原理之一。

attach:jdk1.6新增功能,通过attach机制,可以在jvm运行中,通过pid关联应用

instrument:jdk1.5新增功能,通过instrument俗称javaagent技术,可以修改jvm加载的字节码

然后Arthas和其他诊断工具一样,都是先通过attach链接上目标应用,通过instrument动态修改应用程序的字节码达到不重启应用而监控应用的目的

最后的救命稻草

使用完整的JDK中的java命令。

以上方式都试过不行之后,最后我把完整的JDK给下载到本地了,然后通过jdk的bin目录下的java命令启动arthas-boot.jar终于ok了,出现了熟悉的java进程选择界面:

/opt/kl/jdk1.8.0_191/bin # ./java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.1.0
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 1 org.apache.catalina.startup.Bootstrap

最后定位到的问题其实很简单,我记录了Arthas大盘中关于内存部分的图,如下:

Arthas排查Kubernetes中应用频繁挂掉重启异常

上图从标题栏开始往下,分别是heap(堆内存)、eden_space(伊甸园区内存),survivor_space(幸存者区内存)、tenured_gen(老年代内存)。这张图是触发导出操作后的内存分布,堆内存从一开始的200M左右占用、到400M、到600M、一瞬间就飚升到900多兆了。最后从堆内存指标我们看到,总共989M,使用的内存已经飚升到988M了,这个时候其实应用已经挂了,Kubernetes容器已经在重启这个实例了。到这里基本已经到位到应用在容器中频繁挂掉重启问题的本质了。

但是为什么堆内存会这么小呢?

最终查明,有方面的原因:

1、因为我们这边都是spring boot应用,只有一个遗留的tomcat部署的应用,所以在镜像优化方面更偏向jdk基础基础镜像,而tomcat镜像没怎么关注,一开始对堆内存这块并没调优设置。

2、后面出现问题后,也确实想到过因为是导出报表导致应用挂掉,很可能是内存问题,设置过tomcat镜像内的堆内存大小,但是因为我们重新打包的镜像没有使用新的版本号,而是直接覆盖之前的版本,又使用Jenkins构建的,Jenkins所在主机拉过之前的镜像,导致镜像更新后,Jenkins打包时并没有去拉最新的调优过基础镜像。

解决问题

1、调优过的镜像加上新的版本号,让应用基于新的版本号构建镜像。或者清理下Jenkins所在主机的镜像,这个会导致第一次构建时速度变慢

2、优化导出报表的实现,我给的方案是,在导出大数据报表时,可以通过id分区,分别作业写入同一个服务器本地的文件中,然后让web容器映射下这个文件所在的目录,等所有分区的任务都结束后,直接组装一个文件下载链接返回给前端,让前端触发一次读文件操作即可。

最后尝试下jmap

除了使用Arthas外,最后还尝试了使用jmap工具,但是因为重新下载的JDK版本和主机jre版本不兼容,所有没用上。最后通过JDK发行的归档页面找到了对应的版本,还是成功的使用jmap -heap pid看到了内存情况,。内存分布也蛮清晰的,如:

/opt/kl/jdk1.8.0_191/bin # ./jmap -heap 1
Attaching to process ID 1, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.191-b12
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 3221225472 (3072.0MB)
   NewSize                  = 1073741824 (1024.0MB)
   MaxNewSize               = 1073741824 (1024.0MB)
   OldSize                  = 2147483648 (2048.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 966393856 (921.625MB)
   used     = 856023096 (816.3672409057617MB)
   free     = 110370760 (105.25775909423828MB)
   88.57911199302988% used
Eden Space:
   capacity = 859045888 (819.25MB)
   used     = 787314712 (750.8418197631836MB)
   free     = 71731176 (68.4081802368164MB)
   91.6499017104893% used
From Space:
   capacity = 107347968 (102.375MB)
   used     = 68708384 (65.52542114257812MB)
   free     = 38639584 (36.849578857421875MB)
   64.00529537736568% used
To Space:
   capacity = 107347968 (102.375MB)
   used     = 0 (0.0MB)
   free     = 107347968 (102.375MB)
   0.0% used
tenured generation:
   capacity = 2147483648 (2048.0MB)
   used     = 1521987528 (1451.4804153442383MB)
   free     = 625496120 (596.5195846557617MB)
   70.87306715548038% used

jdk归档下载页:https://www.oracle.com/technetwork/java/javase/downloads

结语

Arthas是一个非常不错的工具,我们线上多次使用Arthas定位过问题。不过像今天的这种Kubernetes容器环境的话,因为本身运行时环境的缺失,可能使用的时候会存在各种问题,这里主要还是分享个思路。希望能给类似场景的同学提供一个有用的参考。

以上就是Arthas排查Kubernetes中应用频繁挂掉重启异常的详细内容,更多关于Arthas异常排查Kubernetes频繁重启的资料请关注三水点靠木其它相关文章!

MySQL 相关文章推荐
详解Mysql 函数调用优化
Apr 07 MySQL
MySQL命令行操作时的编码问题详解
Apr 14 MySQL
MySQL大小写敏感的注意事项
May 24 MySQL
MYSQL数据库使用UTF-8中文编码乱码的解决办法
May 26 MySQL
MySQL中InnoDB存储引擎的锁的基本使用教程
May 26 MySQL
sql注入教程之类型以及提交注入
Aug 02 MySQL
SpringBoot连接MySQL获取数据写后端接口的操作方法
Nov 02 MySQL
你真的会用Mysql的explain吗
Mar 31 MySQL
MySQL创建管理RANGE分区
Apr 13 MySQL
Mysql开启外网访问
May 15 MySQL
MySQL 逻辑备份 into outfile
May 15 MySQL
MySQL控制流函数(-if ,elseif,else,case...when)
Jul 07 MySQL
一文搞懂MySQL索引页结构
MySQL七大JOIN的具体使用
一文弄懂MySQL索引创建原则
一文了解MySQL二级索引的查询过程
Mysql数据库表中为什么有索引却没有提高查询速度
教你如何让spark sql写mysql的时候支持update操作
Feb 15 #MySQL
一文弄懂MySQL中redo log与binlog的区别
Feb 15 #MySQL
You might like
PHP编码规范之注释和文件结构说明
2010/07/09 PHP
PHP将XML转数组过程详解
2013/11/13 PHP
php简单判断文本编码的方法
2015/07/30 PHP
php基于curl实现随机ip地址抓取内容的方法
2016/10/11 PHP
解决Laravel5.2 Auth认证退出失效的问题
2019/10/14 PHP
用jQuery中的ajax分页实现代码
2011/09/20 Javascript
js 时间函数应用加、减、比较、格式转换的示例代码
2013/08/23 Javascript
Javascript检查图片大小不要让大图片撑破页面
2014/11/04 Javascript
jquery 无限极下拉菜单的简单实例(精简浓缩版)
2016/05/31 Javascript
jQuery UI结合Ajax创建可定制的Web界面
2016/06/22 Javascript
JavaScript中闭包的写法和作用详解
2016/06/29 Javascript
基于touch.js手势库+zepto.js插件开发图片查看器(滑动、缩放、双击缩放)
2016/11/17 Javascript
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
2016/12/14 Javascript
Vue 过渡实现轮播图效果
2017/03/27 Javascript
浅谈vue自定义全局组件并通过全局方法 Vue.use() 使用该组件
2017/12/07 Javascript
vue 优化CDN加速的方法示例
2018/09/19 Javascript
对vue中的事件穿透与禁止穿透实例详解
2019/10/28 Javascript
js实现磁性吸附的示例
2020/10/26 Javascript
[02:06]DOTA2肉山黑名单魔法终结者 敌法师中文配音鉴赏
2013/06/17 DOTA
[17:13]DOTA2 HEROS教学视频教你分分钟做大人-斯拉克
2014/06/13 DOTA
从零学Python之入门(四)运算
2014/05/27 Python
详解Django中的form库的使用
2015/07/18 Python
CentOS下Python3的安装及创建虚拟环境的方法
2018/11/28 Python
python 获得任意路径下的文件及其根目录的方法
2019/02/16 Python
python3实现斐波那契数列(4种方法)
2019/07/15 Python
css3设置box-pack和box-align让div里面的元素垂直居中
2014/09/01 HTML / CSS
HTML5中如何显示视频呢 HTML5视频播放demo
2013/06/08 HTML / CSS
斯洛伐克香水和化妆品购物网站:Parfemy-Elnino.sk
2020/01/28 全球购物
网上卖盒饭创业计划书
2014/01/26 职场文书
2014端午节活动策划方案
2014/01/27 职场文书
公司营业员的自我评价
2014/03/04 职场文书
年终晚会活动方案
2014/08/21 职场文书
2015年幼儿园保育员工作总结
2015/04/23 职场文书
如何使用Python实现一个简易的ORM模型
2021/05/12 Python
Python下opencv使用hough变换检测直线与圆
2021/06/18 Python
图文详解matlab原始处理图像几何变换
2021/07/09 Python