Capture heap dumps .hprof que persistem entre reinicializações de pods por OOM ao montar um volume NAS com suporte a CNFS.
Pré-requisitos
Verifique os seguintes itens:
Um sistema de arquivos NAS gerenciado por CNFS provisionado.
Uma instância do Enterprise Edition do Container Registry criada.
Observações de uso
Defina o heap máximo da JVM abaixo do limite de memória do pod. Mantenha o parâmetro -Xmx inferior ao limite de memória do pod. Se o heap da JVM atingir o limite do pod primeiro, o OOM killer do Linux encerrará o pod antes que a JVM consiga gravar o dump.
Utilize um CNFS dedicado para heap dumps. Um único evento de OOM pode gerar um arquivo .hprof de vários gigabytes, capaz de esgotar a cota compartilhada e interromper as operações.
Sincronize a imagem do File Browser antes de começar. A imagem docker.io/filebrowser/filebrowser:v2.18.0 pode falhar no pull devido a restrições de rede. Sincronize-a com sua instância do ACR Enterprise Edition assinando imagens de fora da China:
|
Campo |
Valor |
|
Artifact source |
Docker Hub |
|
Source repository coordinates |
|
|
Subscription policy |
|
Após a sincronização, configure o pull de imagens sem senha entre a instância do ACR Enterprise Edition e seu cluster ACK.
Implante a aplicação Java
Implante um Deployment Java com armazenamento suportado por CNFS como destino dos heap dumps. A imagem de exemplo registry.cn-hangzhou.aliyuncs.com/acs1/java-oom-test:v1.0 executa Mycode com um limite de heap de 80 MiB.
O Deployment utiliza subPathExpr: $(POD_NAMESPACE).$(POD_NAME) para criar subdiretórios individuais por pod dentro do volume NAS compartilhado. Isso evita que reinicializações de pods sobrescrevam os dumps uns dos outros.
O camposubPathExprusa parênteses —$(POD_NAME)— e não chaves. Os valores provêm das variáveis de ambiente da Downward APIPOD_NAMEePOD_NAMESPACE.
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-application
spec:
selector:
matchLabels:
app: java-application
template:
metadata:
labels:
app: java-application
spec:
containers:
- name: java-application
image: registry.cn-hangzhou.aliyuncs.com/acs1/java-oom-test:v1.0
imagePullPolicy: Always
env:
- name: POD_NAME # Inject pod name via Downward API
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE # Inject pod namespace via Downward API
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
args:
- java
- -Xms80m # Minimum heap size
- -Xmx80m # Maximum heap size (keep below pod memory limit)
- -XX:HeapDumpPath=/mnt/oom/logs # Write heap dumps to the CNFS-backed mount
- -XX:+HeapDumpOnOutOfMemoryError # Trigger heap dump on OOM
- Mycode
volumeMounts:
- name: java-oom-pv
mountPath: "/mnt/oom/logs"
subPathExpr: $(POD_NAMESPACE).$(POD_NAME) # Round brackets, not curly brackets
volumes:
- name: java-oom-pv
persistentVolumeClaim:
claimName: cnfs-nas-pvc
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: cnfs-nas-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: alibabacloud-cnfs-nas
resources:
requests:
storage: 70Gi # If directory quota is enabled, limits the subdirectory to 70 GiB
---
EOF
Verifique o evento de OOM
Após o início do Deployment, o Mycode aloca memória até que a JVM exaura o heap de 80 MiB e dispare um OOM. O pod é reiniciado e o ACK registra um alerta de back-off restarting no Event Center.
Para confirmar a ocorrência do OOM:
Faça login no console do ACK. No painel de navegação à esquerda, clique em Clusters.
Localize seu cluster e clique no nome dele. No painel de navegação à esquerda, escolha Operations > Event Center.
-
Verifique na lista de eventos se há um alerta de
back-off restartingno podjava-application.
Navegue pelos arquivos de heap dump
Implante o File Browser com o mesmo PVC do CNFS montado em rootDir para navegar e baixe os heap dumps.
Implante o File Browser
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: filebrowser
namespace: default
labels:
app.kubernetes.io/instance: filebrowser
app.kubernetes.io/name: filebrowser
data:
.filebrowser.json: |
{
"port": 80,
"address": "0.0.0.0"
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: filebrowser
namespace: default
labels:
app.kubernetes.io/instance: filebrowser
app.kubernetes.io/name: filebrowser
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: filebrowser
app.kubernetes.io/name: filebrowser
template:
metadata:
labels:
app.kubernetes.io/instance: filebrowser
app.kubernetes.io/name: filebrowser
spec:
containers:
- name: filebrowser
# Replace with your ACR image address after syncing filebrowser/filebrowser:v2.18.0
image: XXXX-registry-vpc.cn-hangzhou.cr.aliyuncs.com/test/test:v2.18.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: http
protocol: TCP
volumeMounts:
- mountPath: /.filebrowser.json
name: config
subPath: .filebrowser.json
- mountPath: /db
name: rootdir
- mountPath: /rootdir
name: rootdir
volumes:
- name: config
configMap:
name: filebrowser
defaultMode: 420
- name: rootdir
persistentVolumeClaim:
claimName: cnfs-nas-pvc # Same PVC as the java-application Deployment
EOF
Saída esperada:
configmap/filebrowser unchanged
deployment.apps/filebrowser configured
Crie um Service
Faça login no console do ACK. No painel de navegação à esquerda, clique em Clusters.
Localize seu cluster e clique no nome dele. No painel de navegação à esquerda, escolha Network > Services.
-
Na página Services, selecione o namespace
defaulte clique em Create. Configure os seguintes parâmetros:Consulte a visão geral de faturamento do NLB .
Parâmetro
Valor
Name
filebrowserService type
SLB > SLB type: CLB. Selecione Create Resource e defina Access method como Public Access.
Backend
Clique em +Reference Workload Label. Defina Resource type como
Deploymentse Resources comofilebrowser.Port mapping
Service port:
8080, Container port:80, Protocol:TCP Envie a configuração.
Acesse o File Browser
Copie o endereço do endpoint na página de Services.
Abra
<endpoint-address>:8080no navegador. A página de login do File Browser será exibida.-
Faça login com as credenciais padrão: nome de usuário
admin, senhaadmin.
-
Clique duas vezes em rootdir para acessar o ponto de montagem do NAS.

Resultado
Dentro de rootdir, o diretório de dump de cada pod segue a nomenclatura definida pela regra subPathExpr: $(POD_NAMESPACE).$(POD_NAME) — por exemplo, default.java-application-76d8cd95b7-prrl2.

Abra o diretório para localizar o arquivo java_pid1.hprof. Baixe-o e analise-o com o Eclipse Memory Analyzer (MAT) para identificar o código que causou o OOM.

Como funciona
Três mecanismos atuam em conjunto para persistir os heap dumps entre reinicializações de pods:
PVC CNFS com
ReadWriteMany: O PVC suportado por NAS é montado tanto nos pods dajava-applicationquanto nos do File Browser. O NAS persiste os arquivos independentemente do ciclo de vida do pod, garantindo que os dumps sobrevivam a falhas.subPathExprcom Downward API: Cada pod monta seu próprio subdiretório (<namespace>.<pod-name>) em vez da raiz do NAS. Isso evita que reinicializações sobrescrevam dumps e facilita o rastreamento dos arquivos até seus respectivos pods.-XX:+HeapDumpOnOutOfMemoryError: A JVM grava o estado do heap no caminho definido por-XX:HeapDumpPathantes de sair devido a OOM. Como esse caminho reside no NAS, o arquivo permanece disponível após o encerramento do pod.