Docker 重启后卡在 Loading containers:一次 firewalld 阻塞排查
记录一次服务器重启后 Docker 无法由 systemd 正常拉起的排查过程,从残留网络引用一路定位到 dockerd 初始化 libnetwork 时被 firewalld 的 D-Bus 调用阻塞。
服务器重启后,/home/ac/ui/auth 下的 Docker Compose 服务没有自动恢复。
这次故障一开始看起来像 Docker 自身启动异常,后来又暴露出旧容器引用了不存在的 Docker network。清理残留后,业务虽然可以通过手动 dockerd 临时恢复,但一旦切回 systemd 托管,Docker 仍然卡在 Loading containers: start.。
最后通过 dockerd 的 goroutine 堆栈确认,真正阻塞 systemd 启动链路的是:
dockerd -> libnetwork -> iptables -> firewalld -> D-Bus
最终判断
这不是磁盘、inode、overlay2 或普通 containerd 目录残留导致的问题。核心原因是 Docker 18.06.3-ce 初始化网络时调用 firewalld 的 D-Bus 接口长期不返回,导致 daemon 无法完成启动。
现场现象:Docker API 不可用
业务容器是:
common-authentication-frontend
使用的镜像是:
nginx:1.25
容器预期监听端口为:
0.0.0.0:80->80/tcp
0.0.0.0:443->443/tcp
服务器重启后,在业务目录执行:
docker-compose up -d
直接报错:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
查看 Docker 服务状态:
sudo systemctl status docker --no-pager -l
一开始看到 Docker 长时间停在:
Active: deactivating (stop-sigterm)
后续再次通过 systemd 启动,又长期停在:
Active: activating (start)
Loading containers: start.
这个阶段 dockerd 和 docker-containerd 进程都存在,但 Docker API 不可用。下面两个命令都会卡住:
sudo docker version
sudo docker info
第一层问题:旧容器引用了缺失的 network
为了绕开 systemd,先手动前台启动 Docker:
sudo dockerd --debug
这时可以看到 Docker daemon 能完成一部分初始化,但加载旧容器时出现网络引用异常:
Failed to start container 82dc0b19b9d6...: network e7b97d72f2ca... not found
这说明 Docker 的旧容器元数据还引用着一个已经不存在的 network。
先清理残留容器和 Compose 网络:
展开代码
sudo docker container prune -f
sudo docker network prune -f
sudo docker system prune -f
sudo docker stop common-authentication-frontend 2>/dev/null
sudo docker rm common-authentication-frontend 2>/dev/null
sudo docker network rm auth_default 2>/dev/null然后临时用手动后台方式启动 Docker:
sudo dockerd > /tmp/dockerd.log 2>&1 &
再重新拉起业务:
sudo docker-compose up -d
sudo docker-compose ps
业务容器成功恢复:
common-authentication-frontend nginx:1.25 Up 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
到这里可以确定:业务可以通过手动 dockerd 临时恢复,旧容器和旧网络引用也确实参与了第一阶段故障。
第二层问题:systemd 托管仍然启动失败
临时恢复业务后,还需要把 Docker 切回 systemd 托管,否则下次服务器重启后仍然没有可靠的自动恢复能力。
当时查看进程:
sudo ps aux | grep -E "dockerd|containerd" | grep -v grep
可以看到存在手动启动链路:
sudo dockerd
dockerd
docker-containerd
docker-containerd-shim
而 systemd 中的 Docker 服务状态是:
Active: failed
Loaded: loaded (/etc/systemd/system/docker.service; disabled)
于是停止业务、清理手动进程和 socket,再尝试恢复 systemd 托管:
展开代码
sudo docker-compose down
sudo pkill dockerd
sudo pkill containerd
sudo rm -f /var/run/docker.pid /var/run/docker.sock
sudo rm -f /run/docker.pid /run/docker.sock
sudo systemctl reset-failed docker
sudo systemctl daemon-reload
sudo systemctl enable docker
sudo systemctl start docker结果 systemctl start docker 再次卡住:
Active: activating (start)
Loading containers: start.
这说明第一层的旧容器和旧网络残留清掉以后,systemd 启动链路里还有另一个阻塞点。
排除过的方向
为了避免误判,先排除了几个常见原因。
磁盘空间和 inode
检查磁盘空间与 inode:
df -h
df -i
结果显示根分区和 inode 使用率都很低,因此不是磁盘空间或 inode 耗尽。
容器目录残留
检查 Docker 容器目录:
sudo ls -la /var/lib/docker/containers
结果目录已经为空:
total 0
drwx------. 2 root root 6 May 11 18:40 .
drwx--x--x. 15 root root 200 May 11 18:46 ..
Docker network 本地数据库
也清理过 Docker network 的本地状态:
sudo rm -f /var/lib/docker/network/files/local-kv.db
sudo rm -rf /run/docker/libnetwork
sudo rm -rf /var/run/docker/libnetwork
再检查:
sudo find /var/lib/docker/network -maxdepth 3 -type f -o -type d
仅剩基础目录:
/var/lib/docker/network
/var/lib/docker/network/files
Docker 挂载残留
检查挂载:
mount | grep docker
没有输出,说明没有 Docker mount 残留。
containerd 运行时目录
还曾备份并清理 containerd 相关目录:
sudo mv /var/lib/docker/containerd /var/lib/docker/containerd.bak.20260511191905
sudo rm -rf /var/run/docker/containerd
sudo rm -rf /run/docker/containerd
但 systemd 启动 Docker 仍然卡在:
Loading containers: start.
所以问题不在普通的 containerd 运行时目录残留。
关键证据:dockerd 卡在 firewalld 的 D-Bus 调用
当 Docker 卡住时,向 dockerd 发送 USR1 信号:
sudo kill -USR1 <dockerd_pid>
Docker 会生成 goroutine 堆栈文件:
/var/run/docker/goroutine-stacks-2026-05-11T193633+0800.log
查看主协程堆栈:
sudo head -n 220 /var/run/docker/goroutine-stacks-2026-05-11T193633+0800.log
关键内容如下:
展开代码
goroutine 1 [chan receive, 16 minutes]:
github.com/docker/docker/vendor/github.com/godbus/dbus.(*Object).Call
github.com/docker/docker/vendor/github.com/docker/libnetwork/iptables.checkRunning
github.com/docker/docker/vendor/github.com/docker/libnetwork/iptables.FirewalldInit
github.com/docker/docker/vendor/github.com/docker/libnetwork/iptables.initFirewalld
github.com/docker/docker/vendor/github.com/docker/libnetwork/iptables.initDependencies
github.com/docker/docker/vendor/github.com/docker/libnetwork/drivers/bridge.removeIPChains
github.com/docker/docker/vendor/github.com/docker/libnetwork/drivers/bridge.(*driver).configure
github.com/docker/docker/vendor/github.com/docker/libnetwork/drivers/bridge.Init
github.com/docker/docker/vendor/github.com/docker/libnetwork.New
github.com/docker/docker/daemon.(*Daemon).initNetworkController
github.com/docker/docker/daemon.(*Daemon).restore
github.com/docker/docker/daemon.NewDaemon
main.(*DaemonCli).start这段堆栈把问题指向了 Docker 初始化网络控制器的过程。
更具体地说,dockerd 在初始化 libnetwork、bridge 和 iptables 时,会通过 D-Bus 查询 firewalld 状态。这个调用没有返回,导致 Docker daemon 无法完成初始化,systemd 也就一直看到服务处于启动中。
根因判断
这次问题分成两个层次。
第一层是服务器重启后,Docker 自动恢复旧容器时遇到缺失的 network:
network e7b97d72... not found
这一层通过清理旧容器、旧 network 和 Compose 网络后可以绕过去。
第二层是切回 systemd 托管时,Docker 仍然卡在启动阶段。最终定位为:
Docker 18.06.3-ce 在初始化 libnetwork/iptables 时通过 D-Bus 调用 firewalld,调用长期阻塞,导致 dockerd 卡在 Loading containers: start.
所以后续 systemd 启动失败的核心问题不是容器目录、镜像层、overlay2、containerd,也不是磁盘空间,而是 firewalld 与 Docker 网络初始化链路之间的阻塞。
临时恢复业务的方案
由于继续修复 systemd/firewalld 链路会拉长恢复时间,当时选择先回到手动部署方式恢复业务。
操作步骤如下:
展开代码
sudo systemctl kill --kill-who=all --signal=SIGKILL docker
sudo pkill -9 dockerd 2>/dev/null
sudo pkill -9 containerd 2>/dev/null
sudo pkill -9 containerd-shim 2>/dev/null
sudo systemctl stop firewalld
sudo rm -f /var/run/docker.pid /var/run/docker.sock
sudo rm -f /run/docker.pid /run/docker.sock
sudo rm -rf /var/run/docker/containerd
sudo rm -rf /run/docker/containerd
sudo dockerd > /tmp/dockerd.log 2>&1 &
sleep 10
sudo docker version
sudo docker ps -a
sudo docker network ls
cd /home/ac/ui/auth
sudo docker-compose up -d
sudo docker-compose ps预期结果是容器重新处于 Up 状态:
common-authentication-frontend Up 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
临时 systemd 配置变更
排查过程中,为了防止 systemd 在强杀 dockerd 后自动拉起并再次卡住,临时增加了 drop-in:
/etc/systemd/system/docker.service.d/no-restart.conf
内容是:
[Service]
Restart=no
这个配置会覆盖原 Docker service 中的:
Restart=always
如果继续使用手动启动 Docker,可以暂时保留该文件,避免 systemd 干扰临时恢复。
如果后续要恢复 systemd 托管,需要删除这个临时覆盖:
sudo rm -f /etc/systemd/system/docker.service.d/no-restart.conf
sudo systemctl daemon-reload
sudo systemctl show docker -p Restart
期望输出恢复为:
Restart=always
后续彻底修复思路
后续如果要恢复服务器重启后的自动拉起能力,建议在业务低峰期专项处理 firewalld、D-Bus 与 Docker 的兼容问题。
先检查 D-Bus 和 firewalld 状态:
sudo systemctl status dbus --no-pager -l
sudo systemctl status firewalld --no-pager -l
sudo busctl list | grep -i firewalld
sudo firewall-cmd --state
再在停机窗口测试停用 firewalld 后,systemd 是否可以正常启动 Docker:
sudo systemctl stop firewalld
sudo systemctl restart docker
sudo systemctl status docker --no-pager -l
sudo docker version
如果停用 firewalld 后 Docker 能正常启动,问题就基本集中在 firewalld 与 D-Bus 集成上。
后续可以重点评估三件事:
- 修复或禁用 firewalld 链路:确认
firewalld和 D-Bus 状态是否正常,必要时在维护窗口内禁用firewalld,改用明确的iptables规则管理。 - 升级 Docker:当前版本是
Docker 18.06.3-ce,版本较老,libnetwork与firewalld相关兼容问题风险更高。 - 恢复 systemd 托管前做完整验证:确认 Docker 服务、Docker API、网络列表和业务容器都能稳定返回。
恢复 systemd 托管前,可以按下面顺序确认:
sudo systemctl start docker
sudo systemctl status docker --no-pager -l
sudo docker version
sudo docker ps -a
sudo docker network ls
确认 Docker 正常后,再恢复自启:
sudo rm -f /etc/systemd/system/docker.service.d/no-restart.conf
sudo systemctl daemon-reload
sudo systemctl reset-failed docker
sudo systemctl enable docker
sudo systemctl restart docker
最后验证业务:
cd /home/ac/ui/auth
sudo docker-compose up -d
sudo docker-compose ps
curl -I http://127.0.0.1
curl -k -I https://127.0.0.1
总结
这次排查的关键不是某一个清理命令,而是区分了两个不同阶段的问题:
- 服务器重启后,旧容器元数据引用了已经不存在的 Docker network,导致自动恢复失败。
- 清理残留后,systemd 启动 Docker 仍然卡住,最终通过 goroutine 堆栈定位为
dockerd在初始化libnetwork/iptables时调用firewalld的 D-Bus 接口阻塞。
当线上业务需要先恢复时,手动启动 dockerd 是一个临时绕行方案。但真正要解决重启后的自动恢复问题,还是要回到 firewalld、D-Bus 和 Docker 版本兼容这条链路上处理。