← 全部笔记

Docker · 3 容器运行与管理

先看状态,再选排查命令

起不来先读启动错误,退出后查日志和状态;还在运行,再沿访问路径验证。每一步都用证据缩小范围。

约 15 分钟 · 截至 2026-11-03,Docker Engine 29.x · 本页 Docker 实验未实测

三种现象,对应三个证据入口 起不来先读命令行错误并检查是否创建了容器;启动就退出合读 logs 和 inspect;运行却无法访问先查端口和监听,再用 exec 验证。stats 用于资源线索。 起不来 查 启动错误/是否创建 启动就退 查 logs + inspect 运行不通 查 映射 → 监听 → 请求 inspect → exec
图 1 状态决定从哪里取证;资源疑点再交给 stats。[1] [3]

入口 · 先确定对象还在不在

先分状态

先运行 docker ps -a,不要一上来就 exec。-a 会列出停止的容器。下文用 app 代表待排查的容器名,执行时换成你自己的名字。[1]

docker ps -a
docker container inspect --format '{{.State.Status}}' app

第二条命令读取状态。--format 用模板只取所需字段;{{.State.Status}} 表示读取 State 下的 Status。不加它会输出完整的 JSON(结构化的键值记录)。[3]

看见什么先看什么证据下一步
没有目标容器原始 docker run 报错;名字与连接目标可能创建前失败,或已被 --rm 删除
created.State.Error;原始启动错误对象已创建,主进程可能尚未启动成功
exitedlogs;退出码与结束时间按下一节合读证据
running端口映射;应用日志;实际请求沿访问路径检查,再用 exec

拉取镜像失败时,容器可能根本没创建;端口被占用时,错误可能发生在主进程启动前。logs 没内容不能排除这些问题。若提示连不上 Docker 后台服务或无权限,先解决客户端到服务端的连接;可复习第一次 docker run 的请求链路。

docker run 报错了,docker ps -a 里也没有目标容器。此时直接执行 docker exec app sh,能帮你找到应用错误吗?写出下一步。

不能。exec 需要一个正在运行的目标。先保留原始启动报错,确认容器名、连接目标,以及是否用了 --rm;没有容器对象就没有它的日志和状态可读。

深入一点:paused 和 restarting 怎么办?

paused 表示进程被暂停,此时不能 exec;先确认暂停原因,再决定是否恢复。restarting 表示正在反复重启,exec 可能赶不上运行窗口;先用 logs 和 inspect 保存证据。重启策略在下一页学习。[5] [8]

退出 · 把程序叙述和运行记录拼起来

退出查两处

logs 告诉你程序说了什么;inspect 告诉你 Docker 记录了什么。容器停止后,只要对象还在,仍可查这两处。先保存证据,再考虑重启。[2] [8]

docker logs --tail 50 --timestamps app
docker container inspect --format '{{json .State}}' app
docker logs --since 10m --tail 50 -f app

--tail 50 只取末尾 50 行;--timestamps 加时间戳。--since 10m 筛最近 10 分钟,-f 持续跟随新日志;按 Ctrl + C 结束跟随,不会因此停止容器。json 把 State 这组字段转成 JSON。[2] [3]

六步把退出线索变成待验证的判断 确定对象状态、读取近期日志、找应用线索、核对退出字段、对齐时间,最后提出下一次验证。日志和状态共同支持判断。 ① ps -a:确认对象与状态 取日志② logs:限定末尾与时间 读线索③ 配置错误?依赖连接失败? 核对④ inspect:退出码与状态 对时⑤ 日志时间 ↔ FinishedAt 验证⑥ 提出假设,只改一个条件
  1. 确认对象。先区分没有创建、已经停止、仍在运行。名字相同也要确认查的是本次实例。
  2. 限定日志。先取末尾和故障附近的记录,避免旧错误淹没当前问题。先猜:日志里有“配置缺失”,还要核对哪类信息?
  3. 摘出线索。记录应用自己的错误及发生时间。日志中的报错是线索,还不能代表 Docker 的退出记录。
  4. 核对状态。读取 ExitCode、Error、OOMKilled。先猜:上次重启前的错误,能直接解释这次退出吗?
  5. 对齐时间。用 StartedAt 和 FinishedAt 对照日志,避免把历史错误当成本次原因。
  6. 提出可验证的判断。例如“启动参数漏了配置项”。保留证据后,只改这一项并重新观察。
图 2 程序日志与退出记录在同一时间段吻合,才能支持下一步验证。
深入一点:空日志和退出码 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]

从宿主机端口到容器监听的访问路径 浏览器访问 Docker 宿主机的 127.0.0.1:8080,经发布规则进入容器的 80 端口,再由应用响应。三段都需要验证。 本机请求 127.0.0.1:8080 连接宿主机 发布规则:宿主机 → 容器 127.0.0.1:8080 → 80/tcp 转发请求 容器边界 应用监听 0.0.0.0:80 接收请求并响应
图 3 发布规则存在,还需要应用在对应端口接受请求;此图省略底层网络实现。
docker container inspect --format '{{json .NetworkSettings.Ports}}' app
docker logs --since 10m --tail 50 app
  1. 核对映射。找到容器端口(如 80/tcp),看对应的 HostIp 和 HostPort。EXPOSE 只声明端口,不会自动发布。此处限定普通 bridge 网络与 -p;网络模式后续再学。[3] [7]
  2. 核对监听。从日志和应用配置确认实际监听端口。容器内的 127.0.0.1 只面向容器自身;通常需要让服务监听容器网络接口,例如 0.0.0.0 表示所有 IPv4 接口。发布到容器端口不会替你修改监听地址。网络隔离说明
  3. 发实际请求。在 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]

exec 创建新的 shell 进程终端通过 exec 新开 sh 进程;主进程继续运行。exit 结束这个 shell。 你的终端 运行中的容器 exec 新建 sh 新进程 主进程继续运行
图 4 exec 新建 shell;在这个 shell 输入 exit 只结束它。
attach 连接已有主进程的标准流终端连接主进程的标准输入输出,不创建 shell。输入与信号可能影响主进程。 你的终端 运行中的容器 attach 连接 已有主进程 输入 ↓ ↑ 输出/错误
图 5 attach 连接已有标准流;输入或信号可能直接影响主进程。
docker exec app cat /etc/os-release
docker exec -it app sh
# 在新开的 sh 中输入 exit,返回宿主机终端
docker exec app sh -c 'pwd; id'

-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]

docker stats --no-stream app
docker stats app
CPU %
计算用量 → 是否持续繁忙?
MEM USAGE / LIMIT
内存用量/可用上限 → 是否接近边界?
NET I/O
累计接收/发送数据 → 请求是否带来变化?
BLOCK I/O
累计块设备读写 → 是否持续读写磁盘?
PIDS
进程与内核线程数 → 是否异常增长?
图 6 字段指向待验证的资源疑点;这里是字段释义,没有模拟测量值。

高 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. 故意退出,保留现场

docker run --name lp19-exit --network none alpine:3.22 sh -c 'echo "missing configuration" >&2; exit 7'
docker ps -a --filter name=lp19-exit
docker logs --tail 10 lp19-exit
docker container inspect --format '{{json .State}}' lp19-exit

预期(未实测):脚本向标准错误打印你在命令中写的提示,再以指定的非零码结束。容器保留为已退出状态;logs 应能读到提示,inspect 的 ExitCode 应与脚本一致。这是人为设置的故障,不是 Docker 自动诊断了配置问题。

这里特意没用 --rm,方便退出后取证。--network none 禁用这个容器的外部网络,但不阻止 Docker 在创建前拉取镜像。[1]

2. 启动 Web,检查访问和进程

docker run --rm -d --name lp19-web -p 127.0.0.1:8080:80 nginx:alpine
docker container inspect --format '{{json .NetworkSettings.Ports}}' lp19-web
docker exec lp19-web nginx -t
docker exec lp19-web cat /etc/nginx/conf.d/default.conf
curl --fail --show-error http://127.0.0.1:8080/
docker logs --tail 20 lp19-web
docker stats --no-stream lp19-web
docker exec -it lp19-web sh
# 在新 shell 输入 exit,然后在宿主机继续:
docker container inspect --format '{{.State.Running}}' lp19-web

预期(未实测):映射记录应对应本机 8080 到容器 80;nginx -t 检查配置语法及引用的文件,cat 让你核对监听配置。它们都不能替代 curl 的实际请求。等启动完成后,curl 应收到默认页 HTML;logs 可观察请求日志,stats 给出本次资源数据。退出 exec 的 shell 后,主进程应仍在运行。NGINX 官方镜像说明、NGINX 命令行参数 [4]

若第一次请求赶在服务准备好之前失败,先读日志,再重试。若出现端口占用,换一个空闲宿主机端口并同步修改 curl 地址。不要改容器端口来猜;先从配置确认应用监听了什么。

3. 清理本页创建的容器

docker stop lp19-web
docker rm lp19-exit

Web 容器带 --rm,停止后自动删除;故障容器则显式删除。只运行与你实际创建成功的容器对应的清理命令。镜像仍保留供以后使用。[1]

回顾 · 让每条命令回答一个问题

按证据推进

容器 running,浏览器却连接失败。你想检查配置,同时让主服务继续运行。哪个选择最合适?

attach 把输入交给已有主进程,不会解释成新建 shell 的请求。

低内存读数不能说明端口正确、服务已监听或依赖可用。

inspect 核对配置,exec 新开进程检查应用条件,实际请求验证路径。running 只说明主进程存活;资源数据无法替代连通性验证。

来源

下列官方资料核对于 2026-11-03。本页以 Docker Engine 29.x 的这些命令为范围,不依赖后续版本的新选项;具体状态字段另查 29.0 对应的 API v1.52。

  1. docker container run:创建、启动、ps -a、--rm、端口与网络参数。
  2. docker container logs:末尾、时间范围、时间戳与跟随。
  3. docker inspect 与 container inspect:格式模板、json 函数与端口字段。
  4. View container logs:stdout/stderr、日志驱动和 NGINX 日志。
  5. docker container exec:新进程、主进程生命周期、shell 语法与暂停状态。
  6. docker container attach:标准流、信号与脱离按键。
  7. Port publishing and mapping:发布地址、容器端口与本机访问范围。
  8. Engine API v1.52(OpenAPI 原文):ContainerState 字段;Engine 与 API 版本对应表。
  9. docker container stats:实时数据、字段释义、内存口径与线程计数。