在k8s中查看使用 Arthas 查看 JAR 包源码的完整实战
在 Kubernetes 容器中使用 Arthas 查看 JAR 包源码的完整实战
作为一名 Java 开发者,你是否曾遇到过这样的困境:线上应用出了问题,但手头只有正在运行的 JAR 包,没有源码,无法快速定位逻辑?在 Kubernetes 环境下,甚至连容器内部都没有
curl或wget,如何破局?本文将详细记录一次在 K8s Pod 中利用 Arthas 反编译查看源码的完整过程,涵盖从环境准备、踩坑到最终胜利的所有细节。(文中所有敏感信息均已使用占位符替换)
一、背景与目标
我们的应用部署在 Kubernetes 集群中,镜像为基础 JDK 17 的精简镜像,内部没有 curl 工具。我们需要查看某个业务类的 process 方法源码,以排查数据转换的 bug。
目标 Pod:my-app-deployment-7b9d8f6c4d-abc12,命名空间 my-namespace
目标类:com.example.business.service.impl.MyService#process(com.example.common.model.DataEntity)
二、环境信息
-
Kubernetes 集群版本:v1.24+
-
Pod 内 JDK 版本:OpenJDK 17
-
Arthas 版本:4.3.1
-
容器基础镜像:Alpine Linux(无
curl,但有wget)
三、操作步骤全记录
1. 确认 Pod 状态及命名空间
首先,列出命名空间 my-namespace 下的所有 Pod:
[root@k8s-master-01 ~]# kubectl get pod -n my-namespace
NAME READY STATUS RESTARTS AGE
connector-saas-5566cb95d-fpqjd 1/1 Running 0 21m
my-app-deployment-7b9d8f6c4d-abc12 1/1 Running 0 40m
another-service-689fc9f6bf-89v6f 1/1 Running 0 42m
nfs-provisioner-8444644c9c-dgkwd 1/1 Running 2 (24d ago) 25d
nginx-sss-s1-5c58fbb8f8-b27v2 1/1 Running 0 37d
v-nginx-5c4df78469-9z796 1/1 Running 0 36d
目标 Pod my-app-deployment-7b9d8f6c4d-abc12 正在运行。
2. 进入容器(踩坑点:忘记加命名空间)
最初尝试直接 exec:
[root@k8s-master-01 ~]# kubectl exec -it my-app-deployment-7b9d8f6c4d-abc12 -- /bin/bash
Error from server (NotFound): pods "my-app-deployment-7b9d8f6c4d-abc12" not found
原因:没有指定 -n my-namespace,kubectl 默认在 default 命名空间查找,自然找不到。
正确命令:
[root@k8s-master-01 ~]# kubectl exec -it my-app-deployment-7b9d8f6c4d-abc12 -n my-namespace -- /bin/sh
由于容器内没有 /bin/bash,使用 /bin/sh 成功进入。
3. 查看 Java 进程 PID
进入容器后,执行:
/app # ps -ef | grep java
1 root 0:00 /bin/sh -c # 将自定义 hosts 写入 /etc/hosts cat /etc/hosts.custom >> /etc/hosts java $JAVA_OPTS -jar /app/app.jar
8 root 2:16 java -Xms1536m -Xmx1536m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:ReservedCodeCacheSize=128m -XX:MaxDirectMemorySize=256m -XX:+UseContainerSupport -jar /app/app.jar
605 root 0:00 grep java
关键发现:
-
PID 1 是启动脚本的 shell 进程,不是 JVM。
-
真正的 JVM 进程 PID 是 8。
-
稍后 attach 时必须用 PID 8,而不是 1。
4. 下载 Arthas(容器内无 curl)
尝试直接用 curl 下载:
/app # curl -O https://arthas.aliyun.com/arthas-boot.jar
/bin/sh: curl: not found
发现容器没有 curl,但幸运的是有 wget:
/app # wget --help
BusyBox v1.35.0 ... multi-call binary.
于是用 wget 下载:
/app # wget https://arthas.aliyun.com/arthas-boot.jar
Connecting to arthas.aliyun.com (203.119.175.202:443)
saving to 'arthas-boot.jar'
arthas-boot.jar 100% |*******************************************************************************************************| 144k 0:00:00 ETA
'arthas-boot.jar' saved
5. 启动 Arthas(踩坑:错误地使用了 PID 1)
初始尝试 attach 到 PID 1:
/app # java -jar arthas-boot.jar 1
[INFO] JAVA_HOME: /opt/openjdk-17
[INFO] arthas-boot version: 4.3.1
[INFO] Start download arthas from remote server: https://arthas.aliyun.com/download/4.3.1?mirror=aliyun
[INFO] File size: 16.29 MB, downloaded size: 11.27 MB, downloading ...
[INFO] Download arthas success.
[INFO] arthas home: /root/.arthas/lib/4.3.1/arthas
[INFO] Try to attach process 1
Picked up JAVA_TOOL_OPTIONS:
[ERROR] Start arthas failed, exception stack trace:
com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file /proc/1/root/tmp/.java_pid1: target process 1 doesn't respond within 10500ms or HotSpot VM not loaded
...
报错原因:PID 1 不是 JVM 进程,无法 attach。
解决:使用正确 PID 8 重新启动:
/app # java -jar arthas-boot.jar 8
这次成功,出现 [arthas@8]$ 提示符,表示已进入 Arthas 交互界面。
6. 使用 jad 反编译查看源码
6.1 直接查看整个类
[arthas@8]$ jad --source-only com.example.business.service.impl.MyService
输出类完整源码(略)。
6.2 只查看特定方法(推荐)
我们想看 process 方法的实现:
[arthas@8]$ jad --source-only com.example.business.service.impl.MyService process
如果该类只有一个 process 方法,直接输出该方法体。若有多个重载,Arthas 会列出所有重载签名,提示歧义。
6.3 处理方法重载(进阶)
如果提示 Found more than one method,可以先查看所有方法签名:
[arthas@8]$ sm com.example.business.service.impl.MyService
找到目标方法的完整签名后,再使用精确匹配(用空格分隔方法名和参数类型,但参数类型只写简单类名或全限定名,且不能带括号):
[arthas@8]$ jad --source-only com.example.business.service.impl.MyService process com.example.common.model.DataEntity
注意:这里没有 # 和括号,而是将方法名和参数类型用空格隔开。
6.4 如果上述仍不行,直接反编译全类并用 grep 过滤
[arthas@8]$ jad --source-only com.example.business.service.impl.MyService > /tmp/class.java
[arthas@8]$ cat /tmp/class.java | grep -A 20 "process"
7. 输出源码保存到文件(便于外部查看)
在 Arthas 内部或直接在 shell 下,将反编译结果重定向:
[arthas@8]$ jad --source-only com.example.business.service.impl.MyService process > /tmp/process.java
然后退出 Arthas(quit),从容器外使用 kubectl cp 将文件拷出:
# 在宿主机执行
[root@k8s-master-01 ~]# kubectl cp my-namespace/my-app-deployment-7b9d8f6c4d-abc12:/tmp/process.java ./process.java
四、问题排查经验总结
| 常见问题 | 原因 | 解决方案 |
|---|---|---|
kubectl exec 报 NotFound |
未指定命名空间 | 加上 -n <namespace> |
curl: not found |
镜像无 curl | 尝试 wget 或 kubectl cp |
| Arthas attach 失败(目标进程不响应) | 指定了错误的 PID(比如 shell 进程) | 用 ps -ef 找出真正的 Java 进程 PID |
jad 提示方法歧义 |
存在多个重载方法 | 用 sm 查看签名,或直接反编译全类手动查找 |
jad 命令带括号或 # 报错 |
Shell 解析特殊字符 | 方法参数应使用空格分隔,不加括号和 # |
五、其他值得推荐的方法
-
预先将 Arthas 打包进镜像:在 Dockerfile 中添加
RUN wget https://arthas.aliyun.com/arthas-boot.jar -O /opt/arthas/arthas-boot.jar,下次使用无需下载。 -
使用
kubectl cp上传:若容器内无 wget/curl,可在宿主机下载后kubectl cp传入。 -
使用
kubectl debug临时容器(K8s v1.25+)启动一个带工具的调试容器共享进程命名空间,更方便调试。
六、结语
经过一番波折,我们最终成功地在 K8s 容器中通过 Arthas 查看了目标 JAR 包的源码。整个过程的核心在于:
-
明确目标进程的 PID;
-
选择合适的工具下载方式;
-
正确使用
jad命令的语法。
Arthas 的强大不仅限于反编译,还包括动态跟踪、方法执行监控等,是线上问题排查的利器。希望这篇实战记录能帮助你在类似场景下快速解决问题。
附:本文使用到的所有命令速查表
# 进入 Pod
kubectl exec -it <pod> -n <namespace> -- /bin/sh
# 查看 Java 进程
ps -ef | grep java
# 下载 Arthas(有 wget)
wget https://arthas.aliyun.com/arthas-boot.jar
# 启动 Arthas(attach 到正确 PID)
java -jar arthas-boot.jar <PID>
# 反编译类
jad --source-only <类全限定名> [方法名] [参数类型...]
# 查看类的方法签名
sm <类全限定名>
# 复制文件到本地
kubectl cp <namespace>/<pod>:<容器内路径> <本地路径>
本文来自博客园,作者:程序员鲜豪,转载请注明原文链接:https://www.cnblogs.com/hg-blogs/p/21303739

浙公网安备 33010602011771号