ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker容器退出后日志不见了?从退出码到docker logs的排查指南

Docker容器退出后日志不见了?从退出码到docker logs的排查指南 1. 先搞清楚容器退出的状态再决定怎么查日志1.1 第一件事永远都是 docker ps -a遇到容器“消失”的情况很多人的第一反应是慌。其实容器只要不是被你手动 docker rm 删掉哪怕已经退出Exited它在宿主机上的“档案”都还保留着。第一步永远是先跑一条命令docker ps -a注意不是 docker ps。docker ps 只显示正在运行的容器已经退出的是看不到的所以你会误以为容器丢了。docker ps -a 会列出所有容器包括正在运行的、正常停止的、异常退出的一股脑全在里面。输出里的 STATUS 列是关键。常见的状态有Up 2 hours正常运行没啥问题。Exited (0) 3 minutes ago退出码 0表示是正常结束可能是任务跑完了也可能是你主动 stop 了。Exited (1) 5 seconds ago退出码 1应用自己抛错退出这是最常见的故障状态。Restarting (2) 1 second ago容器在反复重启一般是启动入口命令一直失败配合 restart 策略在无限重试。这个状态下先把 CONTAINER ID 或者 NAMES 记下来后面所有操作都要用这个唯一标识。有些朋友会顺手用 docker rename 给容器起个好记的名字这也是个好习惯后面看日志的时候不用对着那一长串 ID 发懵。1.2 用 docker inspect 看退出码比 ps 更详细docker ps -a 能看到的退出码只是一个数字但如果想了解更完整的退出信息我建议再多跑一条docker inspect 容器名或ID --format {{.State.ExitCode}} docker inspect 容器名或ID --format {{.State.Error}} docker inspect 容器名或ID --format {{.State.FinishedAt}}docker inspect 拉出来的是一个超大的 JSON直接人眼看会崩溃所以一般用 --format 精准取值。ExitCode 是退出码Error 会记录容器退出时的错误描述FinishedAt 能看到它是什么时候挂掉的。为什么要先看退出码而不是直接翻日志因为退出码能帮你快速缩小排查范围。比如退出码 137那你翻日志大概率看不到应用层报错因为这是被系统杀掉一般是 OOM日志里往往只有戛然而止的痕迹。退出码 0那就是正常退出你翻半天日志也翻不出“故障”。我把各环境常见的退出码整理了一下可以直接对照退出码含义常见原因日志排查重点0正常退出任务执行完毕、主动 stop不需要排查1一般性错误应用代码退出、启动失败看进程结束前最后几十行2misuse of shell builtins应用/脚本非法调用看 stderr 输出126命令存在但无法执行权限问题看启动命令相关日志127命令未找到镜像里没有该命令检查启动命令拼写130被 CtrlC 终止手动中断一般非故障137被 SIGKILL 杀掉OOM Killer 或 docker kill看系统日志、dmesg139段错误 SIGSEGV应用内存越界看崩溃前堆栈输出143被 SIGTERM 终止执行了 docker stop、平台发终止信号看优雅停机逻辑是否正常看退出码这个步骤熟练之后基本一眼就知道这个容器是“正常下班”还是“被抬走的”。2. 用 docker logs 查看已退出容器的日志最直接的办法2.1 容器退出后日志还在吗先说结论默认情况下只要容器不是用 --rm 启动的退出之后日志依然完整保留在宿主机上docker logs 依然可以读。很多朋友以为容器退出等同于日志清空其实不会。Docker 的日志机制是容器进程往标准输出stdout和标准错误stderr写的所有内容都会被宿主机上的 docker 守护进程接管然后按日志驱动写入到宿主机文件里。这个过程和你容器是跑着还是已经退出没关系只要你没有手动删容器日志文件就一直在。所以查看已退出容器日志的核心命令非常简单docker logs 容器名或ID不过这条命令有个坑它会一次性把所有日志全打到终端上哪怕容器已经跑了一个月日志有几万行。如果你的终端比较“脆”或者日志量巨大屏幕会直接爆掉甚至把终端卡死。所以我建议线上排查几乎别用不带参数的 docker logs至少加个 --tail 限制一下docker logs --tail 200 容器名或ID这条命令只看最后 200 行基本覆盖了容器退出前 90% 的线索。2.2 搭配时间戳和时间范围来过滤容器日志排查最烦的一个问题就是不显示时间戳根本不知道哪条日志是退出前几秒打出来的。所以在排查已退出容器时我强烈建议默认加上 --timestampsdocker logs --tail 200 --timestamps 容器名或ID每条日志前面会带完整的 RFC3339 格式时间戳比如 2025-06-01T14:23:11.582Z。看到时间戳之后再配合 --since 就能按时间段精确过滤。假设你的容器是在 6 月 1 日 14:30 退出的你想看它死前 10 分钟里到底发生了什么docker logs --since 2025-06-01T14:20:00 --until 2025-06-01T14:30:00 --timestamps 容器名或ID--since 和 --until 也支持相对时间比如 --since 10m 表示最近 10 分钟--until 1h 表示 1 小时前以前其实一般不会单独用 until。这种相对时间在快速排查时特别方便docker logs --since 20m --timestamps 容器名或ID这里有个小坑Docker 的 --since 参数相对时间还好说但如果用绝对时间默认解析是 UTC。你在国内日常用的是东八区写绝对时间时要换算好或者干脆用相对时间省得出错。我遇到太多人排查时因为时区对不上少看了 8 个小时的日志愣是没找到问题。2.3 把日志导出来慢慢看别在终端硬扛当容器日志量特别大的时候与其直接在终端里翻不如先把日志重定向到文件里再分析docker logs 容器名或ID /tmp/container.log 21加了 21 才能把标准错误一起导进文件。然后你想怎么折腾都行tail -n 200 /tmp/container.log grep -n ERROR\|Exception /tmp/container.log | tail -n 50 less /tmp/container.log如果日志本身是 JSON 格式比如很多 Java 应用配了 JSON 日志格式还可以用 jq 把它解析成表格方便筛选docker logs 容器名或ID 21 | jq -r select(.levelERROR) | .message这个操作在终端里跑和在导出文件里跑都可以但对于 GB 级日志docker logs 每次都要把整个 json-file 从头解析一遍效率并不高。日志特别大的时候我更推荐直接去宿主机上读日志文件方法往下看第三节。2.4 容器退出后想再进容器里查怎么办docker logs 只能看 stdout/stderr。如果应用把日志写到了容器内的文件里比如 /var/log/app/app.log而 docker logs 什么都看不到这时候怎么办容器已经退出docker exec 是进不去的。但有个小技巧先把容器重新启动起来再 exec 进去docker start 容器名或ID docker exec -it 容器名或ID bash启动命令可以临时覆盖方便卡在启动阶段就崩溃的应用docker run -it --rm --entrypoint sh 镜像名如果不想让容器真正跑业务只是想进去看看文件这个办法最实用。如果容器本身还在哪怕已经退出但你想把里面的日志复制出来分析docker cp 在容器停止状态下也能用docker cp 容器名或ID:/var/log/app/app.log /tmp/app.log这点知道的人不多但真到排查的时候能救命。3. 从宿主机日志文件入手底层原理与替代方案3.1 json-file 日志文件到底存在哪儿Docker 默认的日志驱动是 json-file也就是说容器打的 stdout/stderr 最终会被 docker 守护进程写进一个 JSON 格式的文件里一行一条日志而且这个文件天然支持 docker logs 读取。这个文件怎么找最靠谱的姿势是让 docker 自己告诉你docker inspect --format {{.LogPath}} 容器名或ID输出通常类似/var/lib/docker/containers/8f2a4c9e5d61a3e7b0c2d4f6a8b1c3e5d7f9a0b1c2d4e5f67890a1b2c3d4e5f6/config.v2.json不对LogPath 一般指向的是日志文件本身会是以 -json.log 结尾的路径/var/lib/docker/containers/8f2a4c9.../容器长ID-json.log拿到路径之后直接当成普通文件操作就行tail -n 200 /var/lib/docker/containers/8f2a4c9.../8f2a4c9...-json.log文件内容每一行是一个 JSON 对象结构类似{log:2025-06-01 14:20:11 INFO starting...,stream:stdout,time:2025-06-01T14:20:11.582Z}stream 字段区分是 stdout 还是 stderrtime 是写入时间log 就是原始日志行。直接读文件的优势在于不经过 docker 守护进程的过滤和解析对于超大日志文件的检索速度更快。另外即使某些极端情况下 docker logs 命令不可用比如 docker daemon 异常但文件还在你依然能拿到日志内容。3.2 日志驱动不是 json-file 怎么办不一定所有 Docker 环境都默认用 json-file有些运维会全局改掉日志驱动。用下面这条命令可以查看当前默认驱动docker info --format {{.LoggingDriver}}也可以查单个容器的实际驱动配置docker inspect --format {{.HostConfig.LogConfig.Type}} 容器名或ID不同驱动下“查看日志”的方式完全不同日志驱动docker logs 是否可用日志流向查看方式json-file可用宿主机 JSON 文件docker logs、直接读文件local可用宿主机二进制文件docker logsjournald新版可用systemd journaljournalctl -u docker、journalctl CONTAINER_IDxxxsyslog不可用宿主机 syslog/rsyslog/var/log/messages、/var/log/syslogfluentd不可用Fluentd 采集端去 Fluentd 日志中心gelf不可用Graylog 等去对应日志平台awslogs不可用CloudWatch Logs去 AWS 控制台如果你是老版本 Dockerdocker logs 对 journald 驱动不一定支持这时候直接用 journalctl 查journalctl CONTAINER_ID容器ID --since 10 min agojournalctl 里的 CONTAINER_ID 是全小写的容器完整 ID也就是 docker inspect 里的长 ID不是短 ID。另外如果你环境里日志驱动被改成了 syslog那 docker logs 铁定查不到任何东西得去系统日志文件里 grep 容器名。3.3 日志轮转配置别等磁盘被写爆才想起来容器日志不清理宿主机磁盘迟早被写满这是个经典事故。尤其是那些高并发的应用一天写几个 GB 日志很常见。等你发现磁盘满了容器可能早就因为写不进去日志而退出这又是一个“查已退出容器日志”的场景只不过这次排查的入口是磁盘告警。Docker 日志轮转是在 daemon.json 里全局配置的{ log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 } }这个配置就是日志文件单个最大 100MB最多保留 3 个文件超过上限就会滚动清理。配置修改后需要重启 docker daemonsystemctl restart docker注意daemon.json 里的配置只对之后新建的容器生效已经存在的容器不会自动套用新配置。所以要么新建容器要么修改已有容器的启动参数只能删了重建。单容器也可以在创建时指定docker run -d --log-opt max-size100m --log-opt max-file3 --name app your_image这里特别提醒日志轮转只影响 stdout/stderr 路线上的日志也就是 docker logs 能看到的那部分。容器内应用自己写文件的日志比如 Java 应用写 /var/log/app.log不归 docker 管要用应用自身的日志框架或者挂载目录 logrotate 去清理。两者别搞混了。4. 容器被删除了日志还能找回吗4.1 最坑的 --rm 参数有些朋友图省事启动容器时习惯加 --rmdocker run --rm your_image--rm 的含义是容器退出时自动把容器删掉连同它的文件系统、配置、日志全部清理干净。这个参数在临时调试时很香但放在生产环境就是个巨大的雷。一旦容器异常退出你连 docker ps -a 都看不到它docker logs 更是直接报错“No such container”。日志去哪儿了没了被我清理掉了。如果遇到报错Error: No such container: xxx恭喜你踩的就是这个坑。这时候唯一的办法去看能不能从外部日志系统如果你当初配了日志收集里捞出点东西。如果你至今什么都没配那就只能当一次深刻教训了。所以我的习惯是生产环境容器一律不加 --rm重要容器还要配合重启策略和日志挂载。加 --rm 省的那点磁盘空间远不及排查问题时损失的时间。4.2 日志目录被挂出来了吗容器删除后/var/lib/docker/containers/容器ID/ 目录通常也会被清理json-file 日志文件跟着没了。但有两种情况日志还能救回来第一种你把应用日志目录挂载到了宿主机上。比如启动时这么写的docker run -d -v /data/app/logs:/app/logs your_image应用写入 /app/logs 的文件实际落在宿主机 /data/app/logs 里。容器删了也不影响宿主机上的文件直接去宿主机目录翻就行。第二种容器还没删只是退出了。这种情况按第二部分讲的操作先 docker logs 导出或者 docker cp 把容器内日志文件拷出来然后再删容器。顺序千万别反。我一般建议上线前就把重要容器的日志目录挂载出来这个习惯在排查问题时受益无穷。出问题直接进目录 tail -f 或者 grep比 docker logs 顺手得多。4.3 Docker Desktop 用户怎么看到宿主机文件Windows / macOS 上跑 Docker Desktop容器的 /var/lib/docker 目录在它内置的虚拟机里不是直接暴露在 Windows 文件系统里的。所以你想直接去宿主机找 json-file 日志文件路径没那么直观。但好消息是docker logs、docker inspect 这些命令是经过虚拟化层封装的你在 Windows 终端里照样能跑所以日常排查 Docker Desktop 环境下的已退出容器日志直接用 docker logs 即可不需要折腾底层文件。如果你一定要访问虚拟机里的 /var/lib/docker比如日志驱动是 json-file 但 docker logs 因为某些原因坏了Docker Desktop 基于 WSL2 的环境可以这样进入wsl -d docker-desktop进去之后你就能看到 /var/lib/docker/containers 目录了。不过说实话我基本不会这么做太绕了能靠 docker logs 解决的问题不值得浪费这个时间。Windows 下的一个额外提醒cmd 和 PowerShell 默认的代码页不太兼容 UTF-8 日志中文乱码的情况很常见。看日志前可以先执行chcp 65001把代码页切到 UTF-8乱码能缓解很多。搜文件内容的时候尽量不要用 findstr它对 UTF-8 的中文支持也不行建议用 grepGit Bash 环境或者直接导出文件后用编辑器打开检索。5. 常见问题与排查技巧实录5.1 一个完整的排查过程从退出到定位拿一个我实际遇到过很多次的场景举例某 Java 服务容器退出码是 137。第一步发现容器退出docker ps -a | grep java-app输出类似d3f8a1b2c3e4 java-app java -jar app.jar Exited (137) 2 minutes ago第二步确认退出码语义137 对应 SIGKILL优先怀疑 OOMdocker inspect java-app --format {{.State.OOMKilled}}输出 true基本坐实是被 OOM Killer 杀掉的。第三步看容器死前日志确认有没有业务报错docker logs --since 30m --timestamps java-app 21 | tail -n 100日志里可能看不到业务异常只有正常输出到某一行就断了这跟 OOM 的特征吻合。第四步去宿主机系统日志确认内存情况dmesg | grep -i oom | tail -n 20或者更直接一点dmesg -T | grep -i killed process能明确看到内核把 java 进程杀了。到此定位流程结束不是应用代码问题是容器内存限制太小。排查完调大内存限制重新启动容器。这套流程的关键在于用退出码确定方向用 docker logs 找业务线索用系统日志找底层证据。以后你碰到类似问题也可以照这个组合拳来打。5.2 高频问题速查表现象可能原因建议排查方式docker logs 显示 No such container容器被 --rm 删除或手动 rm 掉了查日志收集平台恢复备份避免 --rm退出码正常 0但业务看起来有问题应用可能只是进程退出了业务状态没落盘看应用自身状态文件/数据库docker logs 一直没有任何输出应用日志写到了文件而不是 stdout/stderr进容器查文件、docker cp 导出容器退出码为 137OOM 或外部 killdocker inspect .State.OOMKilleddmesg日志文件太大docker logs 卡死json-file 文件增长失控直接读宿主机文件配置 max-size日志中文字符乱码Windows 终端编码问题chcp 65001导出用文本编辑器看restarting 状态看不到日志输出容器启动即崩一直没有 stdout结合 --since 10s 和 docker events 观察启动周期容器频繁重启但 docker logs 时间戳很乱日志时间戳默认 UTC加 --timestamps注意时区换算5.3 几个值得长期坚持的好习惯看完已退出容器日志这个动作本身很简单但要把异常容器快速排查明白背后拼的是平时习惯。我踩了几年坑整理几个很值得长期坚持的做法第一所有业务容器启动时都加 --name。名字是人类友好的日志定位、inspect、restart 全都靠名字别光记着一串随机 ID。第二重要容器一律加日志轮转参数不管 Docker 默认有没有自己显式写一遍docker run -d --log-opt max-size50m --log-opt max-file5 --name app your_image这行参数不写短时间看不出来问题等出问题的时候大概率是磁盘警报先响起。第三应用尽量把日志打到 stdout/stderr而不是只写文件。因为 stdout/stderr 是 Docker 生态的统一语言docker logs、journald、日志采集器全都在这条线上收集。应用写文件当然也有价值但请务必两条腿走路别只走一条。第四关键时刻要养成看时间戳的习惯。别用无时间戳的 docker logs 发呆看半天泡杯茶的功夫一回头发现看的日志是两小时前的。加 --timestamps 之后再配合 --since 过滤效率完全两个级别。第五排查大门一旦打开要按顺序走docker ps -a 找容器docker inspect 看退出码docker logs --since 看关键时间段的日志。上来就 docker logs 一把梭很容易被海量日志淹没在噪声里浪费时间。做容器排查这些年我最大的体会是日志是容器留下的唯一“遗言”退出码则是“遗言”的摘要。两者结合着看大部分容器死亡原因都能在几分钟内定位。希望这篇内容能帮你少走点弯路下次再遇到已退出的容器冷静打开 docker logs问题往往就在那最后几十行日志里等着你。
返回列表