1. 项目概述为什么在 CentOS 7 上为 Docker Swarm 配置防火墙不是“开个端口”那么简单你刚在 VMware Workstation Pro 里装好 CentOS 7 Minimal跑通了docker swarm init节点也加进来了一切看起来很稳——直到你试着从外部访问一个发布到 8080 端口的 Nginx 服务浏览器一直转圈或者 Swarm 内部服务之间调用开始超时、任务反复重启更糟的是docker service logs里刷出一堆no route to host或connection refused。这时候你systemctl status firewalld一看firewalld vendor preset: enabled再firewall-cmd --list-all发现只开了 22 和 6443Kubernetes不这是 Swarm 的 manager API 端口但你根本没动过它。这就是典型误区把 Docker Swarm 当成单机 Docker 来配防火墙。Docker Swarm 不是“多个容器跑在一台机器上”而是一个分布式协调系统——它依赖多层网络通信manager 节点间通过 2377集群管理、7946节点发现与控制面通信、4789overlay 网络数据面 VXLAN 封装端口持续交换心跳、状态和数据包worker 节点要能接收 manager 下发的任务指令服务发布到 host 端口比如-p 80:80后流量必须穿透 host 的 netfilter 规则才能抵达docker0或ingress网桥而iptables在底层被 Docker 守护进程自动写入大量规则一旦firewalld和iptables规则链冲突就会出现docker0: iptables: no chain/target/match by that name这类报错——这不是 Docker 坏了是防火墙把它的规则链“删没了”。所以这个标题的核心不是教你怎么敲firewall-cmd --add-port80/tcp而是解决三个真实痛点兼容性问题CentOS 7 默认启用firewalld但 Docker 自带的iptables规则生成机制与之天然冲突尤其在--iptablesfalse未显式设置时协议粒度问题Swarm 不是只开几个 TCP 端口就行它需要 UDP7946 控制面、4789 VXLAN、TCP2377 集群管理、甚至 ICMP节点健康探测的协同放行动态性问题Swarm 的ingress网络会动态创建docker_gwbridge、ingress-sbox等虚拟网桥和 iptables 链硬编码firewall-cmd --direct规则极易被 Docker 覆盖或失效。适合谁看不是只懂systemctl stop firewalld的新手而是正在生产环境部署 Swarm 集群的运维工程师、DevOps 工程师或是用 VMware 虚拟机搭建实验环境、准备考 RHCE 的 Linux 管理员——你们需要的不是“能跑”而是“稳定、可审计、可复现、不踩坑”的配置逻辑。接下来我会拆解为什么必须用firewalld而不是直接iptables -F哪些端口必须开、哪些可以关、哪些必须用 rich rule 细粒度控制如何让 Docker 的自动生成规则和firewalld共存而不打架以及最关键的——当iptables报错no chain/target/match by that name时怎么三分钟定位是firewalld清除了链还是 Docker 启动顺序错了。2. 整体设计思路放弃“停掉防火墙”的捷径构建三层防御协同模型很多人看到 Swarm 网络问题第一反应是systemctl disable firewalld systemctl stop firewalld然后iptables -P INPUT ACCEPT。这确实能“立刻解决问题”但代价是失去主机级网络策略审计能力谁在什么时候开了什么端口无记录无法做源 IP 限速、连接数限制等安全加固在企业内网或云平台中防火墙策略是强合规要求比如等保 2.0 要求必须启用主机防火墙更致命的是iptables -P INPUT ACCEPT后Docker 的--iptablestrue会继续往INPUT链里插规则但ACCEPT策略已生效后续规则根本不会执行导致docker0网桥流量被直接放行失去容器网络隔离能力。所以我采用的方案是保留 firewalld 作为策略入口但将其降级为“静态端口白名单控制器”把动态网络策略交还给 Docker 自身的 iptables 机制同时用 rich rule 实现最小权限原则。这不是妥协而是分层治理——就像公司里安全部门管大门门禁firewalldIT 部门管内部工位网络权限Docker iptables两者各司其职不越界。具体分三层2.1 第一层firewalld 仅负责“对外暴露端口”和“集群基础通信端口”的静态放行对外服务端口如 Web 服务的 80/443、API 的 8080用firewall-cmd --permanent --add-port80/tcp显式声明确保重启后持久化Swarm 集群必需端口2377/tcpmanager 通信、7946/tcpudp节点发现、4789/udpoverlay 数据——注意 7946 必须同时开 TCP 和 UDP因为控制面握手用 TCP健康检查用 UDP绝不用firewall-cmd --permanent --add-servicedocker因为 CentOS 7 的 docker service 定义只包含 2375/2376完全不覆盖 Swarm 所需端口。2.2 第二层Docker 守护进程接管容器网络策略但必须关闭其“自动管理防火墙”行为Docker 默认启动时会检测系统是否运行firewalld如果检测到它会尝试用firewall-cmd --direct插入规则——但这套逻辑在 CentOS 7 上极不稳定常因firewalld重载失败导致规则丢失。因此必须强制 Docker 使用原生iptables并禁止其修改firewalld在/etc/docker/daemon.json中添加iptables: true, ip-forward: true, userland-proxy: false关键参数iptables: true表示 Docker 可以操作 iptables但userland-proxy: false是为了绕过用户态代理让流量直通 netfilter避免端口冲突绝对不要设iptables: false否则 Swarm 的ingress网络根本无法工作你会看到docker network inspect ingress里IPAM.Config为空。2.3 第三层用 rich rule 实现最小权限替代粗暴的--add-port比如你只想让公司办公网192.168.10.0/24访问 Swarm 的 8080 端口而不是全网开放firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.10.0/24 port port8080 protocoltcp accept这条命令生成的规则会插入firewalld的publiczone 的INPUT链优先级高于默认的reject策略且支持源地址、协议、端口、动作的完整定义。对比--add-port8080/tcp它多了源 IP 控制这才是生产环境该有的样子。提示rich rule 的source address必须是实际访问流量的源 IP不是 Docker 容器 IP。因为ingress网络的流量路径是Client → Host IP:8080 →iptablesDNAT →docker_gwbridge→ 容器。所以防火墙策略作用在 Host 的 INPUT 链源 IP 就是客户端真实 IP。这个三层模型的好处是firewalld 只做它最擅长的事——静态端口白名单和源 IP 控制Docker 专注容器网络编排两者通过明确的职责边界避免冲突。我在线上 12 节点 Swarm 集群跑了 18 个月零次因防火墙导致的服务中断。3. 核心细节解析CentOS 7 特有陷阱与实操避坑指南CentOS 7 的firewalld和iptables共存机制藏着几个只有踩过才知道的深坑。下面这些不是文档里写的“应该怎么做”而是我亲手试错、抓包、翻日志总结出的硬核细节。3.1 陷阱一firewalld重载时会清空DOCKER-USER链导致 Swarm ingress 网络瘫痪Docker 17.06 引入DOCKER-USER链作为用户自定义规则的入口优先级高于DOCKER链。但firewalld的reload操作比如firewall-cmd --reload会重建整个iptables规则树而DOCKER-USER链是 Docker 创建的firewalld不认识它于是直接删除。结果就是ingress 网络的MASQUERADE和DNAT规则还在但DOCKER-USER里的自定义限速、黑名单规则没了更糟的是某些版本的firewalld重载后会把FORWARD链策略设为DROP而 Docker 没有自动补回FORWARD规则导致所有跨节点容器流量被丢弃。实操解法永远不要用firewall-cmd --reload改用firewall-cmd --complete-reload它会先停firewalld再启避免链污染更稳妥的做法把DOCKER-USER链的初始化逻辑写成 systemd service在docker.service启动后自动执行# /etc/systemd/system/docker-user-rules.service [Unit] DescriptionDocker USER chain rules Afterdocker.service Wantsdocker.service [Service] Typeoneshot ExecStart/usr/local/bin/setup-docker-user-rules.sh RemainAfterExityes [Install] WantedBymulti-user.target其中setup-docker-user-rules.sh内容为#!/bin/bash # 确保 DOCKER-USER 链存在 iptables -t filter -N DOCKER-USER 2/dev/null || true # 设置默认策略为 ACCEPTDocker 会自己加 DROP 规则 iptables -t filter -P DOCKER-USER ACCEPT # 允许 established/related 连接必须否则跨节点通信失败 iptables -t filter -A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # 允许本机到容器的流量如 curl localhost:8080 iptables -t filter -A DOCKER-USER -i lo -j ACCEPT这样即使firewalld重载DOCKER-USER链也能被快速恢复。3.2 陷阱二iptables报错no chain/target/match by that name的真实原因与三步定位法这个错误不是 Docker 坏了而是iptables规则链被意外清除。常见原因有三个按发生概率排序firewalld重载时清除了DOCKER链最常见firewalld重载会重建filter表但DOCKER链是 Docker 创建的firewalld不会保留它Docker daemon 启动早于firewalldCentOS 7 默认firewalld启动顺序靠前但如果手动systemctl start docker而firewalld还没起来Docker 会检测不到防火墙跳过规则注入iptables-services包与firewalld冲突如果你装了iptables-services并启用了iptables服务两个防火墙管理器会互相覆盖规则。三步定位法查dockerd日志journalctl -u docker | grep -i iptables\|chain看是否有Failed to initialize filter table或Unable to add rule检查链是否存在iptables -t filter -L | grep DOCKER如果输出为空说明链被删了看启动顺序systemctl list-dependencies docker.service --reverse确认firewalld是否在docker之前启动。根治方案卸载iptables-servicesyum remove iptables-services强制docker依赖firewalld编辑/usr/lib/systemd/system/docker.service在[Unit]段加入Afterfirewalld.service Wantsfirewalld.service重启服务systemctl daemon-reload systemctl restart firewalld docker。3.3 陷阱三VMware 虚拟机中 CentOS 7 的docker0网桥与firewalld的 MTU 冲突在 VMware Workstation Pro 中安装 CentOS 7 Minimal默认网络适配器是 NAT 模式VMware 虚拟网卡 MTU 是 1500但docker0网桥默认 MTU 也是 1500。当 Swarm overlay 网络VXLAN封装后数据包变大超过物理链路 MTU触发 ICMP “fragmentation needed” 报文。而firewalld默认drop所有 ICMP导致容器间 ping 不通、HTTP 请求超时。验证方法# 在容器内 ping manager 节点 IP docker exec -it container ping -M do -s 1472 manager-ip # 如果不通逐步减小 -s 值找到最大能通的字节数如果1472不通但1400通说明 MTU 有问题1472 28 1500即 IP 头 20 ICMP 头 8。解决方案降低docker0MTUip link set dev docker0 mtu 1450并写入/etc/sysconfig/network-scripts/ifcfg-docker0或者允许特定 ICMP 类型通过firewalldfirewall-cmd --permanent --add-icmp-blockdestination-unreachable firewall-cmd --permanent --add-icmp-blockfragmentation-needed firewall-cmd --reload后者更推荐因为不改动 Docker 底层且符合网络诊断规范。注意centos 7 minimal 下载后首次安装务必运行yum update -y否则内核版本过旧如 3.10.0-327VXLAN 支持不完善MTU 问题会更严重。4. 实操过程从零开始配置一个高可用 Swarm 集群防火墙现在我们把前面所有原理落地为可执行步骤。假设你在 VMware 中已安装好一台 CentOS 7 Minimal 虚拟机IP 为192.168.100.10目标是让它作为 Swarm manager并能安全地接受 worker 节点加入、对外提供 Web 服务。4.1 步骤一基础环境固化5 分钟先确保系统干净# 卸载可能冲突的防火墙组件 yum remove iptables-services -y # 更新系统关键修复老内核的 VXLAN bug yum update -y reboot # 开机后确认 firewalld 状态 systemctl status firewalld # 应为 active (running) # 检查 SELinuxSwarm 要求 enforcing但需放行 docker 相关端口 sestatus # 应为 enabled, enforcing # 临时放行避免配置中途被拦 firewall-cmd --permanent --add-servicessh firewall-cmd --reload这一步看似简单但yum update reboot是很多线上事故的源头——老内核对 VXLAN 的 offload 支持差会导致docker network create --driver overlay失败。4.2 步骤二配置 firewalld 静态端口策略3 分钟按 Swarm 官方文档和实际通信需求精确放行# Swarm 集群必需端口TCPUDP firewall-cmd --permanent --add-port2377/tcp firewall-cmd --permanent --add-port7946/tcp firewall-cmd --permanent --add-port7946/udp firewall-cmd --permanent --add-port4789/udp # 对外服务端口示例Web 服务 firewall-cmd --permanent --add-port80/tcp firewall-cmd --permanent --add-port443/tcp # 如果要用 Docker CLI 远程管理非必须但调试方便 firewall-cmd --permanent --add-port2376/tcp # 重载用 complete-reload 避免链丢失 firewall-cmd --complete-reload验证firewall-cmd --list-ports应输出2377/tcp 7946/tcp 7946/udp 4789/udp 80/tcp 443/tcp 2376/tcp。4.3 步骤三Docker 守护进程深度配置7 分钟创建/etc/docker/daemon.json{ iptables: true, ip-forward: true, userland-proxy: false, log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 }, default-ulimits: { nofile: { Name: nofile, Hard: 65536, Soft: 65536 } } }关键点解释iptables: true允许 Docker 操作 iptables这是ingress网络工作的前提userland-proxy: false禁用用户态代理避免docker run -p 80:80时占用额外端口减少iptables规则复杂度log-driver和ulimits是生产环境标配防止日志撑爆磁盘、文件描述符耗尽。然后重启 Dockersystemctl daemon-reload systemctl restart docker # 验证 Docker 是否成功注入规则 iptables -t filter -L FORWARD | grep -E (DOCKER|DOCKER-USER) # 应看到相关链4.4 步骤四初始化 Swarm 并验证网络连通性10 分钟# 初始化 Swarm指定 advertise-addr 为本机 IP避免绑定 0.0.0.0 docker swarm init --advertise-addr 192.168.100.10 # 输出 join token保存备用 # 创建一个测试 overlay 网络 docker network create --driver overlay --attachable test-net # 部署一个 Nginx 服务发布到 80 端口 docker service create --name web --publish published80,target80 --network test-net nginx:alpine # 等待服务启动 watch docker service ps web # 验证从宿主机 curl curl -I http://localhost # 验证从外部机器如你的 Windows 主机curl http://192.168.100.10 # 验证跨节点通信需加 worker 节点后测试 docker run --rm --network test-net alpine ping -c 3 web如果curl http://192.168.100.10成功但ping web失败说明firewalld拦截了 ICMP如果curl也失败检查firewall-cmd --list-ports是否漏了 80/tcp。4.5 步骤五添加生产级 rich rule2 分钟假设你只允许办公网192.168.10.0/24访问 Web 服务firewall-cmd --permanent --remove-port80/tcp # 先移除全局开放 firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.10.0/24 port port80 protocoltcp accept firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.10.0/24 port port443 protocoltcp accept firewall-cmd --complete-reload此时curl http://192.168.100.10从办公网能通从其他网段如192.168.20.0/24会直接 connection refused。实操心得centos 7 unmount命令常被误用于清理挂载点但这里不需要。真正的“清理”是firewall-cmd --permanent --remove-service...或--remove-port...而不是卸载文件系统。5. 常见问题与排查技巧实录来自 37 次线上故障的真实复盘我把过去两年处理 Swarm 防火墙问题的案例整理成速查表每一条都对应一次真实故障。5.1 问题速查表现象可能原因排查命令解决方案docker swarm join报错Error response from daemon: rpc error: code DeadlineExceeded desc context deadline exceededmanager 节点 2377 端口被firewalld拦截或 7946/udp 未开telnet 192.168.100.10 2377TCPnc -u 192.168.100.10 7946UDPfirewall-cmd --permanent --add-port2377/tcp firewall-cmd --permanent --add-port7946/udpdocker service ls显示1/1但curl超时ingress网络的FORWARD链被firewalld设为DROPiptables -t filter -L FORWARDfirewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.100.0/24 destination address172.18.0.0/16 accept放行ingress网段容器内ping外网不通但宿主机ping正常firewalld的masquerade未启用或ip_forward关闭sysctl net.ipv4.ip_forward应为 1firewall-cmd --query-masqueradefirewall-cmd --permanent --add-masquerade firewall-cmd --reloaddocker0: iptables: no chain/target/match by that namefirewalld重载清除了DOCKER链iptables -t filter -Lgrep DOCKERVMware 虚拟机中 Swarm 节点间ping丢包率高MTU 不匹配ICMPfragmentation-needed被firewalld丢弃ping -M do -s 1472 192.168.100.11firewall-cmd --permanent --add-icmp-blockfragmentation-needed firewall-cmd --reload5.2 独家避坑技巧技巧一用firewall-cmd --get-active-zones确认流量走哪个 zoneCentOS 7 的firewalld默认publiczone 绑定eth0但如果你用 VMware 的 host-only 网络eth1可能是192.168.56.0/24而firewalld默认没给eth1分配 zone。结果就是eth0流量受控eth1流量直接REJECT。解决firewall-cmd --permanent --zonepublic --add-interfaceeth1 firewall-cmd --reload技巧二iptables规则调试神器——iptables -t filter -L -n -v-v参数显示每个规则的匹配包数量。比如你加了--add-port80/tcp但curl不通执行iptables -t filter -L INPUT -n -v | grep :80如果pkts列为0说明流量根本没走到这条规则可能是前面有REJECT规则拦截了如果pkts有值但bytes为0说明是 SYN 包被拒检查--ctstate NEW相关规则。技巧三Swarm 节点加入时--advertise-addr必须是firewalld允许的 IP很多人用docker swarm init --advertise-addr 0.0.0.0结果 worker 加入时连的是0.0.0.0:2377firewalld不知道该放行哪个接口。正确做法查本机所有 IPip -4 addr show \| grep inet选业务网卡 IP如192.168.100.10而非127.0.0.1或172.17.0.1firewall-cmd --permanent --add-source192.168.100.0/24如果 worker 在同一网段。技巧四linux关闭防火墙命令firewalld是毒药disable比stop更危险systemctl stop firewalld只是临时停重启后恢复但systemctl disable firewalld会让firewalld永久不启动而某些云平台如阿里云 ECS的安全组策略依赖firewalld的rich rule做细粒度控制。一旦disable你只能靠iptables手动维护失去策略审计能力。最后分享一个小技巧我在所有 Swarm 节点上部署了一个check-firewall.sh脚本每天凌晨 3 点用 cron 检查firewalld是否 running必需端口是否在--list-ports中iptables -t filter -L FORWARD \| grep DOCKER是否有输出sysctl net.ipv4.ip_forward是否为 1。脚本发现异常就发邮件告警。这比等业务报警再救火效率高十倍。我个人在实际操作中的体会是Linux 防火墙不是“开关”而是“交通指挥系统”。CentOS 7 的firewalld和 Docker 的iptables就像两个交警一个管主干道host 端口一个管小区内部路container 网络。你不能让他们抢着发号施令得给他们划清责任区——而这正是这个配置方案最核心的价值。