目录
容器调试教程:使用 docker logs、exec、inspect、cp 和 stats
容器在后台运行,出错时不会直接显示在屏幕上。本节整理了最常用的调试工具,并实际重现几个常见错误。本节的示例都以一个 nginx 容器为对象:
docker run -d --name web -p 8080:80 nginx:1.30
查看日志:docker logs
容器主进程输出到标准输出(stdout)和标准错误(stderr)的内容,都可以用 docker logs 查看。先向 nginx 发送几个请求(其中 /page1 到 /page3 不存在),再查看日志:
$ docker logs --tail 3 web
2026/10/12 23:52:59 [error] 28#28: *3 open() "/usr/share/nginx/html/page3" failed (2: No such file or directory), client: 172.18.0.1, server: localhost, request: "GET /page3 HTTP/1.1", host: "localhost:8080"
172.18.0.1 - - [12/Oct/2026:23:52:59 +0000] "GET /page3 HTTP/1.1" 404 153 "-" "curl/8.22.0" "-"
172.18.0.1 - - [12/Oct/2026:23:52:59 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.22.0" "-"
$ docker logs --tail 2 -t web
2026-10-12T23:52:59.403434333Z 172.18.0.1 - - [12/Oct/2026:23:52:59 +0000] "GET /page3 HTTP/1.1" 404 153 "-" "curl/8.22.0" "-"
2026-10-12T23:52:59.503902089Z 172.18.0.1 - - [12/Oct/2026:23:52:59 +0000] "GET / HTTP/1.1" 200 896 "-" "curl/8.22.0" "-"
| 参数 | 作用 |
|---|---|
--tail 3 | 只看最后 3 行 |
-t | 在每行前加上 Docker 记录的时间戳 |
-f | 持续跟踪新日志(类似 tail -f),按 Ctrl + C 退出 |
--since 10m | 只看最近 10 分钟(也可以写成 2026-10-12T23:00:00) |
容器停止后也能查看日志,这是排查“为什么挂了”的第一步。
stdout 与 stderr
将日志保存到文件时,你会发现一件奇怪的事:
$ docker logs --since 1m web > out.log; wc -l out.log
2026/10/12 23:52:57 [notice] 1#1: using the "epoll" event method
2026/10/12 23:52:57 [notice] 1#1: nginx/1.30.5
2026/10/12 23:52:57 [notice] 1#1: built by gcc 14.2.0 (Debian 14.2.0-19)
2026/10/12 23:52:57 [notice] 1#1: OS: Linux 5.4.0-216-generic
2026/10/12 23:52:57 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2026/10/12 23:52:57 [notice] 1#1: start worker processes
2026/10/12 23:52:57 [notice] 1#1: start worker process 28
2026/10/12 23:52:59 [error] 28#28: *1 open() "/usr/share/nginx/html/page1" failed (2: No such file or directory), client: 172.18.0.1, server: localhost, request: "GET /page1 HTTP/1.1", host: "localhost:8080"
2026/10/12 23:52:59 [error] 28#28: *2 open() "/usr/share/nginx/html/page2" failed (2: No such file or directory), client: 172.18.0.1, server: localhost, request: "GET /page2 HTTP/1.1", host: "localhost:8080"
2026/10/12 23:52:59 [error] 28#28: *3 open() "/usr/share/nginx/html/page3" failed (2: No such file or directory), client: 172.18.0.1, server: localhost, request: "GET /page3 HTTP/1.1", host: "localhost:8080"
13 out.log
$ docker logs --since 1m web > all.log 2>&1; wc -l all.log
23 all.log
已经用 > 重定向到文件了,nginx 的启动信息和错误日志却仍然显示在屏幕上,文件里只有 13 行。原因是 docker logs 会分别保留 stdout 和 stderr:nginx 的访问记录写入 stdout,错误和启动信息写入 stderr。> 只重定向 stdout,加上 2>&1 才会把两者都保存到文件(23 行)。用 grep 过滤日志时也一样:docker logs web 2>&1 | grep error。
如果程序把日志写入容器内的文件(例如
/var/log/app.log),docker logs就看不到。官方镜像通常会把日志重定向到 stdout / stderr;自己编写的程序也建议直接输出到 stdout。
进入容器:docker exec
在运行中的容器内运行命令:
$ docker exec web cat /etc/nginx/conf.d/default.conf | head -n 12
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
$ docker exec -u nginx web whoami
nginx
$ docker exec -e DEBUG=1 web sh -c "echo DEBUG=\$DEBUG"
DEBUG=1
| 写法 | 作用 |
|---|---|
docker exec -it web bash | 打开一个交互式 shell(精简镜像没有 bash 时改用 sh) |
docker exec web 命令 | 运行单条命令,输出直接返回到你的终端 |
docker exec -u nginx web … | 以指定用户运行 |
docker exec -e KEY=VALUE web … | 传入环境变量 |
exec 会启动一个额外进程,退出(exit)不会让容器停止。
如果容器启动即退出,就无法使用 exec(容器没有在运行)。这时可以用 --entrypoint 启动一个新容器,不运行原来的服务,直接进入容器查看:
$ docker run --rm --entrypoint sh nginx:1.30 -c "ls /docker-entrypoint.d"
10-listen-on-ipv6-by-default.sh
15-local-resolvers.envsh
20-envsubst-on-templates.sh
30-tune-worker-processes.sh
在 Docker Desktop 中,点击进入容器后,也可以在 Exec 选项卡中打开 shell:

查看配置与状态:docker inspect
docker inspect 会输出容器的完整配置和状态(JSON),搭配 --format 提取需要的字段:
$ docker inspect --format '{{.State.Status}} {{.State.ExitCode}}' web
running 0
$ docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
172.18.0.2
$ docker inspect --format '{{json .HostConfig.PortBindings}}' web
{"80/tcp":[{"HostIp":"","HostPort":"8080"}]}
$ docker inspect --format '{{.Path}} {{.Args}}' web
/docker-entrypoint.sh [nginx -g daemon off;]
| 想了解 | 字段 |
|---|---|
| 停止原因 | .State.ExitCode、.State.Error、.State.OOMKilled |
| 挂载了哪些文件夹 | .Mounts |
| 环境变量 | .Config.Env |
| 实际运行的命令 | .Path、.Args |
| IP 地址 | .NetworkSettings.Networks |
| 端口映射 | .HostConfig.PortBindings |
| 重启次数 | .RestartCount |
复制文件:docker cp
在主机和容器之间复制文件,容器停止时也可以操作:
$ docker cp web:/etc/nginx/nginx.conf ./nginx.conf
$ head -n 5 nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
$ echo "<h1>copied in</h1>" > index.html
$ docker cp ./index.html web:/usr/share/nginx/html/
$ curl -s http://localhost:8080
<h1>copied in</h1>
适合取出配置文件作参考,或临时放入一个文件进行测试。正式做法仍然是写入 Dockerfile 或使用 Volume 挂载。
资源与进程:docker stats、top、diff
$ docker stats --no-stream
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
4a43d09d0bcb web 0.00% 7.59MiB / 1.919GiB 0.39% 2.79kB / 2.5kB 15MB / 8.19kB 2
$ docker top web
PID USER TIME COMMAND
1180 root 0:00 nginx: master process nginx -g daemon off;
1238 101 0:00 nginx: worker process
docker stats:CPU、内存、网络、硬盘 I/O。不加--no-stream会持续更新(按Ctrl + C退出)。未设置内存上限时,LIMIT 就是主机(或 Docker Desktop 虚拟机)的总内存。docker top:查看容器内的进程。nginx 镜像中没有ps命令(参见第一个容器),可以从外部用docker top查看。
$ docker diff web
C /run
A /run/nginx.pid
C /etc
C /etc/nginx
C /etc/nginx/conf.d
C /etc/nginx/conf.d/default.conf
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
docker diff 会列出容器可写层相对于原镜像新增(A)、修改(C)、删除(D)了哪些文件。可以看到刚才用 docker cp 放进去的 index.html 被标记为修改,default.conf 则是 nginx 启动脚本自动修改的(启用了 IPv6)。这些变更会在容器删除时消失。
常见问题示例
容器启动即退出
$ docker run -d --name broken postgres:18
Unable to find image 'postgres:18' locally
18: Pulling from library/postgres
ecc510c1e359: Already exists
4a3cafbebf6d: Pull complete
28ced28a309b: Pull complete
ab69e2551985: Pull complete
1f04dfa92b9f: Pull complete
5a487c9461a5: Pull complete
e0d4a6e58470: Pull complete
496ce2ef5b2f: Pull complete
c8145e79cc2b: Pull complete
d23c975c4693: Pull complete
06ea90d3bcc9: Pull complete
dc25540d793f: Pull complete
7967f593457f: Pull complete
Digest: sha256:74935e72241653ca55e0414067e6d8763aceb8a810eb51b452253ec3dcfc4336
Status: Downloaded newer image for postgres:18
05a9084084fc2b744e4414e95323159bbc7455a1872784176f7333d31e875bda
$ docker ps -a --filter name=broken
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
05a9084084fc postgres:18 "docker-entrypoint.s…" 4 seconds ago Exited (1) 4 seconds ago broken
$ docker logs broken
Error: Database is uninitialized and superuser password is not specified.
You must specify POSTGRES_PASSWORD to a non-empty value for the
superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run".
You may also use "POSTGRES_HOST_AUTH_METHOD=trust" to allow all
connections without a password. This is *not* recommended.
See PostgreSQL documentation about "trust":
https://www.postgresql.org/docs/current/auth-trust.html
docker run -d 打印出容器 ID 后,你可能以为运行成功了,但 docker ps -a 显示它几秒后就变成 Exited (1)。docker logs 直接告诉你原因:没有设置 POSTGRES_PASSWORD。排查顺序始终是:用 docker ps -a 查看状态 → 用 docker logs 查看原因。
端口已被占用
$ docker run -d -p 8080:80 nginx:1.30
c7b7a64eab9314885fb41d5ac82352c519240a17ea2e2e53098a0536a3264e6c
docker: Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint youthful_aryabhata (aff407d4a63df5baee04263bfdc54732e45cb69016b909a8e809c9010d81e926): Bind for 0.0.0.0:8080 failed: port is already allocated
Run 'docker run --help' for more information
主机的 8080 端口已被 web 容器占用。注意,第一行仍然打印出了容器 ID:容器其实已经创建,只是启动失败,会以 Created 状态留在 docker ps -a 中,记得删除它。解决方法:换一个主机端口(-p 8081:80),或找出占用者(docker ps;如果是非 Docker 程序,则用 sudo lsof -i :8080)。
exec format error
$ docker run --rm --platform linux/arm64 alpine:3.23 uname -m
Unable to find image 'alpine:3.23' locally
3.23: Pulling from library/alpine
ace1621be7ff: Pull complete
Digest: sha256:85fe1e81d6758c208f3e1eed4338a1997e19d4be002d4dd32d3100c9a8c010a0
Status: Image is up to date for alpine:3.23
exec /bin/uname: exec format error
这台主机是 amd64(x86_64),却强制运行 arm64 版本的镜像,程序根本无法在这款 CPU 上运行。实际中最常见的情况是:在 Apple Silicon Mac 上构建的镜像,被放到普通的 x86 云主机上运行。解决方法见多平台镜像。
内存不足(OOM)
容器超出 --memory 设置的上限时,Linux 内核会强制结束进程:
docker ps -a显示Exited (137)docker inspect --format '{{.State.OOMKilled}}' 容器名称显示true
遇到 137,但 OOMKilled 是 false,通常是被 docker kill 结束,或 docker stop 超时后强制结束。调高 --memory,或检查程序是否存在内存泄漏。
no space left on device
Docker 占用的硬盘空间太多。先用 docker system df 查看:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 6 2 1.026GB 757.3MB (73%)
Containers 3 2 122.9kB 4.096kB (3%)
Local Volumes 1 0 0B 0B
Build Cache 0 0 0B 0B
RECLAIMABLE 表示可以清理的部分,清理方法见清理硬盘空间。
排查流程总结
docker ps -a:容器是否在运行?退出代码是多少?docker logs 容器(必要时加上2>&1 | grep):程序本身报告了什么?- 对照退出代码:
1查看日志、127找不到命令、137被强制结束或 OOM、139程序崩溃(segfault)。 docker inspect:确认环境变量、挂载、端口、运行的命令是否符合预期。docker run -it --rm --entrypoint sh 镜像:进入容器手动运行,看看缺少什么。
重点回顾
- 先查看
docker ps -a的状态与退出代码,再查看docker logs。 - 用
docker exec进入运行中的容器,用docker cp取出文件。 - 用
docker inspect -f查看配置与状态,用docker stats查看资源用量。 - 容器启动即退出时,用
--entrypoint sh启动新容器手动排查。
动手练习
练习 1:运行 docker run --name broken alpine:3.23 ls /not-exist,用本章的方法找出它为什么失败,以及退出代码是多少。
答案
docker ps -a --filter name=broken
docker logs broken
docker inspect -f "{{.State.ExitCode}}" broken
日志显示 ls: /not-exist: No such file or directory,退出代码是 1。
练习 2:启动 docker run -d --name w nginx:1.30-alpine,在容器内创建一个文件后,用什么命令可以列出容器相对于镜像发生了哪些文件变更?
答案
docker diff w。A 表示新增、C 表示变更、D 表示删除,原理见底层原理。
常见问题
容器启动即退出,无法用 exec 进入怎么办?
先查看 docker logs 容器;再用 docker run --rm -it --entrypoint sh 镜像 启动一个新容器,手动运行原来的命令查看错误。
docker logs 太多看不完怎么办?
用 --tail 50 只看最后 50 行,或用 --since 10m 只看最近 10 分钟,再配合 2>&1 | grep 关键词。完整参数见 [docker logs](/zh/docker/docker-logs/)。
在哪里可以查到错误信息的含义?
参见[常见错误信息大全](/zh/docker/errors/),其中整理了 25 种错误的实际输出和解决方法。
更新于 2026年10月14日