docker 容器段错误:Segmentation fault 退出码 139 速查
更新时间:2026-10-24。FAQ 速查页,面向容器一启动就退出、
docker ps -a里看到Exited (139)的同学。docker 容器段错误的本质是容器内进程收到了 SIGSEGV 信号被杀——139 = 128 + 11,11 正是 SIGSEGV 的信号编号。Docker 只是如实转发了退出码,病灶在容器里的进程本身:程序自身有内存 bug、镜像架构不对被模拟执行、基础镜像的 libc 与二进制不兼容、内存超限引发连锁崩溃、缺调试符号拿不到栈、core dump 被容器配置拦了。本篇管「进程崩在容器里」;容器起不来的权限与端口问题看 docker 权限被拒速查 与 docker 容器端口连接被拒速查。
本文要回答的问题
Exited (139)里的 139 是怎么算出来的,与 137 有什么区别?- 为什么同一份二进制在宿主机跑得好好的,进容器就 segfault?
- 跨架构镜像(x86 镜像跑在 ARM 机器上)为什么经常段错误?
- 容器里崩了,core dump 文件去哪了、怎么收集?
- 没有源码只有二进制时,怎么在镜像内 gdb 抓栈?
一、报错原文与典型现象
段错误在容器层的典型形态(路径以 /proj/demo 占位):
$ docker ps -a
CONTAINER ID IMAGE STATUS NAMES
3f8a1c2e9b01 myapp:latest Exited (139) 2 minutes ago demo-app# 前台跑时直接看到 shell 转发的信号措辞:
$ docker run --rm myapp:latest
Segmentation fault (core dumped)# docker logs 里应用自己的崩溃日志,或内核日志里的记录:
$ dmesg | grep segfault
myapp[2841]: segfault at 0 ip 00007f2c1a3b4c21 sp 00007ffe5b2c8a90
error 4 in libc.so.6[7f2c1a344000+195000]读报错抓三件事:退出码 139 = SIGSEGV(137 = SIGKILL 多半是 OOM,别混淆);docker logs 与 dmesg 里的 segfault at <地址> ip <指令地址> 给出崩溃点;in libc.so.6 这类库名提示是「崩在库里」还是「崩在自己代码里」。三者齐了,排查方向就定了。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| Exited (139) 秒退 | 程序自身内存 bug(空指针/越界) | 宿主机同参数直接跑复测 | 修复代码;镜像内 gdb 抓栈 |
| 宿主机正常、容器内 segfault | 基础镜像 libc 与编译环境不兼容 | 对比编译机与镜像的 ldd 版本 | 编译与运行用同一基底镜像 |
| ARM 机器跑 x86 镜像随机崩 | qemu 模拟执行不完美 | docker inspect 看 architecture | 构建对应架构的原生镜像 |
| 崩在 malloc/free 附近 | 内存踩踏或双重释放 | valgrind 或 ASan 重编译 | 修内存 bug,别只换镜像 |
| Exited (137) 后偶发 139 | OOM 连锁:分配失败后访问空指针 | docker inspect 看 OOMKilled | 加内存限制/修分配失败处理 |
| 崩了但找不到 core 文件 | core dump 被 ulimit/内核参数拦 | ulimit -c 与 core_pattern | 放开 ulimit -c,挂 core 目录 |
| 拿到 core 但 bt 全是问号 | 二进制无调试符号 | file 看 stripped | 用带符号的镜像重跑收集 |
| 只在高并发下偶发 segfault | 数据竞争/线程安全问题 | 压测复现 + TSan | 修并发 bug,加锁或改无锁结构 |
三、排查决策图
下面这张图是标准排查路线,颜色按角色区分:红是报错入口与命中的根因,蓝是排查动作,绿是通过分支,橙是修复动作,紫是防复发沉淀。
读这张图抓住一个分水岭:同参数在宿主机直跑,崩不崩? 崩,说明是代码自身的内存 bug,容器只是现场;不崩,问题在「容器环境」这一侧——架构模拟、libc 版本、资源限制这三根轴挨个查。这个对照实验成本最低、信息量最大,永远先做。
四、逐条排查展开
先读懂退出码。 139 = 128 + 11(SIGSEGV),137 = 128 + 9(SIGKILL,常见于 OOM 与 docker stop 超时强杀)。docker inspect <容器> --format '{{.State.ExitCode}} {{.State.OOMKilled}}' 把两个字段一起看:OOMKilled 为 true 时是内存超限被杀,与段错误是两回事——但注意 OOM 连锁:malloc 返回 NULL 后代码没判空接着写,就会从「OOM」演变成「segfault」,表面 139、根子是内存不够。
别把「容器的锅」和「程序的锅」混为一谈。 很多团队的第一反应是换基础镜像、调 docker 参数,折腾一圈发现崩点纹丝不动——段错误九成是应用层的内存问题,容器配置层能解的只有架构、libc、资源这三类。所以本篇的排查顺序刻意把「宿主机对照实验」放在最前面:用最低成本先把两大阵营切开,再决定往哪边深挖。
程序自身的内存 bug 是头号病因。 空指针解引用、数组越界、释放后使用、栈溢出(深递归),这些在容器里与在宿主机上没有区别。处置不是改 docker 配置,而是抓现场修代码。最快的对照:把容器里的二进制与参数原样拿到宿主机跑,能复现就脱离了 docker 范畴,进入常规调试。
架构不符的模拟执行是「随机崩」的高发源。 在 Apple Silicon 或其他 ARM 机器上拉 x86_64 镜像,Docker 会用 qemu-user 模拟执行。大多数程序能跑,但 qemu 对JIT 编译器、向量指令、某些系统调用的模拟不完美,表现就是偶发 segfault 且位置飘忽。确认方法:
docker inspect myapp:latest --format '{{.Architecture}}'
uname -m # 两端不一致且没刻意跨架构,就是它在作祟处置:用 docker buildx build --platform linux/arm64 构建目标架构的原生镜像,别长期靠模拟执行跑生产负载。
编译环境与运行镜像的 libc 错位。 在本机(glibc 2.38)编译的二进制塞进老基底(glibc 2.31)的镜像里,或反过来把静态假设建立在 musl(alpine)与 glibc(debian)混用上,都可能在启动即崩——dmesg 里崩在 libc.so.6 或 ld-linux 附近就是线索。处置:编译与运行用同一基底镜像(多阶段构建天然解决),或静态链接把 libc 一起打进去。
core dump 的收集要过三道闸。 容器里默认 ulimit -c 常为 0,内核 core_pattern 由宿主机决定,容器文件系统还随容器删除而消失——三者任一不满足都拿不到 core:
docker run --rm --ulimit core=-1 \
-v /proj/demo/cores:/cores \
-e COREDUMP_DIR=/cores myapp:latest
# 宿主机侧确认:cat /proc/sys/kernel/core_pattern 指向可写目录或管道拿到 core 后,用与运行镜像一致的带符号镜像起调试容器挂进去 gdb:gdb /app/myapp /cores/core.2841,bt 拿栈。stripped 二进制 bt 全是问号时,重打一个不去符号的镜像重跑收集。
镜像内临时调试的两条路。 一是 docker exec 进还在跑的容器装 gdb(需要包管理器可用且容器有网);二是更稳的:从同一镜像起一个新容器挂 core 目录调试,不污染事故现场。alpine 这类极简镜像里没 gdb 时,apk add gdb 或改用 -debug 变体镜像。调试完记得把「core 收集命令」沉淀进运维手册,下次崩溃直接有现场。
五、规范做法与预防
- 编译运行同基底:多阶段构建,builder 与 runtime 用同一发行版同一版本族。
- 多架构原生构建:CI 里
buildx --platform linux/amd64,linux/arm64,不靠 qemu 跑生产。 - 内存分配必判空:C/C++ 代码评审把「malloc 返回值检查」列进清单,OOM 连锁 segfault 从源头断。
- core 收集预案进镜像模板:ulimit、core_pattern、挂载目录三件套写进部署文档,崩溃即有现场。
- 带符号镜像留档:每个发布版本留一份 unstripped 镜像,事后可复盘。
- 压测进上线门禁:只在高并发下崩的内存与并发 bug,压测阶段就逼出来。
这一篇在知识体系里的位置
本篇是 docker 排错速查簇的运行期崩溃篇,定位「进程在容器里被 SIGSEGV 杀死」:崩在缺库符号(undefined symbol)看 docker 容器缺动态库速查;崩在版本协商看 docker 版本不兼容速查;容器起不来的权限问题看 docker 权限被拒速查;构建/运行期路径类报错看 docker 挂载与 COPY 路径不存在速查;容器不崩但卡死无响应看 docker 容器卡死与健康检查挂起速查。139 退出码解读、宿主机对照实验、core dump 三闸,是一切容器崩溃排错的通用起点。
一句话总结
docker 容器段错误认准 Exited (139)(128+11=SIGSEGV,与 137 的 OOM 强杀分开);先做的对照实验是「同参数宿主机直跑」——也崩是代码内存 bug,不崩查容器侧三根轴:架构是否被 qemu 模拟(inspect 对 uname -m)、libc 版本是否与编译环境一致、是否 OOM 连锁判空缺失。拿不到现场时过 core dump 三闸:--ulimit core=-1、宿主机 core_pattern、挂卷落盘,再用同基底带符号镜像 gdb 拿栈。编译运行同基底、CI 多架构原生构建、core 收集预案进模板,这类崩溃即可防也可查。