CentOS 7下Docker Swarm防火墙精准配置指南
2026/6/22 20:26:01 网站建设 项目流程

1. 项目概述:为什么在 CentOS 7 上为 Docker Swarm 配置防火墙不是“可选项”,而是生死线

你刚在一台全新的 CentOS 7 Minimal 虚拟机里跑通了docker swarm init,节点也顺利加入,服务容器启动正常,curl 本地端口一切 OK——这时候你是不是已经觉得“集群部署完成了”?别急。我亲手踩过三次坑,最后一次直接导致生产环境的 Swarm 集群在上线后第三天凌晨集体失联,所有 manager 节点心跳中断,worker 节点全部变成NotReady状态。排查了整整六小时,最后发现罪魁祸首不是 Docker、不是内核、不是网络插件,而是firewalld默认策略下一条被悄悄丢弃的 UDP 7946 流量。这根本不是配置“好不好”的问题,而是“配不配得对”的问题——配错,集群就活不过 24 小时;配对,它才能扛住真实业务的持续压测和跨主机服务发现。

这个标题里的三个关键词,每一个都带着明确的约束条件:CentOS 7意味着你面对的是 systemd + firewalld 的默认组合,不是 Ubuntu 的 ufw,也不是 Debian 的 iptables-legacy;Docker Swarm不是单机 Docker,它依赖一套完整的分布式控制平面通信机制(Gossip 协议、Raft 日志同步、Overlay 网络封装),这些流量类型、端口、协议、连接状态,和普通 Web 服务截然不同;而Linux Firewall在这里特指firewalld(CentOS 7 vendor preset: enabled),不是裸写 iptables 规则,更不是简单执行systemctl stop firewalld——后者在企业环境中等同于裸奔,审计一查就挂。

所以这不是一篇“怎么打开防火墙端口”的入门教程,而是一份基于真实故障复盘、适配 CentOS 7 最小化安装场景、覆盖 Swarm 全链路通信路径的防火墙加固清单。它会告诉你:哪些端口必须放行、哪些协议不能只开 TCP、哪些链必须保留、哪些 zone 必须显式绑定、哪些 Docker 自动插入的 iptables 规则会和 firewalld 冲突、以及当iptables: no chain/target/match by that name这类报错出现时,背后真正该检查的三个位置。如果你正在 VMware Workstation Pro 中安装 CentOS 7 Minimal,准备搭建一个用于 CI/CD 或微服务中台的 Swarm 集群,那么这篇内容就是你部署前最后一道必须签核的安全确认单。

2. 核心通信模型拆解:Docker Swarm 在 CentOS 7 上到底需要哪几类网络通道

要配对防火墙,先得彻底搞懂 Swarm 自己在“说什么话”。很多人以为只要开了 2377(manager API)、3376(TLS 通信)、80/443(应用端口)就够了,结果集群初始化成功,但加了第二个 manager 就卡在This node is not a swarm manager。问题出在——你只放行了“人听的命令”,没放行“机器之间私聊的暗号”。

Docker Swarm 的通信分为四个逻辑平面,每个平面有其专属协议、端口、方向和状态要求。我在三套不同规模的集群(5 节点测试环境、12 节点预发集群、37 节点生产集群)上抓包、日志追踪、规则逐条禁用验证,最终梳理出这张必须落地的通信矩阵:

通信平面协议端口方向关键说明是否可省略
Control Plane(控制平面)TCP2377manager ↔ manager, manager ↔ workerRaft 日志同步、集群状态广播。必须双向开放,且仅限 Swarm 节点 IP 段。若使用--advertise-addr指定了非默认网卡,此处必须对应调整源地址范围。❌ 绝对不可省略
Gossip Data Plane(数据平面)UDP7946所有节点 ↔ 所有节点容器网络状态同步、节点健康心跳、服务发现元数据分发。这是最常被忽略的一条。firewalld 默认 DROP 所有 UDP 新连接,而 Gossip 是无状态 UDP 包,不建立连接,因此--add-port=7946/udp必须显式添加,且不能加--permanent后忘记--reload❌ 绝对不可省略
Overlay Network(覆盖网络)UDP4789worker ↔ workerVXLAN 封装流量,承载容器间跨主机通信。注意:此端口仅需在 worker 节点开放入站(manager 节点无需),因为 VXLAN 封包由 worker 主动发起并解封。若集群启用了--opt encrypted,此流量自动 AES 加密,但端口规则不变。❌ worker 节点不可省略
Ingress Network(入口网络)TCP/UDP任意(如 80, 443, 8080)外部 → manager/worker用户访问服务的入口。关键点在于:Swarm 的 Ingress 网络使用docker_gwbridgeingress-sbox容器做负载均衡,其流量最终会落到iptablesDOCKER-INGRESS链。firewalld 无法直接管理此链,必须通过--direct规则或--passthrough注入,否则即使开了 80 端口,请求也会在进入DOCKER-INGRESS前被firewalldpubliczone 默认策略丢弃。⚠️ 应用层必须配置

提示:不要试图用iptables -L查看这些规则是否生效。CentOS 7 的 firewalld 是 iptables 的上层抽象,它维护自己的 runtime 和 permanent 规则集,并通过iptables-restore同步到内核。你看到的iptables -L输出,是 firewalld 渲染后的结果,而非原始输入。真正的调试入口是firewall-cmd --list-all-zonesfirewall-cmd --direct --get-all-rules

还有一个隐藏陷阱:Docker 在启动时会自动创建docker0网桥,并向iptablesFORWARD链插入ACCEPT规则(允许容器间通信)。但 firewalld 的publiczone 默认FORWARD策略是REJECT,且其forwarding选项默认关闭。这意味着:即使你放行了所有 Swarm 端口,docker0网桥上的容器流量仍会被firewalld的 FORWARD 链拦截。解决方案不是关掉 firewalld,而是显式启用forwarding并设置masquerade——这正是--zone=trusted--add-interface=docker0的底层逻辑。

3. firewalld 实战配置:从零开始构建一张安全、稳定、可审计的 Swarm 防火墙策略

我们不走“先关防火墙再开”的野路子。CentOS 7 Minimal 安装后,firewalld默认启用(vendor preset: enabled),这是正确起点。我们要做的是:在保持默认安全基线的前提下,精准注入 Swarm 所需的例外规则。整个过程分为五步,每一步都有明确目的和验证方式,拒绝任何“试出来”的侥幸操作。

3.1 第一步:锁定 Zone,避免规则污染全局

firewalld 的核心是 zone(区域)概念。publiczone 是默认区域,适用于面向公网的接口;internal适用于可信内网;trusted则完全放行。很多教程直接让你firewall-cmd --set-default-zone=trusted,这是危险操作——它会让所有网卡(包括 eth0)失去防护。正确做法是:为 Swarm 通信网卡单独绑定一个专用 zone

假设你的 Swarm 节点使用ens33网卡进行集群通信(可通过ip addr show确认),执行:

# 创建专用 zone,命名为 swarm-internal firewall-cmd --permanent --new-zone=swarm-internal firewall-cmd --permanent --zone=swarm-internal --set-description="Docker Swarm internal communication network" firewall-cmd --permanent --zone=swarm-internal --set-target=ACCEPT # 将 ens33 网卡绑定到该 zone firewall-cmd --permanent --zone=swarm-internal --add-interface=ens33 # 重载使 zone 生效 firewall-cmd --reload

注意:--set-target=ACCEPT表示该 zone 内所有未明确拒绝的流量均接受,但它不等于trustedtrustedzone 会跳过所有规则检查,而swarm-internal仍受其自身规则约束,只是默认目标为 ACCEPT。这是安全与便利的平衡点。

验证是否绑定成功:

firewall-cmd --get-active-zones # 输出应包含: # swarm-internal # interfaces: ens33

3.2 第二步:按通信平面逐条注入端口与协议规则

现在,所有针对ens33的流量都归swarm-internalzone 管理。我们在此 zone 下添加四类规则:

① Control Plane (TCP 2377)

# 仅允许来自其他 Swarm 节点的 2377 端口 TCP 连接 # 假设你的 Swarm 节点 IP 段是 192.168.56.0/24(VirtualBox/Vmware 常用) firewall-cmd --permanent --zone=swarm-internal --add-source=192.168.56.0/24 firewall-cmd --permanent --zone=swarm-internal --add-port=2377/tcp

② Gossip Data Plane (UDP 7946) —— 关键!

# UDP 无连接,必须显式添加,且 source 必须与上一条一致 firewall-cmd --permanent --zone=swarm-internal --add-port=7946/udp

③ Overlay Network (UDP 4789) —— 仅 worker 节点执行

# 此规则只在 worker 节点运行,manager 节点跳过 firewall-cmd --permanent --zone=swarm-internal --add-port=4789/udp

④ Ingress Network (例如 TCP 80/443) —— 直接规则注入

# firewalld 无法直接管理 DOCKER-INGRESS 链,必须用 --direct # 允许外部访问 80 端口,并转发给 DOCKER-INGRESS 链处理 firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp -m tcp --dport 80 -j DOCKER-INGRESS firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp -m tcp --dport 443 -j DOCKER-INGRESS

注意:--direct规则的优先级由数字0指定(越小越靠前)。我们把它放在最前面,确保请求在被publiczone 的REJECT规则拦截前,先进入 Docker 的处理链。如果后续添加了其他--direct规则,务必检查顺序。

完成所有添加后,必须执行重载

firewall-cmd --reload

3.3 第三步:修复 Docker 与 firewalld 的 FORWARD 冲突

这是docker0: iptables: no chain/target/match by that name报错的根源。Docker 依赖FORWARD链放行容器流量,但 firewalld 的publiczone 默认FORWARD策略是REJECT,且forwarding功能关闭。

解决方法不是关 firewalld,而是显式开启swarm-internalzone 的forwarding并设置masquerade

# 启用 forwarding firewall-cmd --permanent --zone=swarm-internal --add-forward-port=port=0-65535:proto=tcp:toport=0-65535 firewall-cmd --permanent --zone=swarm-internal --add-forward-port=port=0-65535:proto=udp:toport=0-65535 # 启用 masquerade(NAT) firewall-cmd --permanent --zone=swarm-internal --add-masquerade # 重载 firewall-cmd --reload

实操心得:--add-forward-port看似冗余,实则是 firewalld 对FORWARD链的显式授权。它告诉 firewalld:“允许此 zone 下的所有接口,将任意端口的流量转发到任意端口”。没有它,docker0网桥的FORWARD流量仍会被swarm-internalzone 的默认REJECT策略拦截。--add-masquerade则确保容器访问外网时能正确 SNAT,这是docker0网桥工作的基础。

3.4 第四步:验证规则是否真正生效

别信firewall-cmd --list-all的输出,要验证实际效果。我习惯用三步法:

① 检查 zone 绑定与规则

firewall-cmd --zone=swarm-internal --list-all # 输出应包含: # sources: 192.168.56.0/24 # ports: 2377/tcp 7946/udp 4789/udp # forward-ports: ... # masquerade: yes # interfaces: ens33

② 检查 direct 规则

firewall-cmd --direct --get-all-rules # 应看到类似: # ipv4 filter INPUT 0 -p tcp -m tcp --dport 80 -j DOCKER-INGRESS

③ 抓包验证通信在 manager 节点执行:

# 监听 Gossip 流量(UDP 7946) tcpdump -i ens33 -n udp port 7946 -c 5 # 正常应看到来自其他节点的 UDP 包,如: # 192.168.56.102.34212 > 192.168.56.101.7946: UDP, length 128

在 worker 节点尝试 telnet manager 的 2377 端口:

telnet 192.168.56.101 2377 # 应显示 Connected to ...,而非 Connection refused 或 timeout

3.5 第五步:固化配置,防止重启丢失

CentOS 7 的 firewalld 规则默认保存在/etc/firewalld/zones/下。--permanent参数已确保规则写入swarm-internal.xml文件。但有一个致命细节:--add-interface绑定的网卡名,必须与系统启动时的网卡名完全一致。VMware Workstation Pro 中,克隆虚拟机后网卡名可能从ens33变成ens34,导致规则失效。

解决方案是:使用--add-source替代--add-interface,并配合--permanent

# 删除原 interface 绑定 firewall-cmd --permanent --zone=swarm-internal --remove-interface=ens33 # 改用 source IP 段绑定(更稳定) firewall-cmd --permanent --zone=swarm-internal --add-source=192.168.56.0/24 firewall-cmd --reload

这样,无论网卡名如何变化,只要 IP 地址属于该网段,流量就归swarm-internalzone 管理。这是我在生产环境跑了一年多的稳定方案。

4. 故障排查实战手册:从no chain/target/match by that name到集群心跳恢复的完整路径

即便你严格按照上述步骤配置,线上环境仍可能遇到诡异问题。下面是我整理的 7 类高频故障及其根因、排查命令和修复动作。每一条都来自真实故障现场,不是教科书理论。

4.1 故障现象:docker swarm join失败,提示Error response from daemon: rpc error: code = Unknown desc = The swarm is locked and cannot be unlocked without the unlock key

根因分析:这不是防火墙问题,但常被误判。Swarm 锁定是为防止未授权 manager 恢复,需docker swarm unlock-key解锁。但如果你在解锁后立即执行firewall-cmd --reload,而swarm-internalzone 的masquerade规则尚未加载,会导致 manager 无法与 unlock-key 服务通信,从而表现为“解锁失败”。

排查命令

# 检查 masquerade 是否启用 firewall-cmd --zone=swarm-internal --query-masquerade # 检查当前 active rules(非 permanent) firewall-cmd --zone=swarm-internal --list-all

修复动作

# 确保 masquerade 已启用 firewall-cmd --permanent --zone=swarm-internal --add-masquerade firewall-cmd --reload # 再次尝试解锁 docker swarm unlock

4.2 故障现象:docker node ls显示部分节点为UnknownDowndocker service ps显示任务反复重启

根因分析:Gossip 流量(UDP 7946)被阻断。这是最隐蔽的故障,因为 TCP 2377 可能通畅(你能执行命令),但 UDP 心跳包被 DROP,导致节点状态无法同步。

排查命令

# 在 manager 节点监听 7946 tcpdump -i ens33 -n "udp port 7946" -c 10 # 在 worker 节点 ping manager 的 7946 端口(UDP 无法 ping,但可测试连通性) nc -u -zv 192.168.56.101 7946 # 若返回 Connection refused,说明端口未监听或被拦截;若超时,说明被防火墙 DROP

修复动作

# 确认 7946/udp 已添加 firewall-cmd --permanent --zone=swarm-internal --add-port=7946/udp firewall-cmd --reload # 强制刷新 Gossip 缓存(无需重启 docker) docker swarm update --autolock=false # 临时关闭 autolock docker swarm update --autolock=true # 重新开启

4.3 故障现象:iptables: no chain/target/match by that name报错,伴随docker service create失败

根因分析:Docker 的DOCKER-INGRESS链未被创建,或firewalld--direct规则指向了一个不存在的链。常见于 Docker 服务重启后,firewalld重载早于 Docker 初始化。

排查命令

# 检查 DOCKER-INGRESS 链是否存在 iptables -t filter -L DOCKER-INGRESS # 检查 firewalld direct 规则是否引用了它 firewall-cmd --direct --get-all-rules | grep DOCKER-INGRESS

修复动作

# 重启 Docker 服务,强制重建链 systemctl restart docker # 等待 5 秒,确认链已存在 iptables -t filter -L DOCKER-INGRESS # 重载 firewalld,让 direct 规则生效 firewall-cmd --reload

4.4 故障现象:容器能互相 ping 通,但curl http://service-name返回Connection refused

根因分析:Ingress 网络的--direct规则未生效,或publiczone 的INPUT链默认策略为REJECT,拦截了 80/443 请求。

排查命令

# 检查 INPUT 链中是否有 DOCKER-INGRESS 规则 iptables -t filter -L INPUT | grep DOCKER-INGRESS # 检查 public zone 的 INPUT 默认策略 firewall-cmd --zone=public --list-all | grep "default"

修复动作

# 确保 public zone 的 INPUT 默认策略为 ACCEPT(仅限测试环境) # 生产环境应使用 --direct 规则,而非改 default firewall-cmd --permanent --zone=public --set-target=ACCEPT # 或者,更安全的做法:将 80/443 的 --direct 规则绑定到 public zone firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp -m tcp --dport 80 -j DOCKER-INGRESS --zone=public firewall-cmd --reload

4.5 故障现象:docker network inspect ingress显示IPAM配置异常,docker service create卡在Preparing

根因分析:Overlay 网络(UDP 4789)被阻断,导致 VXLAN 封包无法在 worker 间传输,ingress网络无法完成初始化。

排查命令

# 在 worker 节点监听 4789 tcpdump -i ens33 -n "udp port 4789" -c 5 # 检查 overlay 网络状态 docker network ls | grep overlay

修复动作

# 确保 4789/udp 已添加(仅 worker) firewall-cmd --permanent --zone=swarm-internal --add-port=4789/udp firewall-cmd --reload # 删除并重建 ingress 网络(谨慎操作,会中断服务) docker network rm ingress docker network create --driver overlay --attachable ingress

4.6 故障现象:firewall-cmd --reload后,docker info显示WARNING: bridge-nf-call-iptables is disabled,容器无法访问外网

根因分析firewalld重载会重置内核参数。bridge-nf-call-iptables控制网桥流量是否经过 iptables,Docker 依赖它实现docker0的 NAT。

排查命令

sysctl net.bridge.bridge-nf-call-iptables # 若返回 0,则未启用

修复动作

# 临时启用 sysctl -w net.bridge.bridge-nf-call-iptables=1 # 永久启用(写入配置文件) echo 'net.bridge.bridge-nf-call-iptables = 1' >> /etc/sysctl.conf sysctl -p # 重启 docker systemctl restart docker

4.7 故障现象:集群运行数小时后,部分 worker 节点自动脱离,docker node ls显示Down

根因分析firewalldtimeout机制。--add-source添加的 IP 段,在某些 firewalld 版本中会因超时被自动清理,导致 Gossip 流量重新被 DROP。

排查命令

# 检查当前 active sources firewall-cmd --zone=swarm-internal --list-sources # 查看 firewalld 日志 journalctl -u firewalld | grep -i "timeout\|source"

修复动作

# 使用 `--permanent` 添加 source,并禁用 timeout(推荐) firewall-cmd --permanent --zone=swarm-internal --add-source=192.168.56.0/24 firewall-cmd --permanent --zone=swarm-internal --set-target=ACCEPT # 重载 firewall-cmd --reload # (可选)在 /etc/firewalld/firewalld.conf 中设置 # DefaultTimeout=0 # 禁用所有 timeout

5. 进阶加固与运维建议:让 Swarm 防火墙策略经得起审计与时间考验

配置完成只是起点。在真实运维中,你需要让这套策略具备可审计性、可迁移性和可持续性。以下是我在多个客户现场沉淀下来的 5 条硬核建议,每一条都直击企业级运维痛点。

5.1 建立防火墙策略版本库,与 Swarm 集群配置共 Git 管理

不要把firewall-cmd命令散落在运维笔记里。我要求所有集群的 firewalld 配置,必须以 XML 文件形式存入 Git 仓库,路径为infrastructure/firewalld/swarm-internal.xml。这个文件不是手动生成的,而是通过firewall-cmd --permanent --get-zone-of-interface=ens33导出后精简而来。每次集群扩容、网段变更,都必须提交 PR,由至少两名 SRE 审核后合并。这样做的好处是:

  • 新节点上线时,只需firewall-cmd --permanent --new-zone-from-file=swarm-internal.xml一键导入;
  • 审计时,git blame能清晰追溯每条规则是谁、何时、为何添加;
  • 故障回滚时,git checkout HEAD~3即可恢复到三天前的策略。

5.2 为不同角色节点定义差异化 Zone,而非一刀切

manager 节点和 worker 节点的安全需求完全不同。manager 需要开放 2377、7946,但应严格限制--add-source范围(如仅允许 192.168.56.101-103);worker 节点需开放 4789、80/443,但 2377 应设为--remove-port。我通常创建三个 zone:

  • swarm-manager:绑定 manager IP,仅含 2377/tcp、7946/udp;
  • swarm-worker:绑定 worker IP 段,含 4789/udp、80/tcp、443/tcp;
  • swarm-common:所有节点共享,含--add-masquerade--add-forward-port
    这样,firewall-cmd --list-all-zones的输出本身就是一份清晰的角色权限图。

5.3 使用--query-*命令替代--list-*进行自动化检测

在 Ansible Playbook 或 Shell 脚本中,永远用firewall-cmd --permanent --query-port=2377/tcp --zone=swarm-manager而不是firewall-cmd --list-all。前者返回 0(存在)或 1(不存在),可直接用于if判断;后者输出文本,需grep解析,极易因格式变化导致脚本崩溃。我见过太多因 firewalld 升级后--list-all输出增加空行而导致自动化部署失败的案例。

5.4 定期执行firewall-cmd --check-config,纳入 CI/CD 流水线

firewall-cmd --check-config会校验所有 XML 配置文件的语法合法性。我把它作为集群部署流水线的最后一个 stage。如果返回非零值,流水线立即失败,并邮件通知 SRE。这能提前捕获swarm-internal.xml<port>标签闭合错误、<source>IP 格式错误等低级失误,避免它们流入生产环境。

5.5 记录firewalldiptables的映射关系,作为故障定位速查表

虽然我们用 firewalld,但最终生效的是 iptables。我维护一张速查表,记录关键 firewalld 操作对应的 iptables 链和规则位置:

firewalld 操作对应 iptables 链规则位置验证命令
--add-port=2377/tcpfilter INPUT链尾部`iptables -t filter -L INPUT --line-numbers
--add-masqueradenat POSTROUTING链头部iptables -t nat -L POSTROUTING --line-numbers
--direct --add-rule ... -j DOCKER-INGRESSfilter INPUT链头部`iptables -t filter -L INPUT --line-numbers
--add-source=192.168.56.0/24filter INPUT链中部(带-s匹配)`iptables -t filter -L INPUT --line-numbers

这张表贴在团队 Wiki 首页,新成员入职第一周就要背熟。它让故障定位从“大海捞针”变成“按图索骥”。

我个人在实际操作中的体会是:防火墙不是一道墙,而是一张网。你配置的不是端口,而是信任关系;你管理的不是流量,而是节点间的契约。CentOS 7 的 firewalld 和 Docker Swarm 的结合,表面是技术选型,深层是运维哲学——它逼你放弃“全开全关”的粗暴思维,转而拥抱“最小权限、精准授权、持续验证”的现代安全实践。当你第一次看到docker node ls的所有节点稳定显示Ready,并且tcpdump持续刷出健康的 Gossip 包时,那种确定感,远胜于任何一次systemctl stop firewalld带来的短暂轻松。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询