nftables Map实现“多入站端口→多容器”极速分发,本质是基于哈希查表的DNAT跳转:创建inet_service:ipv4_addr.inet_service类型Map固化端口到容器IP:Port映射,在prerouting链用ip saddr . tcp dport查表并dnat to @map,配合conntrack与基础策略保障稳定性。

用 nftables 的 Map 实现“多入站端口 → 多容器”的极速分发,本质不是做传统 NAT 转发,而是利用 Map 做目标地址+端口的快速查表跳转,配合 dnat 规则与连接跟踪(conntrack),在内核层完成亚毫秒级决策。关键不在堆规则数量,而在把“端口→容器IP:Port”映射固化为哈希结构,避免链式遍历。
明确分发逻辑与前提条件
该方案适用于宿主机直接暴露多个公网端口(如 8080、8081、9000),每个端口需定向转发至不同容器的私有地址和端口(如 172.18.0.2:80、172.18.0.3:3000)。必须满足:
- 容器使用桥接网络(bridge),且宿主机能路由到容器 IP(通常默认可达)
- 已启用 conntrack(
cat /proc/sys/net/netfilter/nf_conntrack_enable应为 1) - 内核版本 ≥ 5.4(确保支持
ip saddr . tcp dport等复合 key 类型) - 不依赖 Docker 的 iptables 自动规则(建议启动时加
--iptables=false避免冲突)
构建高性能端口映射 Map
创建一个 inet_service : ipv4_addr . inet_service 类型的 Map,将“入站目标端口”映射到“容器 IP + 容器端口”。这种结构让单次查表即可获取完整 DNAT 目标,无需额外匹配。
执行命令:
sudo nft add map inet nat port_to_container { type inet_service : ipv4_addr . inet_service \; size 65536 \;}然后批量加载映射关系(例如:外部 8080 → 容器 A 的 172.18.0.2:80;8081 → 172.18.0.3:3000):
sudo nft add element inet nat port_to_container { tcp port 8080 : 172.18.0.2 . tcp port 80, tcp port 8081 : 172.18.0.3 . tcp port 3000 }在 prerouting 链中嵌入极速查表 DNAT
将 Map 查表动作放在 prerouting 链最前端,确保在路由决策前完成地址改写。使用 meta l4proto tcp @port_to_container ip saddr . tcp dport 进行精确匹配并触发 DNAT:
说明:
-
ip saddr . tcp dport作为 key,从 Map 中查出对应 value(即容器 IP 和端口) -
dnat to @...是 nftables 语法糖,自动展开为dnat to 172.18.0.2:80等具体值 - 该规则只处理 TCP;如需 UDP,另建一个同名 Map(类型改为
inet_service : ipv4_addr . inet_service并指定udp协议)
配套基础策略与稳定性保障
仅靠 Map 分发不够,还需最小化依赖、防止回环、保留连接状态:
- 确保
postrouting链中存在 SNAT 或 masquerade(若容器网段非直连,需伪装源地址) - 在
input链放行已建立连接:nft add rule inet filter input ct state established,related accept - 显式拒绝未命中 Map 的新连接:
nft add rule inet nat prerouting meta l4proto tcp ct state new drop(防端口扫描穿透) - 为调试加计数器:
counter name port_dnat_hit,后续可用nft list chain inet nat prerouting查看命中频次

















