第 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 |
| 137 | OOM Killed | 内存不足 | 增加内存限制 |
| 139 | Segmentation 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: host | host 模式下不参与默认网络 |
问题 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 参数 |
| 未声明顶级 volumes | 在 volumes: 中声明 |
| 绑定挂载目录被清空 | 使用命名卷 |
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 errors | YAML 语法错误 | docker compose config 验证 |
variable is not set | 变量未定义 | 检查 .env 和 environment |
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 是排查的起点 |
| 进入容器调试 | exec 和 run --rm 是最有效的调试手段 |
| 配置验证 | docker compose config 检查语法和变量 |
| 资源监控 | docker stats 和 docker system df |
| 网络调试 | ping、nslookup、nc 是基本工具 |
扩展阅读
上一章:第 13 章 · 监控 ← | 下一章:第 15 章 · 最佳实践 →