Skip to content
Go back

UOS/openEuler 上 Docker 重启后启动失败:firewalld 与 bridge 网络冲突排查

Edit page

UOS/openEuler 上 Docker 重启后启动失败:firewalld 与 bridge 网络冲突排查

最近在一台基于 UOS Server 20 / openEuler 体系的节点上遇到一个 Docker 启动问题:手工修复后 Docker 可以启动,systemctl restart docker 也正常,但机器真实重启后,docker.service 又会失败。

这个问题不是磁盘空间、inode、overlay2 数据目录损坏导致的,而是 firewalldip6tables 和 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

结果显示:

所以问题不在资源耗尽。

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 的来源:

根因分析

这个问题不是单点配置错误,而是几个因素叠加后的结果。

1. firewalld 与 Docker 都会管理 iptables

Docker 需要创建 DOCKER NAT 链,用于容器端口映射、bridge NAT 和转发规则。

firewalld 也会维护 zone、iptables/ip6tables 链和 bridge 接口归属。两者同时管理网络规则时,如果启动顺序或运行态状态不一致,就容易出现:

2. Docker 启动失败会留下中间状态

Docker 网络初始化不是一个“失败后完全回滚”的过程。某次启动失败可能已经写入了部分网络状态:

这些中间状态会影响下一次启动,使错误从 ip6tables 链缺失变成 docker0 bridge name 冲突。

3. 禁用 firewalld 不等于禁用 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 会更稳定。修复时需要同时处理三类状态:

  1. firewalld 是否仍在管理 Docker bridge。
  2. /var/lib/docker/network/files/local-kv.db 是否已经被失败启动写坏。
  3. 系统里是否残留 docker0br-* 和旧的 DOCKER iptables 链。

只处理其中一项,可能会出现“当下能启动,重启后又失败”的情况。


Edit page
Share this post on:

Next Post
排查 `flock` 在 NFSv3 上报 `No locks available` 而在 NFSv4 正常的问题