OA0 = Omni AI 0
OA0 是一个探索 AI 的论坛
现在注册
已注册用户请  登录
OA0  ›  技能包  ›  container-debug:调试运行中的 Docker 容器与 Docker Compose 编排

container-debug:调试运行中的 Docker 容器与 Docker Compose 编排

 
  backup ·  2026-02-04 17:56:01 · 3 次点击  · 0 条评论  

名称: container-debug
描述: 调试正在运行的 Docker 容器和 Compose 服务。适用于检查容器日志、进入运行中的容器、诊断网络问题、检查资源使用情况、调试多阶段构建、排查健康检查问题或修复 Compose 服务依赖关系。
元数据: {"clawdbot":{"emoji":"🐳","requires":{"bins":["docker"]},"os":["linux","darwin","win32"]}}


容器调试

调试正在运行的 Docker 容器和 Compose 服务。涵盖日志查看、命令执行、网络检查、资源监控、多阶段构建、健康检查以及常见故障模式。

适用场景

  • 容器启动后立即退出或崩溃
  • 容器内应用程序的行为与宿主机上不同
  • 容器之间无法通信
  • 容器占用过多内存或 CPU
  • 多阶段 Docker 构建产生意外结果
  • 健康检查失败
  • Compose 服务启动顺序错误或无法连接

容器日志

查看与过滤日志

# 查看最后 100 行日志
docker logs --tail 100 my-container

# 实时跟踪日志流
docker logs -f my-container

# 带时间戳实时跟踪
docker logs -f -t my-container

# 查看指定时间之后的日志
docker logs --since 30m my-container
docker logs --since "2026-02-03T10:00:00" my-container

# 查看时间范围内的日志
docker logs --since 1h --until 30m my-container

# Compose:查看所有服务的日志
docker compose logs -f

# Compose:查看特定服务的日志
docker compose logs -f api db

# 将日志重定向到文件以便分析
docker logs my-container > container.log 2>&1

# 分离 stdout 和 stderr 输出
docker logs my-container > stdout.log 2> stderr.log

检查日志驱动

# 检查容器使用的日志驱动
docker inspect --format='{{.HostConfig.LogConfig.Type}}' my-container

# 如果使用 json-file 驱动,查找实际的日志文件
docker inspect --format='{{.LogPath}}' my-container

# 检查日志文件大小
ls -lh $(docker inspect --format='{{.LogPath}}' my-container)

进入容器执行命令

交互式 Shell

# Bash(最常见)
docker exec -it my-container bash

# 如果 Bash 不可用(如 Alpine、distroless 镜像)
docker exec -it my-container sh

# 以 root 用户身份执行(即使容器以非 root 用户运行)
docker exec -u root -it my-container bash

# 携带特定环境变量
docker exec -e DEBUG=1 -it my-container bash

# 运行单条命令(非交互式 Shell)
docker exec my-container cat /etc/os-release
docker exec my-container ls -la /app/
docker exec my-container env

调试已崩溃的容器

# 容器已退出?检查退出码
docker inspect --format='{{.State.ExitCode}}' my-container
docker inspect --format='{{.State.Error}}' my-container

# 常见退出码:
# 0   = 正常退出
# 1   = 应用程序错误
# 137 = 被杀死(OOM 或 docker kill)— 128 + 信号 9
# 139 = 段错误 — 128 + 信号 11
# 143 = 终止(SIGTERM)— 128 + 信号 15

# 启动已停止的容器以进行调试
docker start -ai my-container

# 或覆盖入口点以获取 Shell
docker run -it --entrypoint sh my-image

# 从已停止的容器中复制文件
docker cp my-container:/app/error.log ./error.log
docker cp my-container:/etc/nginx/nginx.conf ./nginx.conf

调试无 Shell 的容器(distroless / scratch 镜像)

# 使用 docker cp 提取文件
docker cp my-container:/app/config.json ./

# 使用 nsenter 进入容器的命名空间(Linux)
PID=$(docker inspect --format='{{.State.Pid}}' my-container)
nsenter -t $PID -m -u -i -n -p -- /bin/sh

# 将调试容器附加到相同的命名空间
docker run -it --pid=container:my-container --net=container:my-container busybox sh

# Docker Desktop:使用调试扩展
docker debug my-container

网络

检查容器网络

# 显示容器 IP 地址
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-container

# 显示所有网络详情
docker inspect -f '{{json .NetworkSettings.Networks}}' my-container | jq

# 列出所有网络
docker network ls

# 检查网络(查看所有连接的容器)
docker network inspect bridge
docker network inspect my-compose-network

# 显示端口映射
docker port my-container

测试容器间连通性

# 从容器 A 内部访问容器 B
docker exec container-a ping container-b
docker exec container-a curl http://container-b:8080/health

# 容器内的 DNS 解析
docker exec my-container nslookup db
docker exec my-container cat /etc/resolv.conf
docker exec my-container cat /etc/hosts

# 测试端口是否可达
docker exec my-container nc -zv db 5432
docker exec my-container wget -qO- http://api:3000/health

# 如果容器内没有 curl/ping,安装或使用调试容器:
docker run --rm --network container:my-container curlimages/curl curl -s http://localhost:8080

常见网络问题

# 容器间出现“连接被拒绝”
# 1. 检查应用是否绑定到 0.0.0.0,而非 127.0.0.1
docker exec my-container netstat -tlnp
# 如果监听在 127.0.0.1 — 修复应用配置

# 2. 检查容器是否在同一网络
docker inspect -f '{{json .NetworkSettings.Networks}}' container-a | jq 'keys'
docker inspect -f '{{json .NetworkSettings.Networks}}' container-b | jq 'keys'

# 3. 检查已发布端口与暴露端口
# EXPOSE 仅作声明,不发布端口
# 使用 -p 主机端口:容器端口 来发布端口

# “名称未找到” — DNS 无法解析容器名称
# 容器名称仅在用户定义的网络中可解析,默认的 bridge 网络不行
docker network create my-net
docker run --network my-net --name api my-api-image
docker run --network my-net --name db postgres
# 现在 “api” 和 “db” 可以相互解析

捕获网络流量

# 在容器内使用 tcpdump
docker exec my-container tcpdump -i eth0 -n port 8080

# 如果 tcpdump 不可用,使用 sidecar 容器
docker run --rm --net=container:my-container nicolaka/netshoot tcpdump -i eth0 -n

# netshoot 包含:tcpdump、curl、nslookup、netstat、iperf 等工具
docker run --rm --net=container:my-container nicolaka/netshoot bash

资源使用情况

实时统计

# 所有容器
docker stats

# 特定容器
docker stats api db redis

# 单次快照(非流式)
docker stats --no-stream

# 格式化输出
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"

内存调查

# 检查内存限制
docker inspect --format='{{.HostConfig.Memory}}' my-container
# 0 表示无限制

# 检查容器是否因 OOM 被杀死
docker inspect --format='{{.State.OOMKilled}}' my-container

# 内存使用细分(Linux cgroups)
docker exec my-container cat /sys/fs/cgroup/memory.current 2>/dev/null || \
docker exec my-container cat /sys/fs/cgroup/memory/memory.usage_in_bytes

# 容器内进程内存使用
docker exec my-container ps aux --sort=-%mem | head -10
docker exec my-container top -bn1

磁盘使用

# Docker 整体磁盘使用情况
docker system df
docker system df -v

# 容器文件系统大小
docker inspect --format='{{.SizeRw}}' my-container

# 查找容器内的大文件
docker exec my-container du -sh /* 2>/dev/null | sort -rh | head -10
docker exec my-container find /tmp -size +10M -type f

# 检查日志文件是否过大
docker exec my-container ls -lh /var/log/

Dockerfile 调试

多阶段构建调试

# 构建到特定阶段
docker build --target builder -t my-app:builder .

# 检查构建器阶段的内容
docker run --rm -it my-app:builder sh
docker run --rm my-app:builder ls -la /app/
docker run --rm my-app:builder cat /app/package.json

# 检查哪些文件进入了最终镜像
docker run --rm my-image ls -laR /app/

# 无缓存构建(全新构建)
docker build --no-cache -t my-app .

# 构建时显示详细进度输出
docker build --progress=plain -t my-app .

镜像检查

# 显示镜像层(每层大小)
docker history my-image
docker history --no-trunc my-image

# 检查镜像配置(入口点、命令、环境变量、端口)
docker inspect my-image | jq '.[0].Config | {Cmd, Entrypoint, Env, ExposedPorts, WorkingDir}'

# 比较两个镜像
docker history image-a --format "{{.Size}}\t{{.CreatedBy}}" > layers-a.txt
docker history image-b --format "{{.Size}}\t{{.CreatedBy}}" > layers-b.txt
diff layers-a.txt layers-b.txt

# 查找构建间的变化
docker diff my-container
# A = 添加,C = 更改,D = 删除

健康检查

定义与调试健康检查

# 在 Dockerfile 中
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1
# 检查健康状态
docker inspect --format='{{.State.Health.Status}}' my-container
# "healthy"、"unhealthy" 或 "starting"

# 查看健康检查日志(最近 5 次结果)
docker inspect --format='{{json .State.Health}}' my-container | jq

# 手动运行健康检查
docker exec my-container curl -f http://localhost:8080/health

# 运行时覆盖健康检查
docker run --health-cmd "curl -f http://localhost:8080/health || exit 1" \
           --health-interval 10s my-image

# 禁用健康检查
docker run --no-healthcheck my-image

Docker Compose 调试

服务启动问题

# 检查服务状态
docker compose ps

# 查看服务失败原因
docker compose logs failed-service

# 启动时显示详细输出
docker compose up --build 2>&1 | tee compose.log

# 启动单个服务(包含依赖)
docker compose up db

# 启动时不包含依赖服务
docker compose up --no-deps api

# 从头重新创建容器
docker compose up --force-recreate --build

# 检查生效的配置(变量替换后)
docker compose config

服务依赖与启动顺序

# docker-compose.yml
services:
  api:
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

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

  redis:
    image: redis:7
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5
# 在运行命令前等待服务变为健康状态
docker compose up -d db
docker compose exec db pg_isready  # 轮询直到就绪
docker compose up -d api

清理

# 移除已停止的容器
docker container prune

# 移除未使用的镜像
docker image prune

# 移除所有未使用的资源(容器、镜像、网络、卷)
docker system prune -a

# 同时移除卷(警告:会删除数据)
docker system prune -a --volumes

# 移除悬空的构建缓存
docker builder prune

提示

  • docker logs -f 是首要检查项。大多数容器故障在日志中可见。
  • 退出码 137 表示因 OOM 被杀死。增加内存限制或修复内存泄漏。
  • 容器内的应用必须绑定到 0.0.0.0,而非 127.0.0.1。容器内的 localhost 是隔离的。
  • 容器名称仅在用户定义的网络中通过 DNS 解析,默认的 bridge 网络不行。多容器设置应始终创建自定义网络。
  • docker exec 仅适用于运行中的容器。对于已崩溃的容器,使用 docker cp 提取日志或用 docker run --entrypoint sh 覆盖入口点。
  • nicolaka/netshoot 是容器网络调试的瑞士军刀,预装了所有网络工具。
  • 构建时使用 --progress=plain 显示完整的命令输出,这对调试构建失败至关重要。
  • 带有 start-period 的健康检查可防止应用启动缓慢时出现错误的“不健康”状态。
3 次点击  ∙  0 人收藏  
登录后收藏  
0 条回复
About   ·   Help   ·    
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
Developed with Cursor