Skip to content

Java-debug #186

@aibangjuxin

Description

@aibangjuxin

下面给你一个系统性、可落地的分析与解决方案,完全基于 GKE / Kubernetes 隔离模型 + JVM 诊断机制,并给出推荐的最佳实践,避免你在平台层反复踩坑。

一、问题分析(为什么 sidecar / kubectl debug + jmap 会失败)

你遇到的现象:

jmap -dump:live,format=b,file=myheap.hprof 8

卡住 / 超时 / 找不到进程

结论先行:

✅ 核心原因就是:进程不共享(PID namespace 不共享)

这是 Kubernetes 的默认安全模型,不是你操作问题。

1️⃣ Kubernetes 中容器的“进程隔离”真相

Pod ≠ 进程共享(默认)

Namespace 是否默认共享
Network ✅
Volume ✅
PID ❌
IPC ❌

也就是说:

Pod
├── java-app 容器
│ └── PID 8 -> java
└── debug / sidecar 容器
└── PID 8 -> 不存在 / 是另一个进程

👉 jmap attach 的 PID 是“容器视角”的 PID,不是 Pod 级 PID

2️⃣ jmap / jcmd 的 attach 原理(为什么必须共享 PID)

jmap 并不是“远程工具”,它依赖:
• /proc/
• JVM attach socket(Unix Domain Socket)
• ptrace / SIGQUIT

👉 这些全部要求:
• 在 同一个 PID namespace
• JVM 进程对你“可见”

所以结论是:

❌ 只装了 openjdk ≠ 可以 jmap

✅ 能看到 JVM PID 才是前提

3️⃣ 为什么 kubectl debug 也不行?

默认 kubectl debug 行为

kubectl debug pod/my-pod -it --image=openjdk

实际发生的是:
• 新增 ephemeral container
• ❌ 不共享 PID namespace
• ❌ 无法 attach JVM

所以结果与 sidecar 完全一样。

二、如何 100% 验证“进程不共享”这个根因

在 Java 容器中:

ps -ef | grep java
ls -ld /proc/8

在 debug / sidecar 容器中:

ps -ef | grep java
ls -ld /proc/8

你会看到:
• debug 容器中 没有 java
• /proc/8 不存在

👉 这一步是定性证据

三、可行方案全景图(从“能不能用”到“该不该用”)

方案 是否可行 生产可用 推荐度
sidecar / kubectl debug + jmap ❌ ❌ ❌
shareProcessNamespace ✅ ⚠️ ⚠️
修改 Dockerfile 内置 debug 工具 ✅ ✅ ⭐⭐⭐⭐⭐
Debug 专用镜像 ✅ ✅ ⭐⭐⭐⭐⭐
OOM 自动 HeapDump ✅ ✅ ⭐⭐⭐⭐

四、推荐的【最佳实践方案】——平台级标准解法

结论:

❌ 不推荐 sidecar / kubectl debug
✅ 推荐 构建 Debug 能力到镜像本身

✅ 方案一(最推荐):构建「Debug 版 Java 镜像」

设计思路
• 运行容器本身
• 拥有 jmap / jcmd / jstack
• 不依赖 namespace hack
• 可控、稳定、可 SOP 化

示例:完整 Dockerfile(生产级)

FROM eclipse-temurin:17-jre-jammy

安装诊断工具(仅 jdk 工具,不是完整 JDK 编译环境)

RUN apt-get update &&
apt-get install -y --no-install-recommends
openjdk-17-jdk-headless
procps
curl
vim
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY app.jar /app/app.jar

ENV JAVA_OPTS="
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp
-XX:+UnlockDiagnosticVMOptions
"

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"]

使用方式

kubectl exec -it pod/java-app -- bash
jps
jmap -dump:live,format=b,file=/tmp/heap.hprof

为什么这是最佳实践?

维度 优势
稳定性 不依赖 namespace
可控性 工具版本一致
安全 无 ptrace hack
平台化 可做成模板
可审计 镜像即能力

✅ 方案二:Debug 镜像 + 临时 Deployment(更安全)

架构思路
• 正式镜像:无 debug
• Debug 镜像:有 jmap
• 只在需要时替换 Deployment

kubectl set image deploy/app app=app-debug:latest

👉 这是大厂 SRE 的常见做法

⚠️ 方案三:shareProcessNamespace: true(不推荐生产)

spec:
shareProcessNamespace: true

问题
• 放大 blast radius
• 容器间可互相 kill
• 破坏隔离模型
• 安全审计难过

👉 只适合实验 / PoC

五、平台级最佳实践(强烈建议你采纳)

1️⃣ JVM 预埋诊断能力(不等出问题)

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp
-XX:+ExitOnOutOfMemoryError

2️⃣ Heap dump 存储策略

/tmp -> emptyDir -> sidecar -> GCS

或:

kubectl cp pod:/tmp/heap.hprof .

3️⃣ 明确禁止的做法(写进 SOP)

❌ sidecar jmap
❌ kubectl debug attach JVM
❌ ptrace hack

六、最终结论(给你一句能直接写进文档的)

在 GKE 中,jmap / jcmd 必须与 JVM 进程处于同一个 PID namespace。
sidecar 与 kubectl debug 默认不共享进程空间,因此 无法用于 JVM heap dump。
唯一稳定、可维护的方案是:将 JVM 诊断工具构建进应用镜像,或使用 Debug 专用镜像。

如果你需要,我可以下一步帮你:
• 输出 《GKE Java Debug 官方 SOP》
• 或给你一份 平台统一 Java Base Image 规范(prod / debug 双版本)
• 或设计 HeapDump → GCS → 分析工具 的完整链路

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions