使用 `apport` 在 Ubuntu 容器内自动收集 core dump

解读

国内线上环境 90% 以上跑的是 Ubuntu 20.04/22.04 容器镜像,业务进程一旦崩溃,宿主机默认的 systemd-coredump 或 ABRT 对容器内 PID Namespace 不可见,导致 “容器里段错误,宿主机看不到 core”。面试官真正想确认的是:

  1. 你是否理解 容器 PID/Mount Namespace 隔离 导致 apport 传统机制失效;
  2. 能否在不破坏镜像轻量原则的前提下,让 apport 把 core 自动落到 宿主机可采集 的路径,并兼容国内私有云常用的 Jenkins+Harbor+ELK 闭环
  3. 是否知道 国内监管要求(等保 2.0)对“故障可追溯”的审计点,core 文件必须带容器标签、构建版本、时间戳。

知识点

  • apport 触发链:/proc/sys/kernel/core_pattern → apport → /var/crash/xxx → whoopsie(若启用);
  • 容器 Namespace 隔离:Mount Namespace 让 /var/crash 写在容器可写层,宿主机默认拿不到;
  • core_pattern 管道限制:内核要求管道程序必须在宿主机根文件系统可见,否则返回 EACCES;
  • Ubuntu 最小镜像(ubuntu:22.04)默认不带 apport,需显式安装 apport 与 apport-retrace;
  • 国内镜像源(清华、阿里、中科大)对 apport 的 GPG 签名与官方一致,可放心替换;
  • 安全加固:core 文件可能包含敏感内存,需 400 权限 + 宿主机临时加密盘
  • 等保审计:core 文件名必须包含 container_id、image_tag、git_commit 三段信息。

答案

  1. 构建阶段:在 Dockerfile 中 仅安装 apport 与调试符号,不启动服务,保持镜像最小。

    FROM ubuntu:22.04
    RUN sed -i 's@http://archive.ubuntu.com@http://mirrors.aliyun.com@g' /etc/apt/sources.list \
        && apt-get update \
        && apt-get install -y --no-install-recommends apport apport-retrace gdb \
        && apt-get clean && rm -rf /var/lib/apt/lists/*
    
  2. 运行阶段:
    a. 在宿主机准备 全局管道脚本 /opt/apport-wrapper.sh,内容如下:

    #!/bin/bash
    # 接收内核管道传来的 core
    CONTAINER_ID=$(cat /proc/$PPID/cgroup | grep -oP 'docker/+\K[0-9a-f]{64}' | head -n1)
    IMAGE_TAG=$(docker inspect -f '{{index .Config.Labels "org.opencontainers.image.version"}}' $CONTAINER_ID)
    GIT_COMMIT=$(docker inspect -f '{{index .Config.Labels "git.commit"}}' $CONTAINER_ID)
    TIMESTAMP=$(date +%Y%m%d%H%M%S)
    CORE_FILE="/cores/core_${CONTAINER_ID:0:12}_${IMAGE_TAG}_${GIT_COMMIT}_${TIMESTAMP}"
    cat > $CORE_FILE
    chmod 400 $CORE_FILE
    

    b. 宿主机 echo 管道路径:

    echo "|/opt/apport-wrapper.sh %p %s %c %d %P" > /proc/sys/kernel/core_pattern
    

    c. 启动容器时 挂载 /cores 并继承 core_pattern

    docker run -d --rm \
      --ulimit core=-1 \
      -v /cores:/cores \
      -v /proc/sys/kernel/core_pattern:/proc/sys/kernel/core_pattern:ro \
      --security-opt seccomp=unconfined \
      --name myapp \
      myimage:latest
    

    d. 容器内 确认 apport 被禁用,避免双重处理:

    echo "enabled=0" > /etc/default/apport
    
  3. 验证:容器内执行 kill -SIGSEGV $$,宿主机 /cores 立即出现 带容器 ID、镜像版本、commit 的 core 文件,满足国内审计要求。

拓展思考

  • 若集群使用 containerd + cri-o,需把管道脚本放到 kubelet 根路径 /var/lib/kubelet/core_pattern,并通过 Kubernetes hostPath + DaemonSet 统一维护,避免逐节点手工配置;
  • 多租户场景,可在管道脚本中调用 国密 SM4 加密 后再落盘,防止 core 被其他租户截获;
  • 结合 Jenkins 流水线,在构建时把 git.commitbuild.number 注入 OCI Label,core 文件名即天然携带 CI 追溯链,后续通过 ELK Filebeat 采集 /cores,即可在 Kibana 一键定位 “哪次构建、哪行代码” 引发崩溃;
  • 若业务对延迟敏感,可把 coredump_filter 置为 0x33 只 dump 匿名私有段,降低 IO 抖动,满足国内金融级 RT<5ms 要求。