Docker 容器重启后无法启动故障处理
服务器或 Docker 服务重启后,多个容器同时报 exec: "/bin/sh": no such file or directory,本文从现象判断、overlay2 与 containerd 排查,到 Dify 数据保全和恢复,给出完整处置流程。
1. 故障概述
1.1 故障现象
重启前容器运行正常,重启服务器或 Docker 服务后,容器无法启动:

完整报错链路为 daemon → shim task → OCI runtime → runc,最终落在:
unable to start container process:
exec: "/bin/sh": stat /bin/sh: no such file or directory同类报错还有 exec: "sh": executable file not found in $PATH 和 exec: "/bin/bash": stat /bin/bash: no such file or directory。
1.2 相关日志
Docker 日志中出现 containerd 侧的容器记录缺失:
cleanup: failed to delete container from containerd:
container "..." in namespace "moby": not found1.3 受影响环境
| 项 | 值 |
|---|---|
| 操作系统 | Linux |
| Docker 数据目录 | /data/docker |
| 存储驱动 | overlay2 |
| 部署应用 | Dify |
| 影响范围 | 多个 Dify 容器,sh、/bin/sh、/bin/bash 均不可用 |
2. 故障判断
单个容器提示 /bin/bash: no such file or directory,通常只是镜像没装 Bash(例如 Alpine 只有 /bin/sh)。但本次 sh、/bin/sh、/bin/bash 在多个不同容器中同时失效,已经不是镜像自身的问题,而是容器根文件系统没有被正确挂载出来。
可能原因:
- Docker overlay2 文件层损坏;
- 镜像层内容丢失或为空;
- Docker 与 containerd 状态不一致;
- 数据目录所在磁盘曾经写满;
- 数据目录所在文件系统异常;
- 宿主机重启时 Docker/containerd 非正常退出;
- 数据目录被清理脚本、运维工具或人工操作改动;
- 镜像元数据尚在,但实际镜像文件已损坏。
3. 注意事项
排查前先明确三条红线,否则极易把可恢复故障变成数据事故。
3.1 不要直接删除 Docker 数据目录
禁止执行:
rm -rf /data/docker该目录同时存放镜像、容器层和 named volumes,Dify 的 PostgreSQL、Redis、Weaviate 数据、用户上传文件、插件与模型数据都在其中。删掉即不可恢复。
3.2 不要直接删除 volumes
未备份前禁止执行:
docker volume prune
docker system prune -a --volumesDify 的数据库和业务数据保存在 volume 中,删除等同于清库。
3.3 不要单独移动 overlay2 目录
不要在 Docker 运行时执行 mv /data/docker/overlay2 ...。镜像元数据、容器配置与 overlay2 层三者相互引用,只搬走其中一份会导致:
- 镜像元数据与实际层不一致;
- 容器无法删除;
- 镜像无法重新拉取;
- Docker 与 containerd 状态进一步错乱。
确认存储目录严重损坏时,应走第 9 节的“备份数据卷后整体重建”流程。
4. 故障排查
4.1 确认数据目录与存储驱动
docker info | grep -iE 'Docker Root Dir|Storage Driver'预期输出:
Docker Root Dir: /data/docker
Storage Driver: overlay2再确认目录本身非空:
sudo ls -la /data/docker
sudo ls -la /data/docker/overlay2 | head
sudo ls -la /data/docker/volumes | head4.2 检查磁盘与 inode
df -h /data
df -i /data重点关注磁盘使用率或 inode 是否已达 100%、/data 是否正常挂载、所在磁盘是否被更换。磁盘或 inode 写满是镜像层损坏最常见的诱因,若已占满需先清理空间再继续。
4.3 检查容器日志体积
sudo du -sh /data/docker/containers/*/*-json.log 2>/dev/null \
| sort -rh | head -20日志过大时清空(只清空内容,不影响容器):
sudo find /data/docker/containers \
-name '*-json.log' \
-type f \
-exec truncate -s 0 {} \;4.4 检查文件系统异常
sudo dmesg -T | grep -iE \
'xfs|ext4|i/o error|readonly|remount|corrupt|overlay' \
| tail -50同时检查服务日志:
sudo journalctl -u docker --since "2 hours ago" --no-pager
sudo journalctl -u containerd --since "2 hours ago" --no-pager关注这些关键字:I/O error、Read-only file system、No space left on device、overlay、failed to mount、snapshot、container not found。
4.5 检查容器状态
docker ps -a查看镜像与启动命令:
docker inspect 容器名 \
--format 'Image={{.Config.Image}} Path={{.Path}} Args={{json .Args}}'查看容器对应的存储层:
docker inspect 容器名 \
--format '{{json .GraphDriver.Data}}'4.6 验证镜像是否完整
绕过容器配置,直接用镜像起一个临时容器列目录:
docker image ls
docker run --rm --entrypoint /bin/ls langgenius/dify-api:latest /镜像正常时应能看到完整根目录:
bin dev etc home lib proc root run tmp usr var若命令无法执行,或根目录为空,说明镜像层已经损坏——这是区分“容器配置问题”和“存储层问题”的关键一步。
5. Docker 清理方案
如果排查结论是磁盘空间不足,按影响面从小到大清理。
5.1 查看空间占用
docker system df -v5.2 清空容器日志
同 4.3,收益最大且无副作用,优先执行。
5.3 清理已停止的容器
docker container prune5.4 清理悬空镜像
docker image prune5.5 清理构建缓存
docker builder prune # 仅清理未使用缓存
docker builder prune -a # 清理全部构建缓存5.6 清理无用网络
docker network prune5.7 清理无用镜像(谨慎)
docker image prune -a该命令会删除所有当前未被容器使用的镜像,包括本地构建、暂未启用的镜像,执行前确认这些镜像都能重新拉取或重新构建。
6. Dify 恢复方案
6.1 备份 Dify 配置
进入 Dify 项目目录(按实际路径调整):
cd /root/dify
sudo cp -a .env .env.bak.$(date +%F-%H%M%S)
sudo cp -a docker-compose.yaml \
docker-compose.yaml.bak.$(date +%F-%H%M%S)若使用了 docker-compose.middleware.yaml、docker-compose.prod.yaml 等额外文件,一并备份。
6.2 备份 Docker volumes
先确认卷列表和实际路径:
docker volume ls
docker volume inspect 卷名再整体备份:
sudo cp -a /data/docker/volumes \
/data/docker/volumes.bak.$(date +%F-%H%M%S)空间不足时不要强行复制整个目录,但至少要单独备份 PostgreSQL 数据。
6.3 停止 Dify 容器
cd /root/dify
docker compose down --remove-orphansdocker compose down 不会删除 named volumes。切记不要加 -v 参数,也不要顺手执行 docker volume prune,这两个操作都会连同数据一起清掉。
6.4 清理残留容器
docker ps -a
docker rm -f 容器ID # 删除确认无用的单个容器
docker container prune # 删除所有已停止的容器7. 重新拉取并重建容器
确认只是镜像内容异常时,删除镜像重新拉取即可。先列出 Dify 实际使用的镜像:
docker compose config --images删除、重拉、强制重建:
docker rmi -f 镜像名
docker compose pull
docker compose up -d --force-recreate确认结果:
docker compose ps
docker compose logs -f --tail=1008. 重拉镜像后仍失败
如果重新拉取后仍然出现 exec: "sh": executable file not found in $PATH,或多个镜像都起不来,问题就不在镜像,而在存储层或文件系统。
8.1 确认 /data 挂载正确
findmnt /data
lsblk -f
df -hT /data重点确认重启后 /data 没有挂到错误的磁盘或空目录上——挂载漂移会让 Docker 面对一个“看起来存在、实际上是空的”数据目录。
8.2 检查 overlay2 层内容
sudo du -sh /data/docker/overlay2
sudo find /data/docker/overlay2 -maxdepth 2 -type d -name diff | head抽查若干 diff 目录的实际内容:
sudo find /data/docker/overlay2 \
-maxdepth 2 \
-type d \
-name diff \
-exec sh -c 'echo "DIR: $1"; ls -la "$1" | head' sh {} \;大量 diff 目录为空,即可确认镜像层内容已经丢失。
8.3 检查 XFS 的 ftype 参数
/data 使用 XFS 时:
sudo xfs_info /data | grep -o 'ftype=[0-9]'overlay2 要求 ftype=1。若输出 ftype=0,该文件系统不支持 overlay2,需要重新格式化或迁移数据目录到符合要求的文件系统。
9. 严重损坏时的重建方案
同时满足以下条件时,可以考虑重建 Docker 的镜像与容器存储:
- 多个镜像都无法启动;
/bin/sh、/bin/bash均不存在;- overlay2 层内容为空或损坏;
- Docker 日志显示 containerd 状态异常;
- 磁盘与文件系统检查已完成;
- Dify 数据卷已完成备份。
9.1 执行顺序
核心原则是:不删除整个 /data/docker,而是整体改名保留现场。
- 备份 Dify 配置;
- 备份 Docker volumes;
- 确认数据库备份可用;
- 停止 Docker;
- 旧数据目录整体改名;
- 初始化新数据目录;
- 重新拉取镜像;
- 恢复或重新挂载数据卷。
停止服务并确认进程已退出:
sudo systemctl stop docker
sudo systemctl stop containerd
ps -ef | grep -E 'dockerd|containerd' | grep -v grep改名旧目录并新建:
sudo mv /data/docker /data/docker.bak.$(date +%F-%H%M%S)
sudo mkdir -p /data/docker确认 /etc/docker/daemon.json 中数据目录仍指向原路径:
{
"data-root": "/data/docker"
}启动并校验:
sudo systemctl start containerd
sudo systemctl start docker
docker info | grep -iE 'Docker Root Dir|Storage Driver'重新拉取并启动 Dify:
cd /root/dify
docker compose pull
docker compose up -d该方案会重建全部镜像和容器。旧目录中的 volumes 不会自动出现在新目录下,必须提前规划数据卷恢复方案。生产环境执行前务必完成 PostgreSQL、Redis 等数据备份。
10. 配置日志轮转
容器日志无限增长是撑满磁盘、进而损坏存储层的常见起点。编辑 /etc/docker/daemon.json,在原有配置基础上合并(不要整体覆盖):
{
"data-root": "/data/docker",
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}校验格式并重启:
python3 -m json.tool /etc/docker/daemon.json
sudo systemctl restart docker该配置只对新创建的容器生效,Dify 容器重建后才会完全落地。
11. 预防措施
11.1 监控磁盘空间
对 /data 设置分级告警:80% 预警、90% 严重、95% 紧急处理。日常巡检:
df -h /data
df -i /data
docker system df注意 inode 和容量要分别监控,inode 耗尽同样会导致写入失败。
11.2 固定镜像版本
避免长期使用 image: xxx:latest,改用固定版本 image: xxx:1.2.3。生产环境可进一步锁定 digest:
image: xxx@sha256:xxxxxxxx这样镜像损坏后重新拉取,拿到的仍是同一个版本。
11.3 定期备份
至少覆盖 PostgreSQL、Redis、Weaviate、用户上传文件、Dify 配置文件和 Docker volumes,并定期验证备份可恢复。
11.4 不手动改动存储目录
/data/docker 下的 overlay2、image、containers、containerd 一律不要手工修改或删除,所有清理都通过 Docker 命令完成。
12. 处置顺序速查
# 1. 检查空间
df -h /data
df -i /data
# 2. 检查 Docker 占用
docker system df -v
# 3. 清理过大的容器日志
sudo find /data/docker/containers \
-name '*-json.log' \
-type f \
-exec truncate -s 0 {} \;
# 4. 备份 Dify 配置和数据卷
cd /root/dify
sudo cp -a .env .env.bak.$(date +%F-%H%M%S)
sudo cp -a /data/docker/volumes \
/data/docker/volumes.bak.$(date +%F-%H%M%S)
# 5. 停止并删除旧容器,但不要删除 volume
docker compose down --remove-orphans
# 6. 检查端口
sudo ss -lntp | grep ':3080'
# 7. 删除损坏的镜像
docker compose config --images
docker rmi -f 对应镜像
# 8. 重新拉取镜像
docker compose pull
# 9. 强制重新创建容器
docker compose up -d --force-recreate
# 10. 查看状态
docker compose ps
docker compose logs -f --tail=100若重拉后仍无法启动,不要重复执行 docker compose up,转去检查底层:
df -h /data
df -i /data
sudo dmesg -T | tail -100
sudo journalctl -u docker --since "today"
sudo journalctl -u containerd --since "today"排查重点是:磁盘空间、文件系统、overlay2 层、containerd 状态和数据目录挂载情况。