入口 · 先确定对象还在不在
先分状态
先运行 docker ps -a,不要一上来就 exec。-a 会列出停止的容器。下文用 app 代表待排查的容器名,执行时换成你自己的名字。[1]
第二条命令读取状态。--format 用模板只取所需字段;{{.State.Status}} 表示读取 State 下的 Status。不加它会输出完整的 JSON(结构化的键值记录)。[3]
| 看见什么 | 先看什么证据 | 下一步 |
|---|---|---|
| 没有目标容器 | 原始 docker run 报错;名字与连接目标 | 可能创建前失败,或已被 --rm 删除 |
created | .State.Error;原始启动错误 | 对象已创建,主进程可能尚未启动成功 |
exited | logs;退出码与结束时间 | 按下一节合读证据 |
running | 端口映射;应用日志;实际请求 | 沿访问路径检查,再用 exec |
拉取镜像失败时,容器可能根本没创建;端口被占用时,错误可能发生在主进程启动前。logs 没内容不能排除这些问题。若提示连不上 Docker 后台服务或无权限,先解决客户端到服务端的连接;可复习第一次 docker run 的请求链路。
docker run 报错了,docker ps -a 里也没有目标容器。此时直接执行 docker exec app sh,能帮你找到应用错误吗?写出下一步。
不能。exec 需要一个正在运行的目标。先保留原始启动报错,确认容器名、连接目标,以及是否用了 --rm;没有容器对象就没有它的日志和状态可读。
退出 · 把程序叙述和运行记录拼起来
退出查两处
logs 告诉你程序说了什么;inspect 告诉你 Docker 记录了什么。容器停止后,只要对象还在,仍可查这两处。先保存证据,再考虑重启。[2] [8]
--tail 50 只取末尾 50 行;--timestamps 加时间戳。--since 10m 筛最近 10 分钟,-f 持续跟随新日志;按 Ctrl + C 结束跟随,不会因此停止容器。json 把 State 这组字段转成 JSON。[2] [3]
- 确认对象。先区分没有创建、已经停止、仍在运行。名字相同也要确认查的是本次实例。
- 限定日志。先取末尾和故障附近的记录,避免旧错误淹没当前问题。先猜:日志里有“配置缺失”,还要核对哪类信息?
- 摘出线索。记录应用自己的错误及发生时间。日志中的报错是线索,还不能代表 Docker 的退出记录。
- 核对状态。读取
ExitCode、Error、OOMKilled。先猜:上次重启前的错误,能直接解释这次退出吗? - 对齐时间。用
StartedAt和FinishedAt对照日志,避免把历史错误当成本次原因。 - 提出可验证的判断。例如“启动参数漏了配置项”。保留证据后,只改这一项并重新观察。
ExitCode:主进程结束时的退出码。0 通常表示成功完成;长期服务正常结束也会让容器停止。非零含义要结合应用说明。Error:Docker 记录的运行时错误信息。应用自己报错退出时,这个字段仍可能为空。OOMKilled:自最近一次启动以来,容器内是否有进程因内存不足(Out Of Memory,OOM)被杀死。结合退出码与宿主机记录判断。StartedAt/FinishedAt:最近一次启动/结束时间,用来对齐日志。[8]
深入一点:空日志和退出码 137 为什么都不能直接定案?
logs 通常读取标准输出(stdout,程序正常输出)和标准错误(stderr,程序错误输出)。只写容器内文件的日志,不会自动出现在这里;日志驱动(Docker 收集日志的后端)的配置也会影响可读性。[4]
Linux 下 137 常对应收到 SIGKILL(强制终止信号),也可能来自人为强制停止等路径。它本身不能证明 OOM;再看 OOMKilled 及同一时间的系统记录。不要把应用自定义的非零退出码都套成 Docker 错误码。退出状态文档 停止容器与 137 示例
容器已退出,你想只读取主进程退出码。补全字段路径。
docker container inspect --format '{{.}}' app
State.ExitCode 读取退出码。还要合读同组的 Error、OOMKilled 和日志,退出码不是完整诊断。
应用日志提示配置缺失,容器已 exited。你需要保留证据并进一步判断,下一步选什么?
已停止的容器不能 exec;先保存现有日志与状态。
stats 看当前资源数据,不是退出前的历史记录器。
日志给出应用线索,inspect 补充退出和配置证据。确认两者属于同一次启动,再针对缺失的配置做验证。
运行 · 逐段缩小访问故障范围
访问分段查
running 只表示主进程还在,不保证服务已准备好。先看发布的端口,再看应用监听的位置,最后用请求验证。这里以本机访问一个 Web 服务为例。[8] [7]
- 核对映射。找到容器端口(如
80/tcp),看对应的HostIp和HostPort。EXPOSE只声明端口,不会自动发布。此处限定普通 bridge 网络与-p;网络模式后续再学。[3] [7] - 核对监听。从日志和应用配置确认实际监听端口。容器内的
127.0.0.1只面向容器自身;通常需要让服务监听容器网络接口,例如0.0.0.0表示所有 IPv4 接口。发布到容器端口不会替你修改监听地址。网络隔离说明 - 发实际请求。在 Docker 宿主机上访问发布地址。容器内部可访问、宿主机失败,重点查映射及宿主机网络规则;宿主机成功、远程失败,再查发布的宿主机地址和外部网络。
下面实验只发布到宿主机 127.0.0.1。它允许本机验证,不提供远程访问。Docker Desktop 一般可以从桌面系统访问这个发布地址;连接远程 Docker 时,你的电脑和 Docker 宿主机可能不是同一台。[7]
端口发布为 127.0.0.1:8080:80。容器内服务却只监听 127.0.0.1:80。仅凭 inspect 显示有映射,能断定浏览器一定能访问吗?
不能。映射把请求送向容器网络接口,但服务只接收容器自身回环地址上的请求。还要核对监听并实际发请求;inspect 记录配置,不会替你完成连通性测试。
验证 · 给运行中的容器加一个检查进程
exec 新开进程
docker exec 在已运行的容器里启动一个新进程。它既可以执行一条命令,也可以启动 shell(命令解释器)。新进程只在容器主进程存活时运行;主进程在容器里通常是 PID 1,PID 就是进程编号。[5]
exit 只结束它。-i 保持标准输入,-t 分配伪终端(让 shell 以终端方式交互)。执行单条检查命令通常不需要 -it。需要分号、管道等 shell 语法时,显式使用 sh -c。上面的文件、sh 和 id 都要求镜像里实际存在。[5]
docker attach app 连接已有主进程的标准流,不会自动开 shell。Ctrl + C 可能把终止信号转交主进程;不要当作普通退出键。以 -it 启动的容器可用 Ctrl + P、再按 Ctrl + Q 脱离连接并保持运行。只观察日志时,优先 docker logs -f app。[6]
深入一点:没有 bash,或 exec 也报错?
先读错误,确认容器仍处于运行状态。精简镜像可能只有 sh;更精简的镜像可能连 shell、cat 和网络工具都没有。这不代表容器已坏。先使用已有日志和 inspect;调试镜像及工具注入属于进阶内容。检查命令也可能写文件或改变进程,应先做只读验证。[5]
你用 exec -it app sh 打开 shell,然后输入 exit。为什么通常不会让容器停止?什么情况会让它停止?
这个 shell 是 exec 新开的进程,退出它不结束原来的主进程。主进程退出,容器才随之停止;若你在 shell 中主动停止主进程或改坏服务,也会影响容器。
- 区分新 shell 与原主进程
- 用主进程是否存活解释容器状态
- 意识到 exec 内的操作仍可影响应用
资源 · 把实时数据当线索
stats 看当前
stats 适合查运行中的资源用量,不保存退出前的历史。--no-stream 只取一次;去掉它会持续更新,按 Ctrl + C 结束观察。[9]
- CPU %
- 计算用量 → 是否持续繁忙?
- MEM USAGE / LIMIT
- 内存用量/可用上限 → 是否接近边界?
- NET I/O
- 累计接收/发送数据 → 请求是否带来变化?
- BLOCK I/O
- 累计块设备读写 → 是否持续读写磁盘?
- PIDS
- 进程与内核线程数 → 是否异常增长?
高 CPU 是繁忙线索,不能单凭它断言故障。低 CPU 也可能是等待依赖。网络和磁盘的 I/O(输入/输出)是累计数据量,不是直接显示的速率;单次内存读数也不能证明发生过 OOM。[9]
深入一点:为什么内存读数和别的工具不同?
Linux 上,Docker CLI 的内存用量会扣除一部分缓存,原始 API 的口径不同。PIDS 也包含线程,不等同于 ps 看到的进程行数。精确分析资源限制留到下一页。[9]
服务现在能运行,但刚才退出过。你现在采样到内存很低,能排除刚才因内存不足而退出吗?还要查什么?
不能。当前采样不代表退出前用量。故障当时应保存 inspect 的 OOMKilled、退出时间和对应日志;重启后状态可能已更新,重建还会换掉对象。此时需找已保存的证据、宿主机记录或监控历史,不能拿新状态替代旧记录。
动手 · 在同一个流程里练四个命令
做一次排查
编写环境没有 Docker,尝试 docker version 返回命令不存在。以下全是待执行命令;预期仅按文档和脚本语义说明,未提供伪造的终端记录。维护者可补实测输出。
在已启动 Docker 的 Linux 环境运行,使用 Bash 或兼容 shell,并准备 curl。Docker Desktop 需启用 Linux 容器。首次拉取镜像需要联网;alpine:3.22 和 nginx:alpine 是镜像标签,标签内容可能更新。请确认 lp19-exit、lp19-web 两个名字及本机 8080 端口空闲。
宿主机影响:Web 实验创建容器网络、发布本机端口,Docker 可能配置宿主机网络规则。仅绑定 127.0.0.1;不需要手改 iptables,不使用 --privileged,不挂载宿主机目录。这里只操作下列两个实验容器。[7]
1. 故意退出,保留现场
预期(未实测):脚本向标准错误打印你在命令中写的提示,再以指定的非零码结束。容器保留为已退出状态;logs 应能读到提示,inspect 的 ExitCode 应与脚本一致。这是人为设置的故障,不是 Docker 自动诊断了配置问题。
这里特意没用 --rm,方便退出后取证。--network none 禁用这个容器的外部网络,但不阻止 Docker 在创建前拉取镜像。[1]
2. 启动 Web,检查访问和进程
预期(未实测):映射记录应对应本机 8080 到容器 80;nginx -t 检查配置语法及引用的文件,cat 让你核对监听配置。它们都不能替代 curl 的实际请求。等启动完成后,curl 应收到默认页 HTML;logs 可观察请求日志,stats 给出本次资源数据。退出 exec 的 shell 后,主进程应仍在运行。NGINX 官方镜像说明、NGINX 命令行参数 [4]
若第一次请求赶在服务准备好之前失败,先读日志,再重试。若出现端口占用,换一个空闲宿主机端口并同步修改 curl 地址。不要改容器端口来猜;先从配置确认应用监听了什么。
3. 清理本页创建的容器
Web 容器带 --rm,停止后自动删除;故障容器则显式删除。只运行与你实际创建成功的容器对应的清理命令。镜像仍保留供以后使用。[1]
回顾 · 让每条命令回答一个问题
按证据推进
- 起不来:保留原始报错,用
ps -a确认对象;创建前失败可能没有容器可查。 - 启动就退:
logs看应用叙述,inspect核对状态、退出码与时间。 - 运行不通:先查映射、监听和实际请求;
exec新开检查进程,attach连接已有主进程。 - 资源疑点:
stats看当前数据;故障原因仍需日志、状态和验证。
容器 running,浏览器却连接失败。你想检查配置,同时让主服务继续运行。哪个选择最合适?
attach 把输入交给已有主进程,不会解释成新建 shell 的请求。
低内存读数不能说明端口正确、服务已监听或依赖可用。
inspect 核对配置,exec 新开进程检查应用条件,实际请求验证路径。running 只说明主进程存活;资源数据无法替代连通性验证。
来源
下列官方资料核对于 2026-11-03。本页以 Docker Engine 29.x 的这些命令为范围,不依赖后续版本的新选项;具体状态字段另查 29.0 对应的 API v1.52。
- docker container run:创建、启动、ps -a、--rm、端口与网络参数。
- docker container logs:末尾、时间范围、时间戳与跟随。
- docker inspect 与 container inspect:格式模板、json 函数与端口字段。
- View container logs:stdout/stderr、日志驱动和 NGINX 日志。
- docker container exec:新进程、主进程生命周期、shell 语法与暂停状态。
- docker container attach:标准流、信号与脱离按键。
- Port publishing and mapping:发布地址、容器端口与本机访问范围。
- Engine API v1.52(OpenAPI 原文):ContainerState 字段;Engine 与 API 版本对应表。
- docker container stats:实时数据、字段释义、内存口径与线程计数。