目录

一、容器退出状态码速查

二、根因一:程序启动就崩(Exit 1)

三、根因二:内存不够被 OOM Kill(Exit 137)

四、根因三:Entrypoint/CMD 配置错误

五、根因四:健康检查失败导致重启

六、根因五:依赖服务没启动

七、根因六:文件权限问题

八、排查流程图

九、预防方案

十、排查速查表


一、容器退出状态码速查

容器一直在 Restarting 状态,第一步看退出码:

docker ps -a

# 输出关键列:
CONTAINER ID   STATUS
abc123         Restarting (1) 5 seconds ago      ← Exit 1
def456         Restarting (137) 3 seconds ago    ← OOM Killed
ghi789         Restarting (255) 10 seconds ago   ← 入口点错误
退出码含义常见原因频率
0正常退出程序执行完就退了(非服务型程序)偶尔
1程序内部错误代码报错、配置文件错误、数据库连不上最常见
125Docker 自身错误daemon 出问题罕见
126命令不可执行权限问题、二进制不兼容偶尔
127命令找不到路径写错、镜像里没装这个命令常见
137被 SIGKILL内存超限被 OOM Kill常见
139段错误程序 crash(C/C++ 空指针等)偶尔
255未知错误入口点脚本退出码异常常见

二、根因一:程序启动就崩(Exit 1)

这是最常见的。程序代码有问题,或者配置不对。排查命令:

# 看容器日志(最重要的一步)
docker logs --tail 100 abc123

# 常见报错:
# 1. 配置文件找不到
FileNotFoundError: [Errno 2] No such file or directory: '/app/config.yaml'

# 2. 数据库连不上
sqlalchemy.exc.OperationalError: connection refused

# 3. 端口被占用
OSError: [Errno 98] Address already in use

# 4. 环境变量缺失
KeyError: 'DATABASE_URL'

# 实时看日志(边重启边看)
docker logs -f abc123

解决方法取决于具体报错,但通用排查思路:

报错类型排查命令解决方向
文件找不到docker exec abc123 ls -la /app/检查 Dockerfile COPY 路径、volume 挂载
连接被拒绝docker exec abc123 ping db_host检查网络、depends_on、启动顺序
端口占用lsof -i :8080换端口或杀掉占用进程
环境变量缺失docker inspect abc123 | grep Envdocker-compose.yml 里加 environment

三、根因二:内存不够被 OOM Kill(Exit 137)

退出码 137 几乎 100% 是 OOM。Linux 内核发现容器内存超过限制,直接 SIGKILL 杀掉进程。

# 确认是否 OOM
docker inspect abc123 | grep OOMKilled

# 输出:
"OOMKilled": true  ← 确认了

# 看内存限制
docker inspect abc123 | grep Memory

# 看 dmesg 里内核日志
dmesg | grep -i "killed process"

# 输出示例:
[1234567.89] Killed process 1234 (python) total-vm:2048000kB, anon-rss:512000kB

解决方案——要么加内存限制,要么优化程序:

# 方法1:加大内存限制(docker-compose.yml)
services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          memory: 2G    # 从 512M 改到 2G
    restart: unless-stopped

# 方法2:优化程序内存使用
# Python 示例:用生成器代替列表
# 坏的写法(一次加载全部数据)
data = [json.loads(line) for line in open('big_file.json')]
# 好的写法(逐行处理)
def read_json_lines(path):
    with open(path) as f:
        for line in f:
            yield json.loads(line)

# JVM 应用加内存参数
java -Xms512m -Xmx1536m -jar app.jar

注意:容器内存限制不要设成跟宿主机一样大。Docker 自身、其他容器、操作系统都需要内存。建议容器内存 < 宿主机内存的 70%。

四、根因三:Entrypoint/CMD 配置错误

这是新手最容易犯的错。Dockerfile 里 Entrypoint 或 CMD 写法不对:

# 常见错误1:shell 格式导致信号传递失败
CMD "python app.py"          # ❌ 会被解析成 shell -c "python app.py"
CMD ["python", "app.py"]     # ✅ exec 格式,直接执行

# 常见错误2:entrypoint 脚本没有执行权限
COPY entrypoint.sh /app/
RUN chmod +x /app/entrypoint.sh  # ❌ 忘了这行!
ENTRYPOINT ["/app/entrypoint.sh"]

# 常见错误3:entrypoint 脚本最后一行少了 exec
#!/bin/bash
echo "Starting..."
python migrate.py
python app.py  # ❌ 这样 app.py 的 PID 不是 1,收不到 SIGTERM
exec python app.py  # ✅ exec 替换当前进程,app.py 成为 PID 1

排查命令:

# 查看镜像的 Entrypoint 和 CMD
docker inspect myapp:latest | jq '.[0].Config.Entrypoint, .[0].Config.Cmd'

# 临时覆盖 entrypoint 进容器排查
docker run -it --entrypoint /bin/sh myapp:latest

五、根因四:健康检查失败导致重启

程序在跑,但健康检查一直失败,Docker 认为容器不健康。如果配了 restart: on-failure,就会被重启。

# 看健康检查状态
docker inspect abc123 | jq '.[0].State.Health'

# 输出示例:
{
  "Status": "unhealthy",
  "FailingStreak": 5,
  "Log": [
    {
      "ExitCode": 1,
      "Output": "curl: (7) Failed to connect to localhost:8080"
    }
  ]
}

排查方向:

健康检查方式常见失败原因解决方法
curl HTTP 接口端口不对、路径不对、服务还没启动完加 start_period 宽限时间
tcp 端口检查端口没监听检查程序绑定的是 0.0.0.0 还是 127.0.0.1
自定义脚本脚本权限、依赖缺失容器内手动跑脚本看报错
# docker-compose.yml 健康检查配置
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 40s  # ← 启动后 40 秒内失败不算,给程序启动时间

六、根因五:依赖服务没启动

容器 A 依赖数据库,但数据库还没起来,A 启动就报连接失败然后退出,被 restart: always 拉起来,又失败……无限循环。

# docker-compose.yml 的 depends_on 不会等依赖"就绪",只等"启动"
services:
  app:
    depends_on:
      - db    # ← 这只保证 db 先启动,不保证 db 已经 ready
    restart: always

# 解决:用 healthcheck + depends_on condition
services:
  app:
    depends_on:
      db:
        condition: service_healthy  # ← 等 db 健康检查通过才启动
    restart: always
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10

如果不用 docker-compose,程序里可以加重试逻辑:

# Python 示例:等待数据库就绪
import time
import psycopg2

def wait_for_db(max_retries=30, interval=2):
    for i in range(max_retries):
        try:
            conn = psycopg2.connect(
                host=os.getenv('DB_HOST', 'localhost'),
                port=5432,
                dbname=os.getenv('DB_NAME'),
                user=os.getenv('DB_USER'),
                password=os.getenv('DB_PASS')
            )
            conn.close()
            print("Database is ready!")
            return
        except psycopg2.OperationalError:
            print(f"Waiting for database... ({i+1}/{max_retries})")
            time.sleep(interval)
    raise Exception("Database not available after retries")

wait_for_db()  # 启动前先等

七、根因六:文件权限问题

容器里用非 root 用户跑,但挂载的 volume 文件权限不对,程序读写不了直接退出。

# 看报错日志
docker logs abc123
# PermissionError: [Errno 13] Permission denied: '/data/cache.db'

# 检查容器内文件权限
docker exec abc123 ls -la /data/
# -rw-r--r-- 1 root root 4096 cache.db  ← 属于 root,但容器跑的用户是 appuser

# 解决方法1:Dockerfile 里改权限
COPY --chown=appuser:appuser ./data /data
RUN chown -R appuser:appuser /data

# 解决方法2:启动时改
docker run -v $(pwd)/data:/data --user $(id -u):$(id -g) myapp

# 解决方法3:宿主机改权限
sudo chown -R 1000:1000 ./data  # 1000 是容器内 appuser 的 UID

八、排查流程图

Docker 容器重启排查流程容器一直 Restarting看退出码 docker psExit 1: 看日志Exit 137: OOMExit 127: 命令不存在unhealthy: 健康检查docker logs看具体报错加内存限制或优化程序检查 DockerfileCMD/ENTRYPOINT加 start_period检查端口/路径修复 → 验证 → restart: unless-stopped

九、预防方案

预防措施怎么做避免什么问题
用 exec 格式 CMDCMD ["python", "app.py"]信号传递失败、进程不退出
entrypoint 脚本加 exec最后一行 exec "$@"PID 1 问题、优雅停机失败
设合理内存限制compose 里 memory: 2GOOM Kill
加健康检查 + start_periodstart_period: 40s启动慢被误判为不健康
依赖用 conditioncondition: service_healthy依赖服务没就绪
用 unless-stoppedrestart: unless-stopped避免无限重启(区别于 always)
非 root 运行 + 正确权限Dockerfile 里 USER appuser权限拒绝报错

十、排查速查表

步骤命令看什么
1. 看状态docker ps -a退出码(Exit Code)
2. 看日志docker logs --tail 100 容器名具体报错信息
3. 看是否 OOMdocker inspect 容器名 | grep OOMKilledOOMKilled: true
4. 看健康检查docker inspect 容器名 | jq .[0].State.HealthFailingStreak + Log
5. 看入口配置docker inspect 镜像名 | jq .[0].ConfigEntrypoint + Cmd
6. 进容器docker run -it --entrypoint /bin/sh 镜像名手动跑命令看报错

一句话总结:容器重启问题 90% 看两样东西——退出码 + 日志。退出码告诉你方向,日志告诉你细节。docker logs 是你最好的朋友。

更多推荐