目录
一、集群说明二、预备知识三、问题描述四、排查流程五、回顾总结
一、集群说明
- OS: CentOS7.2
- DS(LVS): 10.1.61.114
- LVS-MODE: TUN
- VIP: 10.1.61.82
- RS1(Nginx): 10.1.62.105
- RS2(Nginx): 10.1.62.106
- PORT: 80
- 防火墙: DS、RS1、RS2均安装 firewalld,且开放80端口(tcp,udp)
二、预备知识
firewalld 是 iptables 的前端控制器,用于实现持久的网络流量规则。
firewalld 自身并不具备防火墙的功能,而是和 iptables 一样需要通过内核的 netfilter 来实现,也就是说 firewalld 和 iptables 一样,他们的作用都是用于维护规则,而真正使用规则干活的是内核的 netfilter,只不过 firewalld 和 iptables 的结构以及使用方法不一样罢了。
netfilter 有五链四表,说明如下。
五链:- PREROUTING:数据包进入路由表之前
- INPUT:通过路由表后目的地为本机
- FORWARD:通过路由表后,目的地不为本机
- OUTPUT:由本机产生,向外转发
- POSTROUTIONG:发送到网卡接口之前。
四表:
- filter表,负责过滤数据包,也就是防火墙实现"防火"的功能,iptables命令中如果没有指定 -t,即默认为此表。filter表中只有OUTPUT/FORWARD/INPUT链。
- nat表,实现网络地址转换的表。可以转换源地址、源端口、目标地址、目标端口。NAT表中的链是PREROUTING/POSTROUTING/OUTPUT。
- mangle表,拆解报文,做出修改,并重新封装。mangle表中包含所有的链。
- raw表,加速数据包穿过防火墙的表。只有PREROUTING/OUTPUT表。
四个表的优先级由高到低的顺序为:raw → mangle → nat → filter,比如说 PRROUTING 链上,即有 mangle 表,也有 nat 表,那么先由 mangle 处理,然后由 nat 表处理。
Linux 上的防火墙是由 netfilter 实现的,但是 netfilter 的功能不仅仅只有“防火”,一般可以认为“防火”的功能只是 filter 表的功能。
了解上述知识后,我们可以吧数据包通过防火墙的流程总结为下图。
三、问题描述
集群部署完成后,访问 VIP:PORT 遭到拒绝(Connection refused)。
直接访问 RS1:PORT 和 RS2:PORT 均可以正常访问 Nginx 服务(由于我未写入index.html,所以返回404页面是正常现象)。
四、排查流程
① 关闭 DS 防火墙,未解决问题。恢复开启 DS 防火墙后,再尝试关闭 RS1 防火墙,RS1 服务正常返回,RS2 依旧是连接拒绝。
猜测 Client → DS 这条链路是正常的,问题出现在 DS → RS 这条链路上。
② 恢复开启 RS1 的防火墙,登陆至 DS 机器上,使用命令ipvsadm -L -n
查看连接状态来验证猜想。
ipvsadm -L -n 命令结果小科普:
对于 TCP 协议而言,ActiveConn 列是活动连接数,也就是tcp连接状态的 ESTABLISHED ,而 InActConn 列是指除了 ESTABLISHED 以外的,所有的其它状态的 tcp 连接。
当前 InActConn 和 ActiveConn 均为0。
在 Client 端继续使用命令for i in {1..100}; do curl 10.1.61.82:80; sleep 0.5; done
循环请求 VIP:PORT,然后在 DS 上使用命令ipvsadm -L -n
再次查看连接状态。发现 InActConn 数值发生了改变,说明 DS 是收到了 Client 端的数据包的,ActiveConn 为0是因为 DS → RS 被拒,所以 tcp 连接未能建立。
目前已定位到数据包出现问题的链路是在 DS → RS ,OK,那就抓个包看看呗。
③ 在DS上使用命令tcpdump -i any host 10.1.62.105
截获 RS1 收到和发出的所有数据包(在 Client 依然用老方法循环请求 VIP:PORT ),可以看到 DS 发给 RS1 的数据包被 RS1 用一个协议为 ICMP 的数据包拒绝了。等等,这个数据包的内容咋这么眼熟(ICMP host prohibited),好像用 iptables 查看规则时见过。
④ 我马上登陆到RS1,直接用命令iptables --line -vnL INPUT
查看 INPUT 链中的规则(--line
可显示规则的编号,数据包在链中是顺着规则编号往下逐步匹配的;-v
显示详细信息;-n
不解析IP地址;-L
指定链名;不指定-t
默认显示 filter 表)。
可以看到第七条 REJECT 规则就是用 icmp-host-prohibited 作为数据包内容拒绝的。啊哈?DS 发给 RS1 的数据包被最后一条规则匹配到了?为什么没被前面的规则处理掉?我不是开启了80端口吗?
⑤ 虽然一堆疑问,不过还是先确认下 DS 发给 RS1 的数据包是不是真被第七条规则匹配处理了。这里我采用的方法是,在 Client 端循环请求 VIP:PORT 的前后分别用命令iptables --line -vnL INPUT
查看第七条规则的 pkts(该规则匹配到的数据包数量)数值是否发生了变化,果然 pkts 发生了增长,说明数据包真的是被第七条规则匹配到并且 REJECT 掉了。
额,我用命令iptables --line -vnL
查看了所有 Chain(链) 的规则,找到我开放80端口的那两条规则又确认了一下,发现 dport 是80,协议为 tcp 的那条规则 pkts 一直是0,并没有变化(这条规则在 INPUT 链中是被第五条规则匹配的,规则虽然是在 IN_public_allow 链的,但了解过 iptables 自定义链相关知识后,就会明白这实际上是一个调用链的关系:INPUT_ZONES → IN_public → IN_public_allow,从下图的每个 Chain 的 target 也能看出来)。
再次确认下,这条规则源地址、目标地址、端口、协议等等都没有问题,而且最开始我也直接访问 RS1:PORT 确认过 Nginx 服务是没有问题的,可以正常返回,所以我怀疑从 DS → RS1 的数据包并不是一个 tcp 包,所以没有被此规则匹配并处理。
⑥ 然后又返回去查了下 LVS-TUN 模式的数据包处理流程。
ipvs 工作在 INPUT 链,它在原有的 IP 报文外再次封装多一层 IP 头,内部 IP 头 (源地址为 CIP,目标 IP 为 VIP),外层 IP 头 (源地址为 DIP,目标 IP 为 RIP),然后将封装好的数据包从 DS 发往 RS 。
到这,我已经恍然大悟了,它确实不是一个 tcp 包,再翻到之前从 DS 上抓取 RS1 的数据包内容,可以看到 DS → RS1 的数据包协议为 ipip-proto-4。
⑦ 所以,处理方法也很明确了,放行这个 ipip 协议的数据包。我的处理方法是在 RS1 上使用命令iptables -t filter -I INPUT -p ipv4 -j ACCEPT
增加一条对所有协议类型为 ipip 的数据包ACCEPT的规则(这里的 prot 显示的 4 代表 ipv4 的协议号,想要查看协议名与协议号的对应关系可以在这里查找Protocol Numbers)。
记住一定要在 INPUT 链第七条之前,我这里是直接插入到了第一条,我之前看一篇博文上用的是-A INPUT
,这表示 append(追加) 一条规则,会出现在最后,这样设置的规则数据包根本就匹配不到,因为它已经被在你前面的那条REJECT规则匹配并拒绝掉了。
⑧ 最后我们再从 Client 端请求验证一下规则是否生效,可以看到 RS1 已经正常返回了,且 RS1 上的第一条规则 pkts 也发生了增长,说明数据包已被匹配且正确处理。
RS2 也设置相同的过滤规则,然后再次验证,两台 RS 均正常返回,over。
五、回顾总结
很多搭建 LVS 集群的文章都会前置声明关闭防火墙或者使用
iptables -F
命令将 iptables 规则清空,以免发生通信问题。实际上大多数情况下你确实可以这样做,但是万一生产环境就要开防火墙呢,万一设置的某几条 iptables 规则非常重要,你啪一条命令给人全清了,是不是得写个事故报告。我的排查流程真的是像文章中一样如此顺利吗?其实并不是,因为这个问题出现时,我既不清楚 iptables 相关知识(一两年前简单的看过,然后忘光了),也不清楚 ipvs 的工作原理,就是拿来主义,所以刚开始排查的时候和无头苍蝇一样,一顿瞎比操作,后来还是滚回去补了 iptables 和 ipvs 的相关知识。所以其实排查流程中的第4步开始都已经是第二天的事了。
如果懂了 iptables 相关知识(五链四表、规则管理、匹配条件、扩展模块、自定义链等等)。清楚命令及命令结果中每个参数的意思非常重要,像遇到此类防火墙问题,你都可以用文中的排查流程去走一遍,无非就是规则匹配问题。
分析网络问题,简单的抓包可以达到事半功倍的效果,配合 Wireshark 一起食用味道更佳。