强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Docker Compose 完全指南 / 第 14 章 · 故障排查:常见问题与调试技巧

第 14 章 · 故障排查

14.1 系统化排查思路

面对 Compose 问题时,遵循以下排查流程:

1. 复现问题
   └─→ 2. 收集信息
        └─→ 3. 定位根因
             └─→ 4. 验证修复
                  └─→ 5. 预防复发

信息收集清单

# 1. 服务状态
docker compose ps -a

# 2. 日志(最重要!)
docker compose logs --tail 200 <service>
docker compose logs --since 10m <service>

# 3. 容器详情
docker inspect <container_name>

# 4. 资源使用
docker stats --no-stream

# 5. 网络信息
docker network ls
docker network inspect <network_name>

# 6. 卷信息
docker volume ls
docker volume inspect <volume_name>

# 7. 系统资源
df -h          # 磁盘
free -h        # 内存
docker system df  # Docker 磁盘使用

14.2 启动失败类问题

问题 1:容器立即退出 (Exit Code)

docker compose ps -a
# NAME         STATUS                  EXIT CODE
# app-1        Exited (1) 5 seconds    1

排查步骤:

# 查看退出日志
docker compose logs app

# 如果没有日志输出,检查 entrypoint
docker compose run --rm app sh -c "cat /proc/1/cmdline | tr '\0' ' '"

# 进入容器调试
docker compose run --rm --entrypoint sh app

常见原因及解决:

退出码含义常见原因解决方案
0正常退出一次性任务完成正常行为
1应用错误代码异常、配置错误查看日志
126权限不足文件不可执行chmod +x
127命令不存在entrypoint/cmd 拼写错误检查 Dockerfile
137OOM Killed内存不足增加内存限制
139Segmentation Fault程序段错误检查应用代码

问题 2:端口被占用

Error response from daemon: driver failed programming external connectivity
on endpoint web: Bind for 0.0.0.0:8080 failed: port is already allocated
# 查找占用端口的进程
sudo lsof -i :8080
sudo ss -tlnp | grep 8080

# 方案 1:停止占用进程
sudo kill <PID>

# 方案 2:更换端口
# compose.yaml 中改为 "8081:80"

# 方案 3:停止占用该端口的 Docker 容器
docker ps -a --filter "publish=8080"
docker stop <container_id>

问题 3:镜像拉取失败

Error response from daemon: manifest for myapp:latest not found
# 检查镜像是否存在
docker pull myapp:latest

# 检查镜像名是否正确
docker compose config | grep image

# 检查网络连接
curl -I https://registry-1.docker.io/v2/

# 使用代理
# /etc/docker/daemon.json
{
  "proxies": {
    "http-proxy": "http://proxy:3128",
    "https-proxy": "http://proxy:3128"
  }
}

问题 4:depends_on 不生效

# 应用启动时报数据库连不上
# Error: Connection refused to db:5432

解决:使用 healthcheck + condition

services:
  app:
    depends_on:
      db:
        condition: service_healthy   # 而非 service_started

  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 10

14.3 网络类问题

问题 5:服务间无法通信

# 从 app 容器无法访问 db
docker compose exec app ping db
# ping: bad address 'db'

排查步骤:

# 1. 检查是否在同一网络
docker compose ps
docker network ls
docker network inspect <project>_default

# 2. 检查服务名解析
docker compose exec app nslookup db
docker compose exec app cat /etc/hosts

# 3. 检查目标容器是否运行
docker compose ps db

# 4. 检查端口
docker compose exec app nc -zv db 5432

常见原因:

原因解决方案
不在同一网络将服务加入同一网络
服务未启动确保目标服务运行中
服务名拼写错误检查 compose.yaml 中的服务名
使用了 network_mode: hosthost 模式下不参与默认网络

问题 6:容器无法访问外部网络

docker compose exec app ping google.com
# bad address 'google.com'  (DNS 解析失败)
# 检查 DNS 配置
docker compose exec app cat /etc/resolv.conf

# 检查 Docker daemon DNS 配置
cat /etc/docker/daemon.json
# {"dns": ["8.8.8.8", "114.114.114.114"]}

# 手动设置 DNS
# compose.yaml
services:
  app:
    dns:
      - 8.8.8.8
      - 114.114.114.114

14.4 存储类问题

问题 7:权限拒绝

# Permission denied when writing to volume
web-1  | nginx: [emerg] mkdir() "/var/log/nginx" failed (13: Permission denied)

排查:

# 检查挂载目录权限
ls -la ./logs

# 检查容器内用户
docker compose exec app id
# uid=1000(appuser) gid=1000(appuser)

# 查看宿主机目录 UID
ls -ln ./logs
# drwxr-xr-x 2 0 0 4096 ...

解决:

services:
  web:
    # 方案 1:指定运行用户
    user: "1000:1000"

    # 方案 2:使用 entrypoint 修改权限
    # entrypoint: sh -c "chown -R 1000:1000 /var/log/nginx && nginx -g 'daemon off;'"

    volumes:
      - ./logs:/var/log/nginx
# 方案 3:修改宿主机目录权限
sudo chown -R 1000:1000 ./logs

问题 8:数据丢失

# 容器重建后数据消失
docker compose down && docker compose up -d
# 数据库数据没了!

原因分析:

# 检查是否使用了命名卷
docker compose config | grep volumes

# 检查卷是否存在
docker volume ls | grep <project>

常见原因及解决:

原因解决方案
使用了匿名卷声明命名卷
down -v 删除了卷避免 -v 参数
未声明顶级 volumesvolumes: 中声明
绑定挂载目录被清空使用命名卷

14.5 构建类问题

问题 9:构建上下文过大

# 构建很慢,发送了大量数据
Sending build context to Docker daemon  2.5GB
# 检查构建上下文大小
du -sh .

# 创建/更新 .dockerignore
echo "node_modules/" >> .dockerignore
echo ".git/" >> .dockerignore
echo "dist/" >> .dockerignore

问题 10:缓存不生效

# 每次构建都重新安装依赖

优化 Dockerfile 层顺序:

# ❌ 代码变化导致缓存失效
COPY . .
RUN npm install

# ✅ 先复制依赖文件
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

问题 11:多阶段构建目标错误

# 构建了错误的阶段
# Error: Cannot find module 'typescript'
services:
  app:
    build:
      context: ./app
      target: production    # 确保指定了正确的阶段

14.6 资源类问题

问题 12:磁盘空间不足

# Error response from daemon: write /var/lib/docker/...
# no space left on device
# 查看 Docker 磁盘使用详情
docker system df -v

# 清理策略(按风险递增)
docker container prune         # 清理停止的容器
docker image prune             # 清理悬挂镜像
docker volume prune            # 清理未使用的卷(⚠️ 数据可能丢失)
docker image prune -a          # 清理所有未使用的镜像
docker system prune -a         # 核弹:清理所有未使用资源

# 清理构建缓存
docker builder prune
docker builder prune -a        # 清理所有构建缓存

问题 13:容器 OOM (Out of Memory)

# 容器被 OOM Killer 终止
docker inspect --format='{{.State.OOMKilled}}' <container>
# true
# 设置内存限制
services:
  app:
    deploy:
      resources:
        limits:
          memory: 1G        # 硬限制
        reservations:
          memory: 512M      # 软保证

    # 或使用 mem_limit(独立模式)
    mem_limit: 1g

问题 14:CPU 占用过高

# 查看实时资源使用
docker stats

# 设置 CPU 限制
services:
  app:
    deploy:
      resources:
        limits:
          cpus: "2.0"       # 最多使用 2 个 CPU 核心

14.7 配置类问题

问题 15:变量替换失败

# ERROR: Missing required variable "DB_PASSWORD"
# 查看解析后的配置
docker compose config

# 检查 .env 文件
cat .env | grep DB_PASSWORD

# 检查变量是否在 shell 中设置
echo $DB_PASSWORD

# 设置默认值避免报错
# ${DB_PASSWORD:-default} 或 ${DB_PASSWORD:?必须设置此变量}

问题 16:配置文件语法错误

# ERROR: yaml: line 15: mapping values are not allowed in this context
# 验证 YAML 语法
docker compose config

# 常见 YAML 错误:
# 1. 缩进不一致(混用 tab 和空格)
# 2. 缺少冒号后的空格
# 3. 特殊字符未加引号
# 4. 列表格式错误

# 使用 yamllint 检查
pip install yamllint
yamllint docker-compose.yml

14.8 调试技巧

技巧 1:进入运行中的容器

# 使用 sh(Alpine 镜像通常没有 bash)
docker compose exec app sh

# 使用 bash
docker compose exec app bash

# 以 root 身份进入
docker compose exec --user root app bash

技巧 2:一次性调试容器

# 运行一个带调试工具的临时容器
docker compose run --rm --entrypoint sh app

# 安装调试工具(Alpine)
apk add --no-cache curl wget bind-tools netcat-openbsd

# 安装调试工具(Debian/Ubuntu)
apt-get update && apt-get install -y curl dnsutils netcat-openbsd

# 测试网络连通性
nc -zv db 5432
curl -v http://web:80/health
nslookup db
ping db

技巧 3:使用 busybox 调试网络

# 临时添加到 compose.yaml
services:
  debug:
    image: busybox:latest
    command: sleep infinity
    networks:
      - default

# 启动后进入
docker compose up -d debug
docker compose exec debug sh

# 在 debug 容器中测试
nslookup web
wget -qO- http://web:80
telnet db 5432

技巧 4:查看解析后的配置

# 完整解析后的配置
docker compose config

# 仅查看特定服务
docker compose config --format json | jq '.services.web'

# 查看所有环境变量
docker compose config --format json | jq '.. | .environment? | select(. != null)'

技巧 5:查看容器资源使用

# 实时监控
docker stats

# 只看一次
docker stats --no-stream

# 特定格式
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

技巧 6:追踪启动过程

# 前台启动(看到完整输出)
docker compose up web

# 查看启动过程中的所有事件
docker events --filter container=web

技巧 7:检查 Docker 守护进程

# Docker 守护进程状态
sudo systemctl status docker

# 守护进程日志
sudo journalctl -u docker.service --since "1 hour ago"

# Docker 信息汇总
docker info

# Docker 版本
docker version

14.9 常见错误速查表

错误信息可能原因快速解决
port is already allocated端口被占用更换端口或停止占用进程
manifest not found镜像不存在检查镜像名和标签
no space left on device磁盘满docker system prune
permission denied权限不匹配调整 user 或文件权限
network not found网络不存在docker compose up 会自动创建
OOMKilled内存不足增加 mem_limit
connection refused服务未就绪使用 healthcheck + depends_on
yaml: unmarshal errorsYAML 语法错误docker compose config 验证
variable is not set变量未定义检查 .envenvironment
context canceled操作被中断重试或检查网络
driver failed存储驱动问题检查卷配置
unhealthy健康检查失败检查 healthcheck 命令

14.10 常用调试命令速查

# ===== 状态查看 =====
docker compose ps -a                  # 所有服务状态
docker compose top                    # 进程列表
docker stats --no-stream              # 资源使用

# ===== 日志查看 =====
docker compose logs -f                # 实时日志
docker compose logs --tail 100 web    # 最后 100 行
docker compose logs --since 5m        # 最近 5 分钟

# ===== 进入容器 =====
docker compose exec app bash          # 进入容器
docker compose exec --user root app sh # root 权限进入
docker compose run --rm app sh        # 一次性容器

# ===== 网络调试 =====
docker network ls                     # 列出网络
docker network inspect <name>         # 网络详情
docker compose exec app ping db       # 连通性测试
docker compose exec app nslookup db   # DNS 解析

# ===== 卷调试 =====
docker volume ls                      # 列出卷
docker volume inspect <name>          # 卷详情

# ===== 配置验证 =====
docker compose config                 # 验证配置
docker compose config --services      # 列出服务名

# ===== 资源清理 =====
docker system df                      # 磁盘使用
docker system prune -a --volumes      # 清理所有(慎用)

14.11 小结

要点说明
排查流程状态 → 日志 → 详情 → 资源 → 网络 → 卷
最重要的是日志docker compose logs 是排查的起点
进入容器调试execrun --rm 是最有效的调试手段
配置验证docker compose config 检查语法和变量
资源监控docker statsdocker system df
网络调试pingnslookupnc 是基本工具

扩展阅读


上一章:第 13 章 · 监控 ← | 下一章:第 15 章 · 最佳实践 →