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 InnoDB ReplicaSet(副本集)简单介绍
Apr 24 MySQL
详解MySQL的Seconds_Behind_Master
May 18 MySQL
浅谈mysql返回Boolean类型的几种情况
Jun 04 MySQL
MySQL 5.7常见数据类型
Jul 15 MySQL
mysql事务对效率的影响分析总结
Oct 24 MySQL
MySQL 数据类型详情
Nov 11 MySQL
SQL 聚合、分组和排序
Nov 11 MySQL
mysql使用instr达到in(字符串)的效果
Apr 03 MySQL
MySQL数据库如何查看表占用空间大小
Jun 10 MySQL
MySQL示例讲解数据库约束以及表的设计
Jun 16 MySQL
MySQL实现字段分割一行转多行的示例代码
Jul 07 MySQL
MySQL存储过程及语法详解
Aug 05 MySQL
一文搞懂MySQL索引页结构
MySQL七大JOIN的具体使用
一文弄懂MySQL索引创建原则
一文了解MySQL二级索引的查询过程
Mysql数据库表中为什么有索引却没有提高查询速度
教你如何让spark sql写mysql的时候支持update操作
Feb 15 #MySQL
一文弄懂MySQL中redo log与binlog的区别
Feb 15 #MySQL
You might like
虫族 Zerg 魔法科技
2020/03/14 星际争霸
php str_pad() 将字符串填充成指定长度的字符串
2010/02/23 PHP
需要注意的几个PHP漏洞小结
2012/02/05 PHP
浅谈PHP Cookie处理函数
2016/06/10 PHP
Laravel 关联模型-关联新增和关联更新的方法
2019/10/10 PHP
JavaScript 函数式编程的原理
2009/10/16 Javascript
js 兼容多浏览器的回车和鼠标焦点事件代码(IE6/7/8,firefox,chrome)
2010/04/14 Javascript
基于jquery的防止大图片撑破页面的实现代码(立即缩放)
2011/10/24 Javascript
div拖拽插件——JQ.MoveBox.js(自制JQ插件)
2013/05/17 Javascript
JavaScript九九乘法口诀表的简单实现
2016/10/04 Javascript
360提示[高危]使用存在漏洞的JQuery版本的解决方法
2017/10/27 jQuery
ES6 javascript中Class类继承用法实例详解
2017/10/30 Javascript
详解iview的checkbox多选框全选时校验问题
2019/06/10 Javascript
JS中作用域以及变量范围分析
2020/07/18 Javascript
Python的函数嵌套的使用方法
2014/01/24 Python
实例讲解Python中的私有属性
2014/08/21 Python
Python中Django框架利用url来控制登录的方法
2015/07/25 Python
python实现决策树C4.5算法详解(在ID3基础上改进)
2017/05/31 Python
Django 浅谈根据配置生成SQL语句的问题
2018/05/29 Python
详解python中的json和字典dict
2018/06/22 Python
在自动化中用python实现键盘操作的方法详解
2019/07/19 Python
python实现H2O中的随机森林算法介绍及其项目实战
2019/08/29 Python
Python 使用多属性来进行排序
2019/09/01 Python
Python定义函数实现累计求和操作
2020/05/03 Python
python实现企业微信定时发送文本消息的实例代码
2020/11/25 Python
html5视频自动横过来自适应页面且点击播放功能的实现
2020/06/03 HTML / CSS
Under Armour瑞典官方网站:美国高端运动科技品牌
2018/11/21 全球购物
叙述DBMS对数据控制功能有哪些
2016/06/12 面试题
舞蹈比赛获奖感言
2014/02/04 职场文书
大学开学计划书
2014/04/30 职场文书
励志广播稿300字(5篇)
2014/09/15 职场文书
离婚协议书怎么写的
2014/12/14 职场文书
2014年助理政工师工作总结
2014/12/19 职场文书
大学学生个人总结
2015/02/15 职场文书
综合素质评价自我评价
2015/03/06 职场文书
个人年终总结开头
2015/03/06 职场文书