使用 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/
文件管理策略
自動清理機制
- 啟動時清理:
postStart刪除 7 天前的文件 - 停止前壓縮:
preStop壓縮當前 heap dump 文件 - 定期清理: 可以添加 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 資源
安全考慮
數據保護
- 文件權限: 限制 heap dump 文件訪問權限
- 加密: 如需要,可以加密敏感的 heap dump 文件
- 定期清理: 防止敏感數據長期存儲
訪問控制
# 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: 自動清理和管理文件
- 監控告警: 確保系統健康運行