UOS/openEuler 上 Docker 重启后启动失败:firewalld 与 bridge 网络冲突排查
最近在一台基于 UOS Server 20 / openEuler 体系的节点上遇到一个 Docker 启动问题:手工修复后 Docker 可以启动,systemctl restart docker 也正常,但机器真实重启后,docker.service 又会失败。
这个问题不是磁盘空间、inode、overlay2 数据目录损坏导致的,而是 firewalld、ip6tables 和 Docker bridge 网络恢复之间的冲突。本文记录这次问题的现象、排查过程和最终修复方案。
环境背景
节点环境大致如下:
Operating System: UnionTech OS Server 20
Kernel: 4.19.90-2305.1.0.0199.56.uel20.x86_64
Docker: 27.3.0
Storage Driver: overlay2
Backing Filesystem: xfs
firewalld: enabled
部署过程中曾经清空过 Docker 数据目录,目标是让 Docker 恢复到干净状态,然后重新部署业务容器。
故障现象
重启后 Docker 服务无法启动:
sudo systemctl status docker --no-pager -l
输出中只能看到 systemd 层面的失败:
Active: failed (Result: exit-code)
docker.service: Start request repeated too quickly.
Failed to start Docker Application Container Engine.
继续查看 Docker 日志:
sudo journalctl -u docker --no-pager -n 80
核心报错如下:
failed to start daemon: Error initializing network controller:
error creating default "bridge" network:
Failed to program NAT chain: COMMAND_FAILED:
'/usr/sbin/ip6tables-restore -w -n' failed:
ip6tables-restore v1.8.5 (legacy): goto 'PRE_docker' is not a chain
在多次启动失败后,又会变成另一个错误:
cannot create network ... (docker0):
conflicts with network ... (docker0):
networks have same bridge name
这两个错误看起来像两个问题,但实际上是同一条故障链路上的不同阶段。
排查过程
1. 排除资源问题
先检查磁盘、inode、内存和 Docker 数据目录大小:
df -h
df -i
free -h
sudo du -sh /var/lib/docker
结果显示:
- 根分区空间充足。
- inode 充足。
- 内存充足。
/var/lib/docker数据目录大小正常。
所以问题不在资源耗尽。
2. 检查 firewalld 与 docker0 绑定状态
继续检查 firewalld:
sudo systemctl is-active firewalld
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=docker --list-all
sudo firewall-cmd --zone=trusted --list-all
ip addr show docker0
早期失败时可以看到 docker0 被绑定到了非预期 zone,例如 trusted:
trusted (active)
interfaces: docker0
Docker 启动时会尝试把 docker0 交给自己的 docker zone 管理。如果 docker0 已经被 firewalld 绑定到其他 zone,就会出现类似错误:
ZONE_CONFLICT: 'docker0' already bound to a zone
这一步说明 Docker 与 firewalld 已经在 bridge 接口管理上发生冲突。
3. 修复 zone 后出现 ip6tables 链缺失
把 docker0 从错误 zone 中移除并 reload 后,Docker 可以暂时启动:
sudo firewall-cmd --zone=trusted --remove-interface=docker0 || true
sudo firewall-cmd --reload
sudo systemctl reset-failed docker
sudo systemctl start docker
但真实重启后又失败,日志变为:
ip6tables-restore v1.8.5 (legacy): goto 'PRE_docker' is not a chain
这说明开机阶段 Docker 在恢复 bridge 网络时,firewalld 的 IPv6 NAT 链还没有处于 Docker 期望的状态,导致 Docker 网络控制器初始化失败。
4. 多次失败导致 Docker network DB 异常
继续查看 Docker 网络数据库:
sudo strings /var/lib/docker/network/files/local-kv.db | grep -E 'docker0|inDelete|network/v1.0/network'
可以看到旧的默认 bridge 记录处于异常状态:
"name":"bridge"
"BridgeName":"docker0"
"inDelete":true
这就是后续 networks have same bridge name 的来源:
- 旧的
docker0网络记录仍在local-kv.db中。 - 该记录处于删除中的异常状态。
- Docker 再次启动时试图创建新的
docker0。 - 新旧记录使用同一个 bridge name,于是启动失败。
根因分析
这个问题不是单点配置错误,而是几个因素叠加后的结果。
1. firewalld 与 Docker 都会管理 iptables
Docker 需要创建 DOCKER NAT 链,用于容器端口映射、bridge NAT 和转发规则。
firewalld 也会维护 zone、iptables/ip6tables 链和 bridge 接口归属。两者同时管理网络规则时,如果启动顺序或运行态状态不一致,就容易出现:
docker0被 firewalld 绑定到错误 zone。- Docker 创建
dockerzone 或绑定接口时发生ZONE_CONFLICT。 - firewalld IPv6 NAT 链缺失,Docker 调用
ip6tables-restore失败。
2. Docker 启动失败会留下中间状态
Docker 网络初始化不是一个“失败后完全回滚”的过程。某次启动失败可能已经写入了部分网络状态:
docker0bridge 已经创建。br-*自定义网络 bridge 已经创建。local-kv.db里写入了 network 记录。- iptables 中留下了部分
DOCKER链。
这些中间状态会影响下一次启动,使错误从 ip6tables 链缺失变成 docker0 bridge name 冲突。
3. 禁用 firewalld 不等于禁用 iptables
这里容易误解:
iptables是底层 netfilter 规则接口。firewalld是上层动态防火墙管理器。- Docker 自己也会直接写 iptables 规则。
禁用 firewalld 后,iptables 仍然可以正常使用,Docker 仍然会写入 DOCKER 链。
解决方案
如果节点不依赖 firewalld 管理复杂安全策略,可以采用更稳定的方式:停用 firewalld,让 Docker 直接管理 iptables,并清理已经损坏的 Docker 网络状态。
1. 停止 Docker 与 firewalld
sudo systemctl stop docker || true
sudo systemctl reset-failed docker || true
sudo systemctl disable --now firewalld || true
确认状态:
systemctl is-active firewalld || true
systemctl is-enabled firewalld || true
期望输出:
inactive
disabled
2. 配置 Docker 不使用 IPv6 iptables
当前场景没有使用 Docker IPv6 网络,因此关闭 Docker 对 ip6tables 的编程,避免开机时继续撞上 PRE_docker 链缺失问题。
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
"ip6tables": false
}
EOF
3. 清理 Docker network DB
先备份,再删除异常网络数据库:
sudo mkdir -p /var/lib/docker/network/files
if [ -f /var/lib/docker/network/files/local-kv.db ]; then
sudo cp -a /var/lib/docker/network/files/local-kv.db \
/var/lib/docker/network/files/local-kv.db.bak.$(date +%F-%H%M%S)
fi
sudo rm -f /var/lib/docker/network/files/local-kv.db
WARNING
删除 local-kv.db 会让 Docker 重新生成网络数据库。对于已经部署了复杂自定义 Docker 网络的节点,需要确认这些网络可以重建。
4. 删除残留 bridge 设备
for i in docker0 $(ip -o link show type bridge | awk -F': ' '{print $2}' | cut -d@ -f1 | grep '^br-' || true); do
sudo ip link set "$i" down 2>/dev/null || true
sudo ip link delete "$i" type bridge 2>/dev/null || true
done
5. 清理旧 DOCKER iptables 链
sudo iptables -t nat -D PREROUTING -m addrtype --dst-type LOCAL -j DOCKER 2>/dev/null || true
sudo iptables -t nat -D OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER 2>/dev/null || true
sudo iptables -t nat -F DOCKER 2>/dev/null || true
sudo iptables -t nat -X DOCKER 2>/dev/null || true
sudo iptables -t filter -F DOCKER 2>/dev/null || true
sudo iptables -t filter -X DOCKER 2>/dev/null || true
sudo iptables -t filter -F DOCKER-ISOLATION-STAGE-1 2>/dev/null || true
sudo iptables -t filter -X DOCKER-ISOLATION-STAGE-1 2>/dev/null || true
sudo iptables -t filter -F DOCKER-ISOLATION-STAGE-2 2>/dev/null || true
sudo iptables -t filter -X DOCKER-ISOLATION-STAGE-2 2>/dev/null || true
6. 启动 Docker 并重建业务网络
sudo systemctl enable docker
sudo systemctl start docker
sudo docker network create fastone-network 2>/dev/null || true
验证 Docker 本地重启:
sudo systemctl restart docker
sudo systemctl is-active docker
sudo docker network ls
确认 Docker 仍然正常写入 iptables:
sudo iptables -t nat -S | grep DOCKER
示例输出:
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER -i docker0 -j RETURN
-A DOCKER -i br-xxxxxxxxxxxx -j RETURN
重启验证
这类问题必须做真实重启验证,仅执行 systemctl restart docker 不够。
sudo reboot
机器回来后检查:
sudo systemctl is-active docker
sudo systemctl is-enabled docker
sudo systemctl is-active firewalld || true
sudo systemctl is-enabled firewalld || true
sudo docker network ls
sudo iptables -t nat -S | grep DOCKER
期望状态:
docker: active / enabled
firewalld: inactive / disabled
Docker networks: bridge, host, none, fastone-network
iptables: 存在 DOCKER NAT 链
结论
这次问题的关键点是:Docker 失败并不总是来自 Docker 数据目录本身,也可能来自系统网络管理器和 Docker 网络控制器之间的状态冲突。
对于 UOS/openEuler 这类环境,如果没有明确依赖 firewalld 管理防火墙策略,让 Docker 直接管理 iptables 会更稳定。修复时需要同时处理三类状态:
firewalld是否仍在管理 Docker bridge。/var/lib/docker/network/files/local-kv.db是否已经被失败启动写坏。- 系统里是否残留
docker0、br-*和旧的DOCKERiptables 链。
只处理其中一项,可能会出现“当下能启动,重启后又失败”的情况。