Skip to content

容器排错实战:「起不来、连不上、进不去」怎么破

更新: 8/23/2026 字数: 0 字 时长: 0 分钟

开篇:那些年,联调环境里的容器坑

作为有后端接口开发经验的前端,你大概率遇到过这些场景:

  • 后端甩给你一个镜像,让你本地 docker run 跑起来自己联调,结果跑起来就秒退;
  • 联调环境好好的,某天前端页面所有接口都 502,你 F12 看半天,其实是容器挂了或连不上;
  • 你怀疑后端返回的数据不对,想进容器里看看配置文件和日志,却不知道怎么进去

这三类问题,就是容器化世界里最高频的「起不来、连不上、进不去」。好消息是:排查它们靠的不是内核知识,而是一套和你调接口高度相似的思路——看现象、定位环节、逐层收敛。 本文跳过晦涩的底层原理,只讲能直接抄作业的排错操作。

容器排错三大问题:起不来、连不上、进不去

在动手前,先记住一个"万能第一步"——先看状态:

bash
docker ps -a        # 列出所有容器(-a 包含已停止的),看 STATUS 列

STATUS 列就是你的第一份诊断书:

  • Up 10 minutes → 活着,问题多半在「连不上」;
  • Exited (1) 5 seconds ago → 起不来,退出码非 0,去看日志;
  • Restarting → 反复重启,通常是启动即崩溃在死循环重试。

模块一:起不来 —— 容器启动就退出

常见触发场景

  • 后端镜像 docker run 后,docker ps 里根本看不到它(其实已经 Exited);
  • 端口被占用、环境变量没配、依赖的数据库还没起来;
  • 镜像构建时就有问题(比如启动命令写错)。

分步排错思路

第一步:看退出码和状态。 docker ps -aSTATUSExited (0) 是正常结束(比如脚本跑完就退,这可能是预期行为);Exited (1)Exited (137) 等非 0 码就是异常。其中 137 是个高频信号,通常意味着内存超限被 OOM 杀掉(128 + 9,9 是 SIGKILL 信号)。

第二步:看日志——这是 90% 问题的答案所在。 这一步等价于你调接口时看后端的报错堆栈:

bash
docker logs <容器名或ID>          # 查看容器的标准输出/错误日志
docker logs --tail 50 <容器>    # 只看最后 50 行,快速定位崩溃点
docker logs -f <容器>           # 实时追踪(像 tail -f),适合看反复重启的

日志里一般会明明白白告诉你原因,比如 Error: listen EADDRINUSE :::8080(端口被占)、connection refused(依赖服务没起)、MODULE_NOT_FOUND(依赖没装)。

容器起不来:看日志找退出原因

第三步:如果日志没打出来就崩了(容器起来瞬间就挂,连日志都没来得及写),说明可能是启动命令本身就错了。这时可以覆盖启动命令,进去手动跑:

bash
# 不执行原本的启动命令,而是开一个 shell 进去手动排查
docker run -it --entrypoint /bin/sh <镜像>
# 进去后手动执行启动命令,直接看报错

典型案例:端口占用

docker run -p 8080:8080 backend-image,秒退。docker logs 显示 EADDRINUSE 8080。原因:你之前跑的另一个容器/本地进程已经占了宿主机 8080。

解决:换个宿主机端口映射即可,冒号左边改掉,容器内部不用动:

bash
docker run -p 8081:8080 backend-image   # 宿主机 8081 → 容器 8080
# 之后前端访问 localhost:8081

这和你本地起 dev server 报"端口被占,换个端口"是一模一样的处理逻辑。

模块二:连不上 —— 容器活着,但访问不通

这是最容易让前端困惑的一类,因为"容器明明是 Up 状态,为什么接口就是 connection refused?"。关键是理解:请求从你的浏览器到容器里的服务,要穿过好几层,任何一层断了都连不上。

先分清两种"连不上"

类型场景排查方向
外部访问不通浏览器 / Postman 访问 localhost:3000 失败端口映射、服务是否真在监听
容器间访问不通A 容器调 B 容器的接口失败是否同一网络、服务名解析

容器连不上:从端口映射到服务名逐层排查

分步排错思路(外部访问不通)

第一步:确认端口映射配了没。

bash
docker ps    # 看 PORTS 列,应显示 0.0.0.0:3000->3000/tcp

如果 PORTS 列是空的,或者没有 -> 箭头,说明你忘了加 -p 映射。容器内的服务再正常,外面也进不来——就像后端服务只监听了内网,没对外开放。

⚠️ 前端最常踩的坑:容器之间互访不需要端口映射(走内部网络),但你的浏览器在容器网络之外,必须靠端口映射才能进去。别把这两件事搞混。

第二步:确认服务真的在容器内监听了,而且监听的是 0.0.0.0 而非 127.0.0.1 这是一个极其隐蔽、又极其高频的坑:

bash
docker exec -it <容器> sh          # 进容器(下一模块细讲)
netstat -tlnp    # 或 ss -tlnp,看服务监听在哪个地址

如果看到服务监听的是 127.0.0.1:3000,那就是问题所在:容器内的 127.0.0.1 只有容器自己能访问,端口映射转发进来的流量根本够不着。解决办法是让你的后端服务监听 0.0.0.0(比如 Node 里 app.listen(3000, '0.0.0.0'))。这个坑几乎每个刚接触容器的人都会中一次。

分步排错思路(容器间访问不通)

第一步:确认两个容器在同一个网络。

bash
docker network ls                    # 列出所有网络
docker network inspect <网络>       # 看这个网络里挂了哪些容器

如果 A、B 不在同一个网络,自然互相找不到。用 Docker Compose 编排的服务默认在同一网络里,一般没这问题;手动 docker run 的容器则可能各在各的默认网络。

第二步:用服务名而不是 localhost 互访。 在 A 容器里调 B,地址要写 B 的服务名,不是 localhost:

bash
# ❌ 错误:localhost 指的是 A 容器自己
fetch('http://localhost:3000/api')
# ✅ 正确:用 B 的服务名,Docker 内置 DNS 会解析成它的 IP
fetch('http://backend:3000/api')

第三步:进 A 容器里实测连通性,这一步最能一锤定音:

bash
docker exec -it <A容> sh
ping backend            # 能不能解析到 IP(测 DNS 和网络)
curl http://backend:3000/api   # 能不能真正调通接口(测服务)

ping 通但 curl 不通 → 网络没问题,是对方服务没起或端口不对;ping 都不通 → 网络/服务名的问题。这套"分层定位"的思路,和你排查接口时"先看 DNS、再看网络、最后看服务返回"完全一致。

典型案例:联调环境接口全 502

现象:前端页面所有接口 502。排查:docker ps 发现 api 容器是 Up,但 nginx 容器 curl http://api:3000connection refused。进 api 容器 netstat 一看,服务监听在 127.0.0.1:3000根因:后端服务没监听 0.0.0.0。改配置重启,恢复。

模块三:进不去 —— 想进容器看看却无从下手

前面已经多次用到"进容器",这里系统讲。所谓"进不去",往往是不知道用什么命令进、或者容器压根没有 shell

核心命令:docker exec

docker exec 就是"在正在运行的容器里执行命令",最常用的是开一个交互式 shell 钻进去:

bash
docker exec -it <容器> /bin/bash    # 进入容器的 bash
docker exec -it <容器> /bin/sh      # 若没有 bash 就用 sh(精简镜像常见)

参数解释:-i 保持输入流打开(可交互),-t 分配一个终端(让界面像正常命令行)。两个一起用 -it,你就能像 SSH 到一台服务器一样在容器里敲命令了。

进入容器:docker exec 钻进盒子里看一看

进去之后,你可以像在普通 Linux 里一样干这些事:

bash
cat /app/config.json     # 看配置对不对
env                      # 看环境变量是否正确注入(很多联调问题是环境变量错)
ls -l /app               # 看代码/文件是不是真的在里面
ps aux                   # 看进程是不是真在跑

三个常见"进不去"的坑

坑一:容器已经退出了,exec 进不去。 exec 只能进运行中的容器。如果容器已 Exited,你要排查它,得用上面「起不来」模块的 docker logs,或用 --entrypoint 覆盖启动命令重新跑一个进去。

坑二:提示 bash: not found 很多精简镜像(如 alpine)没有 bash,改用 /bin/sh 即可。

坑三:不想进容器,只想临时看一眼文件。 不必进去,可以直接把文件拷出来:

bash
docker cp <容器>:/app/config.json ./     # 从容器拷文件到本机
docker cp ./fix.json <容器>:/app/config.json  # 也能把文件拷进去做临时修复

典型案例:接口返回的配置不对

现象:后端接口返回的某个配置值明显是错的,但代码看着没问题。排查:docker exec -it api sh 进去,env 一看,发现 API_BASE_URL 环境变量传的是测试环境的值。根因:docker run-e 传错了变量,或 compose 文件里配错。这类"环境变量注入错误"是联调环境最隐蔽的问题之一,进容器一看便知。

知识拓展:排错路上的关联知识

1. 容器网络基础(一句话理清)

  • 每个容器有自己独立的 IP 和 localhost,容器的 localhost 只指它自己;
  • 同一网络内的容器,用服务名互访,Docker 内置 DNS 自动解析,不用记 IP;
  • 端口映射(-p)只为让容器网络"之外"的访问者(你的浏览器)进来,容器互访不需要它。

记住这三条,「连不上」的问题你已经能解决大半。

2. 容器生命周期管理

容器有清晰的状态流转,理解它能帮你判断"该用哪个命令":

bash
docker start <容器>      # 启动已停止的容器
docker stop <容器>       # 优雅停止(发 SIGTERM,给程序时间收尾)
docker restart <容器>    # 重启,连不上时先试试这个
docker rm <容器>         # 删除已停止的容器
docker rm -f <容器>      # 强制删除运行中的容器

一个实用习惯:排查环境类问题,restart 试试,很多"偶发连不上"重启即恢复;若重启无效,再深入看日志。

3. 实用排错工具推荐

工具/命令用途适合场景
docker stats实时看容器 CPU/内存占用怀疑 OOM(137 退出码)、性能问题
docker inspect <容器>看容器完整配置(网络、挂载、环境变量)深挖端口映射、卷挂载、网络细节
docker logs -f实时追踪日志复现问题时盯着看
Docker Desktop GUI图形界面看容器/日志/终端不想记命令的前端,点点鼠标即可
Lazydocker终端里的可视化 Docker 面板想在命令行里高效浏览多个容器

对前端来说,Docker Desktop 的图形界面是最低门槛的选择:容器列表、日志、进入终端、资源占用一目了然,不用背命令。

结尾:容器排错通用方法论

抛开具体命令,排错的底层思路始终是这套**"三步收敛法"**,它和你排查线上接口问题的直觉完全一致:

  1. 看状态,定大类:docker ps -aSTATUS,先判断是「起不来(Exited)」还是「连不上(Up 但不通)」;
  2. 看日志,找线索:docker logs 是第一现场,90% 的启动问题日志里直接给答案;
  3. 进容器,做验证:docker exec -it 钻进去,用 env/netstat/curl 逐层确认环境变量、监听地址、连通性。

核心心法一句话:别猜,去看。 状态、日志、容器内部实况,这三样看全了,绝大多数问题自然浮出水面。

高频命令速查清单

bash
# —— 看状态 ——
docker ps -a                          # 所有容器及状态
docker stats                          # 实时资源占用

# —— 起不来 ——
docker logs --tail 50 <>          # 看最后 50 行日志
docker logs -f <>                 # 实时追踪日志
docker run -it --entrypoint sh <> # 崩溃前就退?覆盖启动命令进去手动查

# —— 连不上 ——
docker ps                             # 看 PORTS 列确认端口映射
docker network ls                     # 看网络列表
docker network inspect <>         # 看网络里有哪些容器
docker exec -it <> sh -c "netstat -tlnp"   # 看服务监听地址(是否 0.0.0.0)

# —— 进不去 ——
docker exec -it <> /bin/bash      # 进容器(无 bash 换 sh)
docker cp <>:/path/file ./        # 从容器拷文件出来
docker inspect <>                 # 看完整配置(环境变量/挂载/网络)

# —— 生命周期 ——
docker restart <>                 # 重启(偶发问题先试它)
docker rm -f <>                   # 强制删除