使用core dump分析实例中的程序异常

Core dump是指在程序运行过程中发生异常终止或崩溃时,操作系统将程序的内存内容转储到一个特殊的文件中,以便于后续的调试和问题分析(如通过gdb调试工具查找程序崩溃产生的原因)。本文介绍如何为ACS Pod实例开启core dump能力,以便在容器异常终止时可以查看分析core dump生成的文件,从而定位问题原因,修复程序异常。

原理介绍

在Linux中,如果程序突然异常终止或者崩溃,操作系统会将程序当时的内存状态记录下来并产生内存状态文件,产生完成后会将业务进程同步退出,这种行为就叫做core dump。Core dump过程中产生的内存状态文件为core dump file,通常称为core文件。您可以通过gdb等调试工具查看和分析core文件,找出引发程序终止的根本原因。

Linux中支持core dump的Signal如下图所示,其中Action为Core表示该Signal默认会产生core文件。更多信息,请参见core dump file

使用方式

默认情况下,ACS集群中的Pod实例不会开启core dump。因为反复的core dump行为生成大量的core文件会导致磁盘占用过多而影响业务不可用,因此推荐您使用挂载远程共享存储(阿里云OSS或NAS)的方式来存储生产的core文件。通过自定义设置core文件的保存路径,既能够确保获得完整的core文件,同时也可以避免因CrashBackOff事件反复产生core文件,导致容器rootfs层存储容量不足的情况。

当出现core文件过大的情况时,您也可以使用临时容器的方案。在发生core dump事件后,core文件会被保存在容器的rootfs层,您可以直接登录到临时容器中,对core文件进行分析。

您可以通过在Pod的annotations中配置alibabacloud.com/core-pattern: core-path/core-pattern来设置core文件的保存路径并开启core dump。同时将此路径目录配置为共享存储卷,方便后续收集core文件并进行异常分析。您可以按需自定义core-pattern文件名的输出格式,例如,在core-%E-%p-%t中:

  • %E:表示引起崩溃的可执行文件的路径(Executable Path)。

  • %p:表示崩溃时进程的进程ID(Process ID)。

  • %t:表示崩溃时的时间戳(Timestamp)。

更多core-pattern文件名格式,请参见Man page of core

apiVersion: v1
kind: Pod
metadata:
  annotations:
    alibabacloud.com/core-pattern: "/data/dump-a/core-%E-%p-%t"
...
说明

以下操作即可以配置ACS集群的kubeconfig使用,也可以在cloudshell中操作。若使用kubeconfig陪配置请确保您本地已经安装kubectl,并且配置准确的kubeconfig文件。具体操作,请参见获取集群KubeConfig并通过kubectl工具连接集群

挂载远程共享存储

挂载NAS存储core文件

基于NAS作为共享存储卷方式采集容器Crash后内核产生的core文件。

  1. 创建NAS存储文件系统和挂载点。具体操作,请参见创建NAS文件系统和挂载点

  2. 使用以下YAML内容,创建名为coredump-nas-volume-test的Deployment。具体操作,请参见创建无状态工作负载Deployment。在创建模板中请替换volumes.volumeAttributes.server的值为您实际的NAS server地址。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: coredump-nas-volume-test
      labels:
        app: test
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          name: nginx-test
          labels:
            app: nginx
          annotations:
            alibabacloud.com/core-pattern: "/data/dump-a/core-%E-%p-%t" # 设置core文件保存路径
        spec:
          containers:
          - name: nginx
            image: registry.cn-hangzhou.aliyuncs.com/acs-sample/nginx:latest
            volumeMounts:
              - name: nas-volume
                mountPath: /data/dump-a/
          volumes:     # 挂载NAS 实现共享卷
            - name: nas-volume
              csi:
                driver: nasplugin.csi.alibabacloud.com
                fsType: nas
                volumeAttributes:
                  server: "0389a***-nh7m.cn-shanghai.extreme.nas.aliyuncs.com"
                  path: "/"
                  vers: "3"
                  options: "nolock,tcp,noresvport"

    上述Pod在触发core dump事件后,core文件会被存放在远端存储NAS目录上

  3. 执行以下命令,查看存储卷挂载是否生效,以便在coredump行为发生后,能在远端共享存储系统上获得并查看core文件。

    kubectl exec -it deploy/coredump-nas-volume-test -- sh -c 'df -h | grep aliyun'

    预期输出:

    0389a***-nh7m.cn-shanghai.extreme.nas.aliyuncs.com:/   10P     0   10P   0% /data/dump-a

    可以看到挂载已经生效。

挂载OSS存储core文件

基于OSS作为共享存储卷方式采集容器Crash后内核产生的core文件。

  1. 创建OSS Bucket。具体操作,请参见静态挂载OSS存储卷

  2. 使用以下YAML内容,创建名为coredump-oss-volume-test的Deployment。具体操作,请参见创建无状态工作负载Deployment。例如,在创建的模板中请替换.Spec.csi.volumeAttributes中您真实的OSS的Endpoint地址和密钥等配置信息。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: coredump-oss-volume-test
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
          annotations:
            alibabacloud.com/core-pattern: "/data/dump-a/core-%E-%p-%t"    # 设置core文件保存路径
        spec:
          containers:
          - name: nginx
            image: registry.cn-hangzhou.aliyuncs.com/acs-sample/nginx:latest
            volumeMounts:
              - name: oss-volume
                mountPath: /data/dump-a/
          volumes:
            - name: oss-volume
              persistentVolumeClaim:
                claimName: oss-pvc
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: oss-pvc
    spec:
      storageClassName: test # 这里的 storageClass name 只做 binding mapping 作用, 无需实际资源
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 50Gi
      selector:
        matchLabels:
          alicloud-pvname: oss-csi-pv
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: oss-csi-pv
      labels:
        alicloud-pvname: oss-csi-pv
    spec:
      storageClassName: test # 这里的 storageClass name 只做 binding mapping 作用, 无需实际资源
      capacity:
        storage: 50Gi
      accessModes:
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain
      csi:
        driver: ossplugin.csi.alibabacloud.com
        volumeHandle: oss-csi-pv
        volumeAttributes:
          bucket: "oss-test"
          url: "oss-cn-hangzhou-internal.aliyuncs.com"
          otherOpts: "-o max_stat_cache_size=0 -o allow_other"
          akId: "<your AccessKey ID>"
          akSecret: "<your AccessKey Secret>"

    上述Pod在触发core dump事件后,core文件会被存放在远端存储OSS目录上。

  3. 执行以下命令,查看存储卷挂载是否生效,以便在coredump行为发生后,能在远端共享存储系统上获得并查看core文件。

    kubectl exec -it deploy/coredump-oss-volume-test -- sh -c 'df -h | grep s3fs'

    预期输出:

    s3fs             16E     0   16E   0% /data/dump-a

    可以看到挂载已经生效。

注入临时容器

通过注入一个临时容器,将core文件的路径以emptyDir volume的形式挂载到容器上,从而获取core文件。

  1. 使用以下YAML内容,创建名为coredump-emptydir-volume-test的Deployment。具体操作,请参见创建无状态工作负载Deployment

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: coredump-emptydir-volume-test
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
          annotations:
            alibabacloud.com/core-pattern: "/data/dump-a/core-%E-%p-%t"    # 设置core文件保存路径
        spec:
          containers:
          - name: nginx
            image: registry.cn-hangzhou.aliyuncs.com/acs-sample/nginx:latest
            volumeMounts:
              - name: emptydir-volume
                mountPath: /data/dump-a/
          volumes:
            - name: emptydir-volume
              emptyDir: {}

    创建一个Deployment,同时挂载名为emptydir-volume的存储卷。完成设置后可以登录到临时容器的共享挂载点查看core文件。

    重要

    当前kubectl debug的社区版本不支持向Pod中注入临时容器的同时配置挂载,因此接下来的步骤将采用kubectl proxy配置本地代理环境的方式进行验证。同时,您需要在本地打开两个相同的终端窗口并且保持配置代理的窗口不被关闭。

  2. 在一个终端窗口执行以下命令,启动本地到集群的代理服务。

    kubectl proxy 

    预期输出:

    Starting to serve on 127.0.0.1:8001
    说明

    您可以在执行kubectl proxy 命令时,通过--port参数来指定端口。更多信息,请参见kubectl proxy命令

  3. 打开另一个终端窗口并执行以下命令,完成对于某个指定的Pod上注入临时容器的操作。命令行中的coredump-emptydir-volume-test-xxxxxtarget-container等参数可以根据实际情况进行修改。

    curl -k http://127.0.0.1:8001/api/v1/namespaces/default/pods/coredump-emptydir-volume-test-xxxxx/ephemeralcontainers -X PATCH  -H 'Content-Type: application/strategic-merge-patch+json' -d '{
      "spec": {
        "ephemeralContainers": [
          {
            "name": "debugger-container-name",
            "command": [
              "/bin/sh",
              "-c",
              "sleep 3600"
            ],
            "image": "registry.cn-hangzhou.aliyuncs.com/acs-sample/nginx:latest",
            "stdin": true,
            "tty": true,
            "targetContainerName": "target-container", # 当Pod内存在多个容器的时候可选指定容器
            "volumeMounts": [
              {
                "name": "emptydir-volume",
                "mountPath": "/data/dump-a/"
              }
            ]
          }
        ]
      }
    }'

    部分参数说明如下。

    参数

    说明

    ...namespaces/${NAMESPACE}...

    注入临时容器的命名空间。

    ...pods/${POD_NAME}...

    注入临时容器的Pod名。

    spec: ${SPEC_DETAIL}

    具体临时容器的Spec内容。

    重要

    建议您逐步按需替换,并通过一些额外的工具校验Spec字段内容是否为合法的JSON格式。

    Spec.ephemeralContainers.name

    指定临时容器的名字,多次执行临时容器注入命令时需要保证临时容器的名字不重复。

    Spec.ephemeralContainers.command

    临时容器的启动命令,使用自定义镜像时按需配置。

    Spec.ephemeralContainers.targetContainerName

    当前Pod存在多个容器的时候可选择指定具体的容器名进行注入。

    Spec.ephemeralContainers.volumeMounts

    临时容器挂载点目录,与Pod的core-pattern值保持一致。

  4. 临时容器运行成功后,执行以下命令登录到临时容器中。

    kubectl exec -it -n default coredump-emptydir-volume-test-xxxxx -c debugger-container-name sh
  5. 在临时容器中,执行以下命令,确认可以访问到已挂载的目录。

    cd /data/dump-a && pwd

    预期输出:

    /data/dump-a