首页/网络与部署/Docker 部署老出问题?这篇排查指南把常见坑一次讲透
网络与部署

Docker 部署老出问题?这篇排查指南把常见坑一次讲透

Docker 部署故障排查完整手册:从容器启动失败、端口冲突、卷权限、Compose 配置、Nginx 反代、磁盘占满、镜像拉取失败到服务依赖顺序错乱,逐个击破生产环境最常见的问题。

发布时间:2026年4月9日 20:29阅读量:2

做 Docker 部署,最怕的不是报错,而是"看起来启动了,实际上根本不能用"。

容器起来了,页面打不开。 端口映射配了,外部访问不到。 数据库挂载了卷,结果重启后权限炸了。 Nginx 反代写好了,最后只看到一个刺眼的 502。 更烦的是,有些问题不是 Docker 本身出错,而是应用、网络、权限、磁盘、反向代理、环境变量混在一起,把排查搞得一团乱。

这篇文章不讲虚的,直接按生产环境里最常见的故障来拆。你可以把它当成一份 Docker 部署故障排查手册:从容器启动失败,到端口冲突、卷权限、Compose 配置、Nginx 反代、磁盘占满、镜像拉取失败、服务依赖顺序错乱,逐个击破。

一、先记住:Docker 部署问题,本质上就 4 类

绝大多数 Docker 部署故障,最终都能归到下面四层:

镜像层 镜像构建失败、镜像拉取失败、架构不匹配、启动命令错误。

容器层 容器秒退、反复重启、环境变量没生效、进程没真正启动。

网络层 端口冲突、端口映射错误、容器间互联失败、DNS 解析异常、反代打不通。

数据层 卷挂载路径错、文件权限不对、配置文件没覆盖成功、磁盘满、日志爆仓。

你只要按这个思路排,不容易乱。

二、Docker 部署出问题时,正确排查顺序是什么

很多人一上来就改 Compose 文件,改半天,越改越乱。 正确顺序应该是:

第一步:看容器状态

bash
docker ps -a

重点看这几个状态:

  • Up:容器运行中
  • Exited:容器已退出
  • Restarting:容器在反复重启
  • Created:创建了但没真正跑起来

如果容器都没活着,别急着看 Nginx,也别急着怀疑网络,先解决容器启动问题。

第二步:看日志

bash
docker logs --tail 200 -f 容器名

或者 Compose 项目:

bash
docker compose logs -f

很多问题,日志第一屏就已经写明白了,比如:

  • 配置文件不存在
  • 端口被占用
  • 数据库连接失败
  • 环境变量缺失
  • 权限不足
  • 启动命令报错
  • 依赖服务还没准备好

一句大实话:Docker 问题里,至少一半是日志已经告诉你答案,只是你没看。

第三步:确认端口是否真的监听

宿主机检查端口占用:

bash
ss -ltnp

指定端口检查:

bash
ss -ltnp | grep :80
ss -ltnp | grep :8080

容器内检查服务是否真的监听:

bash
docker exec -it 容器名 sh

进入后执行:

bash
ss -ltnp

如果容器进程活着,但应用根本没监听目标端口,那外部当然访问不到。

第四步:检查配置与挂载

bash
docker inspect 容器名

重点看:

  • 挂载路径是否正确
  • 环境变量是否传入
  • 端口映射是否正确
  • 启动命令是否符合预期
  • 网络是否加入正确

三、常见问题 1:容器一启动就退出

这是最常见的一类。

典型现象

bash
docker ps -a

看到类似:

  • Exited (1)
  • Exited (127)
  • Exited (137)
  • Restarting (1)

常见原因

1. 启动命令写错了

比如镜像里根本没有这个命令:

yaml
command: npm start

但实际项目里应该跑的是:

yaml
command: node server.js

或者工作目录不对,导致命令找不到。

2. 应用主进程跑完就退出

Docker 容器不是虚拟机。 主进程结束,容器就结束。

比如你写了一个一次性脚本,执行完自然退出,这不是 Docker 出错,是容器生命周期结束。

3. 环境变量缺失

比如:

  • DATABASE_URL not set
  • SECRET_KEY missing
  • PORT undefined

应用直接启动失败。

4. 配置文件路径错

你以为挂载到了 /app/config,实际应用读的是 /etc/app/config.yaml

5. 内存不足被系统杀掉

如果退出码是 137,通常要警惕:

  • 内存不够
  • 容器被 OOM Kill
  • 宿主机资源耗尽

查看系统日志:

bash
dmesg | tail -n 50

解决方法

先看日志:

bash
docker logs --tail 200 容器名

再看启动命令和环境变量:

bash
docker inspect 容器名

如果是 Compose 部署,优先检查:

yaml
services:
  app:
    image: your-app:latest
    environment:
      - NODE_ENV=production
      - PORT=3000
    command: node server.js

四、常见问题 2:容器明明启动了,但外部就是访问不到

这类问题最容易让人误判。

先检查端口映射是否正确

Compose 里常见写法:

yaml
ports:
  - "8080:3000"

意思是:

  • 宿主机 8080
  • 映射到容器内 3000

很多人实际应用监听的是 127.0.0.1:3000,而不是 0.0.0.0:3000,这会导致映射失效。

应用应该监听:

0.0.0.0:3000

而不是:

127.0.0.1:3000

典型排查流程

1. 宿主机上测端口

bash
curl http://127.0.0.1:8080

如果本机能通,外部不通,重点看防火墙和云服务器安全组。

2. 容器内测服务

bash
docker exec -it 容器名 sh
curl http://127.0.0.1:3000

如果容器内都不通,那就不是 Docker 映射问题,而是应用根本没正常监听。

3. 检查防火墙

Ubuntu 常见:

bash
ufw status

CentOS / Rocky / AlmaLinux:

bash
firewall-cmd --list-ports

云服务器还要检查:

  • 安全组
  • 入站规则
  • 运营商端口限制

五、常见问题 3:端口冲突,部署直接失败

常见报错

Bind for 0.0.0.0:80 failed: port is already allocated

或者:

Error starting userland proxy

原因

宿主机端口已经被别的服务占用了,比如:

  • Nginx
  • Apache
  • 另一个 Docker 容器
  • 本地开发服务

排查命令

bash
ss -ltnp | grep :80
ss -ltnp | grep :443

如果你看到 80 端口已经被别的进程占用,要么停掉它,要么换端口。

解决方案

方案一:改宿主机映射端口

yaml
ports:
  - "8080:80"

方案二:释放原端口

如果是旧容器占用:

bash
docker ps
docker stop 旧容器名
docker rm 旧容器名

如果是系统服务占用,比如 Nginx:

bash
systemctl stop nginx
systemctl disable nginx

六、常见问题 4:环境变量配了,程序却像没看到一样

这类坑在生产部署里特别多。

常见原因

1. .env 文件没被 Compose 正确读取

2. 变量名写错

例如程序要的是:

DATABASE_URL

你写成了:

DB_URL

3. YAML 格式错误

错误写法:

yaml
environment:
NODE_ENV: production
PORT: 3000

这种缩进错一点,结果就全乱。

4. 程序读取方式不对

环境变量已经传进去了,但应用代码没取到。

推荐写法

yaml
services:
  app:
    image: myapp:latest
    env_file:
      - .env
    environment:
      - NODE_ENV=production
      - PORT=3000

部署后直接进容器确认:

bash
docker exec -it 容器名 env

看变量到底有没有进去。

七、常见问题 5:数据卷挂载后,权限炸了

典型现象

  • 容器启动报 Permission denied
  • 应用写不了日志
  • 数据库初始化失败
  • 上传目录无法写入
  • 容器里文件能看到,但就是不能改

本质原因

宿主机目录和容器运行用户的 UID/GID 不一致。

很多镜像并不是用 root 跑的。 比如镜像里的应用用户 UID 是 1000,你宿主机挂载目录属主却是 root,那它当然写不进去。

排查方法

查看宿主机目录权限:

bash
ls -lah ./data

查看容器内运行用户:

bash
docker exec -it 容器名 id

解决方法

方案一:调整宿主机目录权限

bash
chown -R 1000:1000 ./data
chmod -R 755 ./data

方案二:显式指定容器用户

yaml
services:
  app:
    image: myapp:latest
    user: "1000:1000"

方案三:别乱用 777

很多人上来就是:

bash
chmod -R 777 ./data

这招不是不能用,而是太粗暴。 临时救火可以,长期生产环境不推荐。

八、常见问题 6:Docker Compose 启动成功,但服务其实没准备好

很多人误以为 docker compose up -d 执行完,业务就一定正常。 错。很可能只是容器启动了,应用还没就绪。

典型场景

  • Web 服务先启动了,但数据库还没准备好
  • 应用依赖 Redis,但 Redis 还没监听端口
  • 反向代理起来了,但后端接口还没 ready

解决思路:健康检查

推荐在 Compose 里加 healthcheck:

yaml
services:
  app:
    image: myapp:latest
    ports:
      - "8080:3000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 5

数据库服务也可以加:

yaml
services:
  db:
    image: mysql:8
    environment:
      - MYSQL_ROOT_PASSWORD=123456
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-p123456"]
      interval: 10s
      timeout: 5s
      retries: 10

然后用应用自身的重试机制,别把"依赖服务一瞬间没起来"当成永远失败。

九、常见问题 7:Nginx 反向代理后出现 502 / 504

这类问题,表面看是 Nginx 报错,实际上多数是后端没通。

502 的常见原因

  • 后端容器没启动
  • Nginx 指向了错误的 upstream 地址
  • 容器名写错
  • 后端监听地址不对
  • 容器间网络不通

504 的常见原因

  • 后端响应超时
  • 程序卡死
  • 数据库查询过慢
  • 上游服务未及时返回

正确理解一个关键点

如果 Nginx 和后端都在 Docker Compose 同一个网络里,Nginx 应该通过服务名访问后端,而不是写宿主机 IP。

例如:

nginx
location / {
    proxy_pass http://app:3000;
}

这里的 app,就是 Compose 里的服务名。

Compose 示例

yaml
services:
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

  app:
    image: myapp:latest
    expose:
      - "3000"

注意:

  • ports 是对外暴露到宿主机
  • expose 是容器网络内可见
  • 同网络容器之间通信用服务名,不靠 localhost

排查方法

进入 Nginx 容器:

bash
docker exec -it nginx sh

然后测试:

bash
curl http://app:3000

如果这里都访问不到,问题就在容器网络或后端服务本身,不在浏览器,不在域名,也不在玄学。

十、常见问题 8:镜像拉取失败、构建失败

拉取失败常见报错

  • pull access denied
  • manifest not found
  • unauthorized
  • TLS handshake timeout
  • connection refused

常见原因

  • 镜像名写错
  • tag 不存在
  • 私有仓库没登录
  • 网络不通
  • DNS 有问题
  • 仓库限流

排查思路

1. 先确认镜像名和 tag

bash
docker pull nginx:latest
docker pull mysql:8.0

不要想当然写一个不存在的 tag。

2. 私有仓库先登录

bash
docker login

或者登录你的镜像仓库地址。

3. 检查 DNS

bash
cat /etc/resolv.conf

必要时给 Docker daemon 配 DNS。

构建失败常见原因

  • COPY 路径不存在
  • .dockerignore 把关键文件排除了
  • 构建上下文错了
  • 基础镜像拉不下来
  • 某一步安装依赖失败

构建时别只看最后一行报错,要往上翻,很多真正原因藏在中间。

十一、常见问题 9:容器之间互相访问失败

典型误区

容器 A 去访问容器 B,结果写成:

http://127.0.0.1:8080

这是大坑。

在容器里,127.0.0.1 指的是当前这个容器自己,不是别的容器,也不是宿主机。

正确写法

如果 Compose 服务名叫 api,端口是 8080:

http://api:8080

排查命令

查看网络:

bash
docker network ls

查看容器是否加入某网络:

bash
docker inspect 容器名

如果两个容器不在同一个 bridge 网络里,互访自然失败。

十二、常见问题 10:磁盘满了,Docker 开始全面发疯

这是线上高危问题。

典型现象

  • 容器突然异常
  • 日志写不进去
  • 数据库报错
  • 镜像构建失败
  • 拉镜像失败
  • 系统卡顿

先看磁盘

bash
df -h

再看 Docker 占用:

bash
docker system df

常见占用大户

  • 无用镜像
  • 停掉但没删的容器
  • 旧卷
  • 构建缓存
  • JSON 日志文件

清理命令

bash
docker system prune -a

清理前请谨慎,这会删除未使用资源。

如果只是清理悬空资源:

bash
docker image prune
docker container prune
docker volume prune

日志爆仓问题

Docker 默认 json-file 日志驱动,如果不限制,日志能把盘撑爆。

推荐加日志轮转:

yaml
services:
  app:
    image: myapp:latest
    logging:
      options:
        max-size: "10m"
        max-file: "3"

这一条很重要,别等磁盘炸了才后悔。

十三、常见问题 11:升级后服务挂了,回滚一脸狼狈

Docker 部署不能只有"上线",没有"回滚"。

推荐做法

1. 不要永远只用 latest

坏习惯:

yaml
image: myapp:latest

推荐:

yaml
image: myapp:1.2.3

2. 保留上一版镜像

新版本出问题,直接切回旧 tag。

3. 配置和数据分离

镜像升级不应该顺手把数据干没了。

4. 上线前先本地验证

bash
docker compose config

先看最终渲染配置有没有问题。

十四、常见问题 12:系统架构不匹配,镜像跑不起来

这几年这个坑越来越常见,尤其是 Apple Silicon 机器。

常见现象

  • 镜像拉下来了,但运行异常
  • 某些依赖安装报错
  • 提示 exec format error

原因

你的机器架构和镜像架构不匹配,比如:

  • 宿主机是 arm64
  • 镜像只有 amd64

排查

查看宿主机架构:

bash
uname -m

查看镜像支持的平台时,可以去镜像仓库页面确认。

解决办法

必要时显式指定平台:

yaml
services:
  app:
    image: some-image:latest
    platform: linux/amd64

但要注意,跨架构运行可能有性能损耗,也可能带来兼容问题。 生产环境最好还是用匹配架构的镜像。

十五、最稳的一套 Docker Compose 生产基础模板

下面给你一份更稳的基础模板,适合多数 Web 应用参考:

yaml
version: "3.9"

services:
  app:
    image: myapp:1.0.0
    container_name: myapp
    restart: unless-stopped
    env_file:
      - .env
    environment:
      - NODE_ENV=production
      - PORT=3000
    ports:
      - "8080:3000"
    volumes:
      - ./data:/app/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 5
    logging:
      options:
        max-size: "10m"
        max-file: "3"

  nginx:
    image: nginx:1.27
    container_name: mynginx
    restart: unless-stopped
    depends_on:
      - app
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
      - ./nginx/certs:/etc/nginx/certs:ro
    logging:
      options:
        max-size: "10m"
        max-file: "3"

这份模板有几个关键点:

  • 用明确版本号,不赌 latest
  • 有 restart 策略
  • 有 healthcheck
  • 有日志轮转
  • 数据目录单独挂载
  • 配置文件只读挂载,降低误改风险

十六、Docker 部署故障排查的万能命令清单

真出问题时,优先把下面这组命令打一遍:

bash
docker ps -a
docker compose ps
docker logs --tail 200 -f 容器名
docker compose logs -f
docker inspect 容器名
docker exec -it 容器名 sh
ss -ltnp
df -h
docker system df
docker network ls

如果你是线上环境,我建议你按下面这个顺序执行:

bash
docker ps -a
docker logs --tail 200 容器名
docker inspect 容器名
ss -ltnp
df -h

这五步,足够先抓出大方向。

十七、上线前自检清单

每次 Docker 部署前,过一遍这张清单,能少踩很多坑:

镜像层

  • [ ] 镜像名和 tag 是否正确
  • [ ] Dockerfile 是否本地构建验证过
  • [ ] 架构是否匹配

容器层

  • [ ] 启动命令是否正确
  • [ ] 环境变量是否完整
  • [ ] 健康检查是否配置
  • [ ] 是否设置自动重启

网络层

  • [ ] 端口映射是否冲突
  • [ ] 服务是否监听 0.0.0.0
  • [ ] 容器间调用是否使用服务名
  • [ ] 防火墙和安全组是否放行

数据层

  • [ ] 卷挂载路径是否正确
  • [ ] 目录权限是否匹配
  • [ ] 配置文件是否真的被挂载进容器
  • [ ] 日志是否做轮转
  • [ ] 磁盘空间是否充足

十八、结语:Docker 真正难的,不是命令,而是排查逻辑

很多人觉得 Docker 难,是因为每次出问题都像在开盲盒。 其实不是 Docker 神秘,而是排查没顺序。

记住一句最实用的话:

先确认容器活没活,再确认服务通没通,再确认端口和网络对不对,最后再看配置和数据。

别一出问题就改配置,别一看到 502 就甩锅 Nginx,别一访问不到就怀疑云厂商。

Docker 部署里最可怕的不是报错,而是你根本不知道该先查哪里。

只要顺着这条线排:

容器状态 → 日志 → 端口 → 网络 → 挂载 → 权限 → 磁盘

大部分问题,都能很快定位。

FAQ:几个高频问题补充

1. docker compose up -d 成功了,是不是就代表部署成功?

不是。 这只代表容器启动流程执行了,不代表应用已经 ready,也不代表外部一定能访问。

2. 容器里访问 localhost 为什么打不通别的服务?

因为容器里的 localhost 只指向当前容器自己。 容器互访应该用服务名。

3. 容器重启后数据没了,是 Docker 不稳定吗?

通常不是。 大概率是你没挂卷,或者数据写进了容器层,容器删除后自然没了。

4. 为什么我本机能访问,外网访问不到?

优先查:

  • 应用是否监听 0.0.0.0
  • 宿主机防火墙
  • 云服务器安全组
  • 端口是否映射正确

5. 为什么 Docker 越跑越占磁盘?

常见原因是:

  • 老镜像没清
  • 旧容器没删
  • 构建缓存堆积
  • 日志没做轮转

问题求助

没能解决你的问题?直接问我

如果你遇到任何技术问题无法解决,可以在这里提交求助。我会尽快查看并回复你。

支持作者

如果这篇文章帮到了你,可以支持我

扫码打赏,支持我持续更新原创排障文章。

打赏二维码