通过CNFS自动收集异常退出的JVM转储文件

当您的业务是使用Java开发,且设置的JVM堆空间过小时,程序会发生OOM(Out Of Memory)的问题。此时您可以使用CNFS(Container Network File System)作为记录日志的载体,挂载到容器内相应目录中,当JVM发生OOM时,CNFS可以将日志记录到相应的目录里。本文介绍如何使用CNFS自动收集异常退出的JVM转储文件。

前提条件

注意事项

  • Java设置的最大Heap值(Xmx)应小于Pod MemoryLimit值,防止JVM未发生OOM,但Pod发生OOM的情况。

  • 在使用Java转储时,建议创建个新的CNFS,将业务使用的CNFSJava转储的CNFS分开,防止.hprof文件过大,转储时占用大量业务资源,影响业务。

  • 示例中的镜像 docker.io/filebrowser/filebrowser:v2.18.0 可能因网络访问限制会拉取失败,您需要通过订阅海外源镜像同步至您的 ACR 企业版实例。具体配置如下:

    • 制品来源:Docker Hub

    • 源端仓库坐标:filebrowser/filebrowser

    • 订阅策略:v2.18.0

      完成镜像订阅后,您需配置 ACR 企业版实例 与 ACK集群 的免密拉取策略,详情请参见ACR免密拉取镜像

操作步骤

  1. 使用registry.cn-hangzhou.aliyuncs.com/acs1/java-oom-test:v1.0示例镜像作为模拟OOMJava程序,用于触发JVMOOM。

  2. 使用以下示例,创建一个名称为java-applicationDeployment。

    本示例在启动Java程序Mycode时,设置申请的堆大小为80 MB,堆转储的目录为/mnt/oom/logs。当JVM的堆大小不满足时,捕获HeapDumpOnOutOfMemoryError错误。

    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:                               #定义两个键值:POD_NAMEmetadata.name,POD_NAMESPACEmetadata.namespace。
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
            args:
            - java                            #执行命令。
            - -Xms80m                         #堆内存的最小Heap值。
            - -Xmx80m                         #堆内存的最大Heap值。
            - -XX:HeapDumpPath=/mnt/oom/logs  #发生OOM时,堆内存转储的路径。
            - -XX:+HeapDumpOnOutOfMemoryError #捕获堆发生OOM的错误。
            - Mycode                          #执行程序。
            volumeMounts:
            - name: java-oom-pv
              mountPath: "/mnt/oom/logs"      #容器内部使用/mnt/oom/logs做为挂载目录。
              subPathExpr: $(POD_NAMESPACE).$(POD_NAME)   #使用$(POD_NAMESPACE).$(POD_NAME)作为创建出子目录,将OOM转储文件生成到子目录中。
          volumes:
          - name: java-oom-pv
            persistentVolumeClaim:
              claimName: cnfs-nas-pvc         #使用CNFSPVC,名称为cnfs-nas-pvc。
    ---
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: cnfs-nas-pvc
    spec:
      accessModes:
        - ReadWriteMany
      storageClassName: alibabacloud-cnfs-nas
      resources:
        requests:
          storage: 70Gi # 如果打开目录限额功能,则storage字段会生效,动态创建目录写入数据量最大为70 GiB。
    ---          
    EOF
  3. 通过容器服务管理控制台的事件中心,查看到该Pod发生了Back-off restarting的告警事件,说明java-application应用发生了OOM。

    1. 登录容器服务管理控制台,在左侧导航栏选择集群列表

    2. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择运维管理 > 事件中心

    3. 查看对应事件。

      3e0492283c067026c9cfd348a898ecb1

  4. 由于NAS目前没有浏览、上传、下载文件的功能,您可以使用File Browser作为Web端的访问工具。首先将NAS的挂载点挂载到File BrowserrootDir上,然后通过创建Service,将File Browser的容器端口映射,再通过浏览器访问存放在NAS上的文件。

    1. 使用以下模板,创建File BrowserDeploymentFile Browser需要使用的ConfigMap,默认开启80端口。

      cat << EOF | kubectl apply -f -
      apiVersion: v1
      data:
        .filebrowser.json: |
          {
            "port": 80,
            "address": "0.0.0.0"
          }
      kind: ConfigMap
      metadata:
        labels:
          app.kubernetes.io/instance: filebrowser
          app.kubernetes.io/name: filebrowser
        name: filebrowser
        namespace: default
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          app.kubernetes.io/instance: filebrowser
          app.kubernetes.io/name: filebrowser
        name: filebrowser
        namespace: default
      spec:
        progressDeadlineSeconds: 600
        replicas: 1
        revisionHistoryLimit: 10
        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:
            - image:  XXXX-registry-vpc.cn-hangzhou.cr.aliyuncs.com/test/test:v2.18.0  #示例镜像docker.io/filebrowser/filebrowser:v2.18.0可能因网络访问限制会拉取失败,请参见注意事项。
              imagePullPolicy: IfNotPresent
              name: filebrowser
              ports:
              - containerPort: 80
                name: http
                protocol: TCP
              resources: {}
              securityContext: {}
              terminationMessagePath: /dev/termination-log
              terminationMessagePolicy: File
              volumeMounts:
              - mountPath: /.filebrowser.json
                name: config
                subPath: .filebrowser.json
              - mountPath: /db
                name: rootdir
              - mountPath: /rootdir
                name: rootdir
            dnsPolicy: ClusterFirst
            restartPolicy: Always
            schedulerName: default-scheduler
            securityContext: {}
            terminationGracePeriodSeconds: 30
            volumes:
            - configMap:
                defaultMode: 420
                name: filebrowser
              name: config
            - name: rootdir
              persistentVolumeClaim:
                claimName: cnfs-nas-pvc
      EOF

      预期输出:

      configmap/filebrowser unchanged
      deployment.apps/filebrowser configured
    2. 登录容器服务管理控制台,在左侧导航栏选择集群列表

    3. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择网络 > 服务

    4. 服务页面选择default命名空间,单击创建,然后根据以下参数配置。

      参数

      示例说明

      服务名称

      filebrowser

      服务类型

      负载均衡(LoadBalancer)

      • 负载均衡类型传统型负载均衡 CLB

      • 选择新建资源,单击新建CLB资源下拉列,将访问方式配置为公网访问计费方式配置为按量计费

      关于CLB的计费说明,请参见CLB计费概述

      服务关联

      选择+引用工作负载标签

      • 资源类型:Deployments

      • 资源列表:filebrowser

      端口映射

      • 服务端口:8080

      • 容器端口:80

      • 协议:TCP

    5. 在对话框中,选择负载均衡(LoadBalancer)作为服务类型。选择新建资源,将访问方式配置为公网访问计费方式配置为按量计费(PayByCLCU),按照页面提示提交配置的修改。

      关于CLB的计费说明,请参见CLB计费概述
    6. 打开浏览器,在地址栏输入端点地址:8080,可以看到File Browser的登录界面,输入默认账号(admin)和密码(admin),进入到容器内部。

      20fe4dcde1759ebc64cbe0b1bb3168da

    7. 由于File Browser将名称为cnfs-nas-pvcPVC挂载到rootDir下,双击rootDir进入到NAS挂载点。

      image

执行结果

File Browser中可以看到名称为default.java-application-76d8cd95b7-prrl2的目录,此目录是java-applicationsubPathExpr: $(POD_NAMESPACE).$(POD_NAME)作为规则生成的目录。

image

然后进入此目录,可以看到目录中的转储文件java_pid1.hprof。如果您需要定位到程序发生OOM的代码行数,可以将java_pid1.hprof下载到本地,通过MAT(Eclipse Memory Analyzer Tools)进一步分析JVM堆栈信息。

lQLPJxMFSGyoLcnNAqTNB2awoAbbe3-kh8AIV2X4pMttAA_1894_676