跳到主要内容

K8S Heapdump Chrome DevTools

2024年12月21日
柏拉文
越努力,越幸运

一、认识


Kubernetes 中部署 Node.js 服务后,如果收到内存不足报警时,我们需要通过 Heapdump 来生成内存快照, 进行快照采集。但是在 Kubernetes 集群多节点部署环境下, 有以下难点和挑战:

二、在 Kubernetes 集群多节点部署环境下, 如何生成特定的 Pod 快照文件?


2.2 通过访问特定 Pod IP

1. 访问指定 Pod: 使用 kubectl 命令找到目标 Pod 的名称, 获取目标 PodIP 地址

kubectl get pods -n <namespace>

2. 触发 API 生成快照: 使用 curl 或类似工具访问目标 Pod/heap-snapshot 路径

curl -X POST -H "x-auth-token: bolawen" http://<pod-ip>:3000/heap-snapshot

2.2 通过容器 Exec 直接触发

在某些情况下,无法直接通过 HTTP 调用接口,你可以通过 kubectl exec 手动触发 Heapdump 的生成。

1. 进入容器

kubectl exec -it <pod-name> -n <namespace> -- /bin/sh

2. 访问服务

三、在 Kubernetes 集群多节点部署环境下, 指定 Pod 生成快照时,如何保证服务的高可用性?


Heapdump 生成快照 会消耗大量的内存和 CPU,从而会影响服务的正常运行。有时候, Node 服务会中断,根据当时服务器内存大小这个时间会在 2 ~ 30min 左右。

3.1 扩展 Pod 副本数

确保服务高可用性和生成 Heapdump 快照的核心策略是将快照生成的资源消耗隔离。可以通过扩展 Pod 副本数,通过负载均衡消除单个 Pod 性能下降的影响。

思路: 通过 ServiceNode.js 服务暴露给外部访问,并结合 Deployment ReadinessProbe 确保 Service 仅将流量分发给健康的 Pod, 保证服务稳定和服务高可用。配置Deployment replicas, 配置 Node.js Pod 多个副本, 在生成 Heapdump 之前,通过 Kubernetes 修改 PodreadinessProbe,让流量从负载均衡中移除, 将当前 Pod 的流量隔离,防止因性能下降影响请求响应。这样即使某个 Pod 性能受损,服务流量会自动分配到其他健康的 Pod,保证服务的高可用性。

Node.js 服务: 在生成 Heapdump 之前,通过 Kubernetes 修改 PodreadinessProbe,让流量从负载均衡中移除。生成后,恢复 Pod

function generateHeapSnapshot(snapshotType, onError, onSuccess) {
triggerGc();

fs.writeFileSync("/tmp/healthy", "0"); // 模拟 readinessProbe 不健康状态
const filename = path.join(
CONFIG.heapSnapshotDir,
`heapdump-${snapshotType}-${Date.now()}.heapsnapshot`
);
heapdump.writeSnapshot(filename, (err, file) => {
if (err) {
console.error(`Error writing heap snapshot (${snapshotType}):`, err);
onError?.(err);
} else {
console.log(`Heap snapshot (${snapshotType}) saved to ${file}`);
uploadViaScp(file);
onSuccess?.(file);
fs.writeFileSync("/tmp/healthy", "1"); // 恢复健康状态
}
});
}

Deployment 部署:

readinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5

3.2 基于独立进程生成快照

确保服务高可用性和生成 Heapdump 快照的核心策略是将快照生成的资源消耗隔离。可以使用 child_process 独立进程分离快照生成逻辑,避免主服务受影响。

思路: 将生成 Heapdump 的任务交给一个独立的进程,避免主服务受到影响。快照生成由独立的脚本或服务运行。主服务通过异步调用启动快照生成服务。

const { spawn } = require("child_process");

// 在生成快照时启动独立的子进程
const heapdumpProcess = spawn("node", ["heapdump-script.js"], {
detached: true,
stdio: "ignore",
});
heapdumpProcess.unref();

3.2 基于 Job 分离快照生成与服务运行

确保服务高可用性和生成 Heapdump 快照的核心策略是将快照生成的资源消耗隔离。可以使用 Job 或 分离快照生成逻辑,避免主服务受影响。

思路: 通过 KubernetesJobCronJob 创建临时 Pod 专门用于生成 Heapdump,而不是在主服务 Pod 中生成。Heapdump 的生成在独立的 Pod 中进行,完全不影响主服务。

Job 部署:

apiVersion: batch/v1
kind: Job
metadata:
name: heapdump-generator
spec:
template:
spec:
containers:
- name: heapdump
image: my-service-image:latest
command: ["node", "generate-heapdump.js"]
restartPolicy: Never

Node.js 服务: 在服务代码中使用 Kubernetes API 触发 Job

const k8s = require("@kubernetes/client-node");
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const batchV1 = kc.makeApiClient(k8s.BatchV1Api);

batchV1.createNamespacedJob("default", {
metadata: { name: "heapdump-generator" },
spec: { ... },
});