使用 PVC 存儲 Java Heap Dump 文件

概述

本文檔說明如何在 Kubernetes 中使用 PersistentVolumeClaim (PVC) 來持久化存儲 Java 應用程式的堆轉儲 (Heap Dump) 文件,並使用容器 lifecycle 來管理文件清理。

背景

Java 應用程式在發生 OutOfMemoryError 時,可以配置 JVM 自動生成堆轉儲文件用於事後分析。但是默認情況下,這些文件存儲在容器的臨時目錄中,當 Pod 重啟時會丟失。本方案使用 PVC 來持久化存儲這些重要診斷文件。

架構設計

Pod
├── 應用容器
│   ├── JVM 應用程式
│   ├── Heap Dump 生成 (/heapdumps/)
│   └── Lifecycle 清理腳本
└── PVC 存儲卷
    ├── 持久化存儲
    └── 自動清理機制

前置條件

  • Kubernetes 集群
  • 存儲類 (StorageClass) 支持動態供應
  • kubectl 命令行工具

實現步驟

步驟 1: 創建 PersistentVolumeClaim

單個服務使用 (ReadWriteOnce)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: heapdump-storage
  namespace: your-namespace
  labels:
    app: your-app-name
    component: diagnostics
spec:
  accessModes:
    - ReadWriteOnce  # 只能被一個 Pod 掛載
  resources:
    requests:
      storage: 50Gi
  storageClassName: default  # Azure Disk

多個服務共享 (ReadWriteMany)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: shared-heapdump-storage
  namespace: your-namespace
  labels:
    component: shared-diagnostics
spec:
  accessModes:
    - ReadWriteMany  # 可以被多個 Pod 掛載
  resources:
    requests:
      storage: 100Gi  # 增加容量以支持多個服務
  storageClassName: azurefile  # Azure File - 支持 RWX

訪問模式說明

存儲類 訪問模式 適用場景 並發性
Azure Disk (default) ReadWriteOnce 單個服務專用 1 個 Pod
Azure File (azurefile) ReadWriteMany 多個服務共享 多個 Pod

選擇建議

  • 單個服務: 使用 default (Azure Disk) - 性能更好,成本更低
  • 多個服務: 使用 azurefile (Azure File) - 支持共享訪問

步驟 2: 修改 Deployment 配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: your-app-deployment
  namespace: your-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
      app: your-app-name
  template:
    metadata:
      labels:
        app: your-app-name
    spec:
      containers:
      - name: your-app-container
        image: your-registry/your-app:latest
        env:
          - name: JAVA_TOOL_OPTIONS
            value: >-
              -XX:MaxRAMPercentage=80
              -XX:MinRAMPercentage=80
              -XX:+HeapDumpOnOutOfMemoryError
              -XX:HeapDumpPath=/heapdumps/heapdump-$(date +%Y%m%d-%H%M%S).hprof
              -XX:+ExitOnOutOfMemoryError
              -XX:OnOutOfMemoryError=/app/log-oom-event.sh
        ports:
        - containerPort: 8080
          name: http
        volumeMounts:
        - name: heapdump-storage
          mountPath: /heapdumps
        lifecycle:
          postStart:
            exec:
              command:
              - /bin/sh
              - -c
              - |
                # 容器啟動時清理舊的 heap dump 文件
                echo "$(date): Starting cleanup of old heap dumps" >> /heapdumps/cleanup.log
                # 刪除 7 天前的文件
                find /heapdumps -name "*.hprof" -mtime +7 -exec rm -f {} \; -exec echo "$(date): Deleted {}" >> /heapdumps/cleanup.log \;
                # 記錄清理完成
                echo "$(date): Cleanup completed" >> /heapdumps/cleanup.log
          preStop:
            exec:
              command:
              - /bin/sh
              - -c
              - |
                # 容器停止前壓縮當前 heap dump 文件
                if ls /heapdumps/*.hprof 1> /dev/null 2>&1; then
                  echo "$(date): Compressing heap dump files before shutdown" >> /heapdumps/cleanup.log
                  gzip /heapdumps/*.hprof || true
                  echo "$(date): Compression completed" >> /heapdumps/cleanup.log
                fi
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
      volumes:
      - name: heapdump-storage
        persistentVolumeClaim:
          claimName: heapdump-storage

步驟 3: 創建 OOM 事件記錄腳本

在應用程式的 Docker 鏡像中創建以下腳本:

#!/bin/bash
# /app/log-oom-event.sh
# OOM 事件記錄腳本

OOM_TIMESTAMP=$(date +%Y%m%d-%H%M%S)
HEAPDUMP_FILE=$1

echo "=========================================" >> /heapdumps/oom-events.log
echo "OOM Event Detected: $OOM_TIMESTAMP" >> /heapdumps/oom-events.log
echo "Container: $(hostname)" >> /heapdumps/oom-events.log
echo "Heap Dump File: $HEAPDUMP_FILE" >> /heapdumps/oom-events.log

if [ -f "$HEAPDUMP_FILE" ]; then
    FILE_SIZE=$(du -h "$HEAPDUMP_FILE" | cut -f1)
    echo "File Size: $FILE_SIZE" >> /heapdumps/oom-events.log
    echo "File successfully created" >> /heapdumps/oom-events.log
else
    echo "ERROR: Heap dump file not found!" >> /heapdumps/oom-events.log
fi

echo "JVM Arguments: $JAVA_TOOL_OPTIONS" >> /heapdumps/oom-events.log
echo "=========================================" >> /heapdumps/oom-events.log

確保腳本有執行權限:

COPY log-oom-event.sh /app/log-oom-event.sh
RUN chmod +x /app/log-oom-event.sh

步驟 4: 部署和驗證

# 部署 PVC
kubectl apply -f pvc.yaml

# 部署應用程式
kubectl apply -f deployment.yaml

# 驗證 PVC 狀態
kubectl get pvc -n your-namespace

# 檢查 Pod 掛載
kubectl describe pod -l app=your-app-name -n your-namespace

# 測試文件持久化
kubectl exec -it <pod-name> -n your-namespace -- touch /heapdumps/test.txt
kubectl delete pod <pod-name> -n your-namespace
# 等待新 Pod 啟動
kubectl exec -it <new-pod-name> -n your-namespace -- ls -la /heapdumps/

文件管理策略

自動清理機制

  1. 啟動時清理: postStart 刪除 7 天前的文件
  2. 停止前壓縮: preStop 壓縮當前 heap dump 文件
  3. 定期清理: 可以添加 CronJob 進行額外清理

文件命名約定

heapdump-YYYYMMDD-HHMMSS.hprof
例如: heapdump-20240107-143052.hprof

存儲容量規劃

  • 基本容量: 50GB
  • 單個 Heap Dump 大小估算: 堆大小的 1.2-1.5 倍
  • 保留文件數量: 根據清理策略決定
  • 壓縮效果: gzip 可減少 70-80% 空間

監控和告警

存儲使用量監控

# 添加到 Deployment 的 annotations
annotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "9090"
  prometheus.io/path: "/actuator/prometheus"

告警規則示例

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: heapdump-storage-alerts
  namespace: monitoring
spec:
  groups:
  - name: heapdump-storage
    rules:
    - alert: HeapDumpStorageUsageHigh
      expr: (kubelet_volume_stats_available_bytes{persistentvolumeclaim="heapdump-storage"} / kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="heapdump-storage"}) < 0.1
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "Heap dump storage usage is above 90%"
        description: "Heap dump storage usage is {{ $value | humanizePercentage }}"

    - alert: HeapDumpOOMEvent
      expr: increase(heapdump_oom_events_total[1h]) > 0
      labels:
        severity: critical
      annotations:
        summary: "OOM event detected in application"
        description: "Application experienced OutOfMemoryError and generated heap dump"

故障排除

常見問題

1. PVC 處於 Pending 狀態

# 檢查存儲類
kubectl get storageclass

# 檢查 PVC 事件
kubectl describe pvc heapdump-storage -n your-namespace

2. 文件無法寫入

# 檢查掛載點權限
kubectl exec -it <pod-name> -n your-namespace -- ls -la /heapdumps

# 檢查 PVC 狀態
kubectl get pvc heapdump-storage -n your-namespace

3. 清理腳本失敗

# 查看清理日誌
kubectl exec -it <pod-name> -n your-namespace -- cat /heapdumps/cleanup.log

# 手動執行清理
kubectl exec -it <pod-name> -n your-namespace -- find /heapdumps -name "*.hprof" -mtime +7

性能影響

  • I/O 影響: Heap dump 寫入可能影響應用程式性能
  • 存儲成本: PVC 會產生雲存儲費用
  • 清理開銷: 定期清理會消耗 CPU 資源

安全考慮

數據保護

  1. 文件權限: 限制 heap dump 文件訪問權限
  2. 加密: 如需要,可以加密敏感的 heap dump 文件
  3. 定期清理: 防止敏感數據長期存儲

訪問控制

# PVC 訪問控制
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: heapdump-access
  namespace: your-namespace
rules:
- apiGroups: [""]
  resources: ["persistentvolumeclaims"]
  verbs: ["get", "list", "watch"]
  resourceNames: ["heapdump-storage"]

最佳實踐

1. 容量規劃

  • 根據應用程式堆大小估算 PVC 容量
  • 考慮壓縮後的文件大小
  • 設置適當的清理策略

2. 備份策略

  • 重要 heap dump 文件應及時備份
  • 考慮自動上傳到長期存儲
  • 設置文件保留期限

3. 監控告警

  • 監控存儲使用量
  • 設置 OOM 事件告警
  • 定期檢查清理腳本運行狀態

4. 成本優化

  • 使用合適的存儲類
  • 定期清理無用文件
  • 考慮存儲成本 vs 診斷價值

總結

通過 PVC 和 lifecycle 管理,可以有效解決 Java 應用程式 heap dump 文件的持久化存儲和自動管理問題。這種方案確保了診斷信息的可用性,同時避免了存儲成本過高和數據累積的問題。

關鍵組件: - PVC: 提供持久化存儲 - Volume Mount: 將存儲掛載到容器 - JVM 配置: 自動生成 heap dump - Lifecycle Hooks: 自動清理和管理文件 - 監控告警: 確保系統健康運行 C:\Users\paul_fang\PVC_HeapDump_Management.md