CentOS 7下Docker Swarm防火墙精准配置指南

📅 2026/6/22 20:26:12
CentOS 7下Docker Swarm防火墙精准配置指南
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-legacyDocker Swarm不是单机 Docker它依赖一套完整的分布式控制平面通信机制Gossip 协议、Raft 日志同步、Overlay 网络封装这些流量类型、端口、协议、连接状态和普通 Web 服务截然不同而Linux Firewall在这里特指firewalldCentOS 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 自己在“说什么话”。很多人以为只要开了 2377manager API、3376TLS 通信、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-port7946/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_gwbridge和ingress-sbox容器做负载均衡其流量最终会落到iptables的DOCKER-INGRESS链。firewalld 无法直接管理此链必须通过--direct规则或--passthrough注入否则即使开了 80 端口请求也会在进入DOCKER-INGRESS前被firewalld的publiczone 默认策略丢弃。⚠️ 应用层必须配置提示不要试图用iptables -L查看这些规则是否生效。CentOS 7 的 firewalld 是 iptables 的上层抽象它维护自己的 runtime 和 permanent 规则集并通过iptables-restore同步到内核。你看到的iptables -L输出是 firewalld 渲染后的结果而非原始输入。真正的调试入口是firewall-cmd --list-all-zones和firewall-cmd --direct --get-all-rules。还有一个隐藏陷阱Docker 在启动时会自动创建docker0网桥并向iptables的FORWARD链插入ACCEPT规则允许容器间通信。但 firewalld 的publiczone 默认FORWARD策略是REJECT且其forwarding选项默认关闭。这意味着即使你放行了所有 Swarm 端口docker0网桥上的容器流量仍会被firewalld的 FORWARD 链拦截。解决方案不是关掉 firewalld而是显式启用forwarding并设置masquerade——这正是--zonetrusted或--add-interfacedocker0的底层逻辑。3. firewalld 实战配置从零开始构建一张安全、稳定、可审计的 Swarm 防火墙策略我们不走“先关防火墙再开”的野路子。CentOS 7 Minimal 安装后firewalld默认启用vendor preset: enabled这是正确起点。我们要做的是在保持默认安全基线的前提下精准注入 Swarm 所需的例外规则。整个过程分为五步每一步都有明确目的和验证方式拒绝任何“试出来”的侥幸操作。3.1 第一步锁定 Zone避免规则污染全局firewalld 的核心是 zone区域概念。publiczone 是默认区域适用于面向公网的接口internal适用于可信内网trusted则完全放行。很多教程直接让你firewall-cmd --set-default-zonetrusted这是危险操作——它会让所有网卡包括 eth0失去防护。正确做法是为 Swarm 通信网卡单独绑定一个专用 zone。假设你的 Swarm 节点使用ens33网卡进行集群通信可通过ip addr show确认执行# 创建专用 zone命名为 swarm-internal firewall-cmd --permanent --new-zoneswarm-internal firewall-cmd --permanent --zoneswarm-internal --set-descriptionDocker Swarm internal communication network firewall-cmd --permanent --zoneswarm-internal --set-targetACCEPT # 将 ens33 网卡绑定到该 zone firewall-cmd --permanent --zoneswarm-internal --add-interfaceens33 # 重载使 zone 生效 firewall-cmd --reload注意--set-targetACCEPT表示该 zone 内所有未明确拒绝的流量均接受但它不等于trusted。trustedzone 会跳过所有规则检查而swarm-internal仍受其自身规则约束只是默认目标为 ACCEPT。这是安全与便利的平衡点。验证是否绑定成功firewall-cmd --get-active-zones # 输出应包含 # swarm-internal # interfaces: ens333.2 第二步按通信平面逐条注入端口与协议规则现在所有针对ens33的流量都归swarm-internalzone 管理。我们在此 zone 下添加四类规则① Control Plane (TCP 2377)# 仅允许来自其他 Swarm 节点的 2377 端口 TCP 连接 # 假设你的 Swarm 节点 IP 段是 192.168.56.0/24VirtualBox/Vmware 常用 firewall-cmd --permanent --zoneswarm-internal --add-source192.168.56.0/24 firewall-cmd --permanent --zoneswarm-internal --add-port2377/tcp② Gossip Data Plane (UDP 7946) —— 关键# UDP 无连接必须显式添加且 source 必须与上一条一致 firewall-cmd --permanent --zoneswarm-internal --add-port7946/udp③ Overlay Network (UDP 4789) —— 仅 worker 节点执行# 此规则只在 worker 节点运行manager 节点跳过 firewall-cmd --permanent --zoneswarm-internal --add-port4789/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 --reload3.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 --zoneswarm-internal --add-forward-portport0-65535:prototcp:toport0-65535 firewall-cmd --permanent --zoneswarm-internal --add-forward-portport0-65535:protoudp:toport0-65535 # 启用 masqueradeNAT firewall-cmd --permanent --zoneswarm-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 --zoneswarm-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 或 timeout3.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 --zoneswarm-internal --remove-interfaceens33 # 改用 source IP 段绑定更稳定 firewall-cmd --permanent --zoneswarm-internal --add-source192.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 --zoneswarm-internal --query-masquerade # 检查当前 active rules非 permanent firewall-cmd --zoneswarm-internal --list-all修复动作# 确保 masquerade 已启用 firewall-cmd --permanent --zoneswarm-internal --add-masquerade firewall-cmd --reload # 再次尝试解锁 docker swarm unlock4.2 故障现象docker node ls显示部分节点为Unknown或Downdocker 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 --zoneswarm-internal --add-port7946/udp firewall-cmd --reload # 强制刷新 Gossip 缓存无需重启 docker docker swarm update --autolockfalse # 临时关闭 autolock docker swarm update --autolocktrue # 重新开启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 --reload4.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 --zonepublic --list-all | grep default修复动作# 确保 public zone 的 INPUT 默认策略为 ACCEPT仅限测试环境 # 生产环境应使用 --direct 规则而非改 default firewall-cmd --permanent --zonepublic --set-targetACCEPT # 或者更安全的做法将 80/443 的 --direct 规则绑定到 public zone firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp -m tcp --dport 80 -j DOCKER-INGRESS --zonepublic firewall-cmd --reload4.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 --zoneswarm-internal --add-port4789/udp firewall-cmd --reload # 删除并重建 ingress 网络谨慎操作会中断服务 docker network rm ingress docker network create --driver overlay --attachable ingress4.6 故障现象firewall-cmd --reload后docker info显示WARNING: bridge-nf-call-iptables is disabled容器无法访问外网根因分析firewalld重载会重置内核参数。bridge-nf-call-iptables控制网桥流量是否经过 iptablesDocker 依赖它实现docker0的 NAT。排查命令sysctl net.bridge.bridge-nf-call-iptables # 若返回 0则未启用修复动作# 临时启用 sysctl -w net.bridge.bridge-nf-call-iptables1 # 永久启用写入配置文件 echo net.bridge.bridge-nf-call-iptables 1 /etc/sysctl.conf sysctl -p # 重启 docker systemctl restart docker4.7 故障现象集群运行数小时后部分 worker 节点自动脱离docker node ls显示Down根因分析firewalld的timeout机制。--add-source添加的 IP 段在某些 firewalld 版本中会因超时被自动清理导致 Gossip 流量重新被 DROP。排查命令# 检查当前 active sources firewall-cmd --zoneswarm-internal --list-sources # 查看 firewalld 日志 journalctl -u firewalld | grep -i timeout\|source修复动作# 使用 --permanent 添加 source并禁用 timeout推荐 firewall-cmd --permanent --zoneswarm-internal --add-source192.168.56.0/24 firewall-cmd --permanent --zoneswarm-internal --set-targetACCEPT # 重载 firewall-cmd --reload # 可选在 /etc/firewalld/firewalld.conf 中设置 # DefaultTimeout0 # 禁用所有 timeout5. 进阶加固与运维建议让 Swarm 防火墙策略经得起审计与时间考验配置完成只是起点。在真实运维中你需要让这套策略具备可审计性、可迁移性和可持续性。以下是我在多个客户现场沉淀下来的 5 条硬核建议每一条都直击企业级运维痛点。5.1 建立防火墙策略版本库与 Swarm 集群配置共 Git 管理不要把firewall-cmd命令散落在运维笔记里。我要求所有集群的 firewalld 配置必须以 XML 文件形式存入 Git 仓库路径为infrastructure/firewalld/swarm-internal.xml。这个文件不是手动生成的而是通过firewall-cmd --permanent --get-zone-of-interfaceens33导出后精简而来。每次集群扩容、网段变更都必须提交 PR由至少两名 SRE 审核后合并。这样做的好处是新节点上线时只需firewall-cmd --permanent --new-zone-from-fileswarm-internal.xml一键导入审计时git blame能清晰追溯每条规则是谁、何时、为何添加故障回滚时git checkout HEAD~3即可恢复到三天前的策略。5.2 为不同角色节点定义差异化 Zone而非一刀切manager 节点和 worker 节点的安全需求完全不同。manager 需要开放 2377、7946但应严格限制--add-source范围如仅允许 192.168.56.101-103worker 节点需开放 4789、80/443但 2377 应设为--remove-port。我通常创建三个 zoneswarm-manager绑定 manager IP仅含 2377/tcp、7946/udpswarm-worker绑定 worker IP 段含 4789/udp、80/tcp、443/tcpswarm-common所有节点共享含--add-masquerade和--add-forward-port。这样firewall-cmd --list-all-zones的输出本身就是一份清晰的角色权限图。5.3 使用--query-*命令替代--list-*进行自动化检测在 Ansible Playbook 或 Shell 脚本中永远用firewall-cmd --permanent --query-port2377/tcp --zoneswarm-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标签闭合错误、sourceIP 格式错误等低级失误避免它们流入生产环境。5.5 记录firewalld与iptables的映射关系作为故障定位速查表虽然我们用 firewalld但最终生效的是 iptables。我维护一张速查表记录关键 firewalld 操作对应的 iptables 链和规则位置firewalld 操作对应 iptables 链规则位置验证命令--add-port2377/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-source192.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带来的短暂轻松。