序言

在家整了一套k8s集群,用了calico来当cni组件,想把容器网络和家里内网打通,在几种网络方案中还是倾向于走路由的方式,一是性能更高,二是pod ip可见即可通,整体思路如下:

  1. 部署k8s,安装calico
  2. 家庭网络增加静态路由,pod的cidr指向到随便一个node (会有容灾问题,家庭网络暂且不论)
  3. 包到了node上后,node根据本身calico维护的路由信息转到目的pod的node,再转入pod内中的服务

方案简介

设备定义

局域网内PC: A
Openwrt旁路由: B
k8s Node节点1(旁路POD CIDR的下一跳): C
k8s Node节点2(目的POD所在节点): D

目标路由

正常路由应该为: A -> B -> C -> D

涉及知识点

  • IP路由
  • Iptables(net_filter)
  • rp_filter

过程步骤

在完成步骤(1)(2)后,预期是请求能正确的到达预期的节点,预期的POD中,但是在测试验证的时候发现还是不通,通过在C抓包发现包已经收到,且已经正常转发出去
WeChatdc27beca76b8c0739e8f832a74557d4e.png

在D节点上抓包发现只有入包,没有回复的包
WeChat1011c21b7587666885196e3d71947cee.png

怀疑是路由有问题,使用ip route查看了路由是没问题的,同时在同集群的别的节点进行ping是通的,接下来就怀疑到了是不是iptables这块,接下来看一看iptables

Iptables基本知识

一个包进入到linux机器前是要经过内核 经过net filter,而net filter是分一些节点对网络进行处理的,我们叫他这些节点叫链(Chain),每个链包含一些处理策略,具体策略的管理手段就是通过我们熟悉的iptables工具了(可知iptables本身并不提供防火墙的一些能力,他只是一个管理工具而已,具体的实现还是内核的net filter)
proxy_hooks_hu62043a03770f01a83f301a079b6e661d_90689_1200x1200_fit_q75_h2_lanczos_3.jpg

然后在每个链中都有一些 table,进行网络的处理,这个table就是我们常见的iptables中用到了 filter表等,具体的表有:

  • filter:做正常的过滤,如接受,拒绝/删,跳
  • nat:网络地址转换,包括 SNAT(源 nat) 和 DNAT(目的 nat)
  • mangle:修改包属性,例如 TTL
  • raw:最早的处理点,连接跟踪前的特殊处理 (conntrack 或 CT,也包含在上图中,但这不是链)
  • security:本文未涉及(很少使用)

这些表在链中的表示位置如下:
proxy_hooks-and-tables_hub42b0940c295444944d794bf714d734d_199241_1200x1200_fit_q75_h2_lanczos_3.jpg

从图中可见:

  • 包在经过PREROUTING后会进行路由判断,如果是目的是本机的就走向INPUT链,如果不是本机的就会判断是否开了ip_forward,如果开了就会走向FORWARD链
  • 经过FORWARD链后会再次进行路由判断,看看本机到目的有没有路由可达,如果可达就走POSTROUTING,没有就丢了

所以会怀疑我们的包是不是在从开始到结束路由中的某个环节丢了,接下来看iptables规则

# iptables -L -nv -t filter
# iptables -L -nv -t mangle
# iptables -L -nv -t raw
# iptables -L -nv -t nat

结果就不展示了,总的来说非常多,非常复杂,不是给人看的。 尝试过往上找iptables规则可视化,效果都不好

Iptables规则追踪

iptables其实是可以追踪的,可以通过指定规则,插入追踪点,然后发起请求,再去看日志(一定要准确定义好追踪点,不然太多日志是没法看的)

接下来我追踪的都是简单定义的icmp,如果大家要用,一定记得自己看看是否适合

Log

执行如下iptables命令,增加日志点(要删除把后面的-A改成-D再执行就行了)

iptables -t raw -A PREROUTING -p icmp -j LOG --log-prefix "raw PREROUTING: "
iptables -t raw -A OUTPUT -p icmp -j LOG --log-prefix "raw OUTPUT: "

iptables -t mangle -A PREROUTING -p icmp -j LOG --log-prefix "mangle PREROUTING: "
iptables -t mangle -A INPUT -p icmp -j LOG --log-prefix "mangle INPUT: "
iptables -t mangle -A OUTPUT -p icmp -j LOG --log-prefix "mangle OUTPUT: "
iptables -t mangle -A FORWARD -p icmp -j LOG --log-prefix "mangle FORWARD: "
iptables -t mangle -A POSTROUTING -p icmp -j LOG --log-prefix "mangle POSTROUTING: "

iptables -t nat -A PREROUTING -p icmp -j LOG --log-prefix "nat PREROUTING: "
iptables -t nat -A INPUT -p icmp -j LOG --log-prefix "nat INPUT: "
iptables -t nat -A OUTPUT -p icmp -j LOG --log-prefix "nat OUTPUT: "
iptables -t nat -A POSTROUTING -p icmp -j LOG --log-prefix "nat POSTROUTING: "

iptables -t filter -A INPUT -p icmp -j LOG --log-prefix "filter INPUT: "
iptables -t filter -A OUTPUT -p icmp -j LOG --log-prefix "filter OUTPUT: "
iptables -t filter -A FORWARD -p icmp -j LOG --log-prefix "filter FORWARD: "

然后可以在 /var/log/messages 去看日志,也可以把这部分日志剥离成单独日志文件,具体信息可以看后面附件的引用的文章

Trace

上面的方式其实是相对比较麻烦,我更加喜欢trace的方式,会简单一点。trace只可以在raw表上增加,他的实现方式也是在包进来后打个标记然后系统在各个节点捕获到了就会打日志,很详细。

不同系统发行版本操作方式还不一样

对于RHEL 6/7: 用-j-trace-扩展进行全程跟踪

对于RHEL8:

iptables -t raw -D OUTPUT -p icmp -j TRACE
iptables -t raw -D PREROUTING -p icmp -j TRACE

然后执行 xtables-monitor --trace 即可查看trace日志(和早期RHEL发行版本查看方式不一样)

经过跟踪后,发现请求经过PREROUTING后就没有信息了(没有丢弃或者改包),预估判断应该是到FORWARD前的路由部分有问题了

峰回路转

接下来没有思路了,查看路由表确实存在,且同集群其他节点能正常转发访问,唯一不同点是源IP到网段不一样,接下来怀疑是不是因为用了calico的IP-IP模式,是不是隧道tunl经过二次转发后不能再次进行转发,经过抓包看ttl等信息后基本确定和这个无关。接下来开始找别的在net filter的FORWARD前会有什么检查校验,这时找到一个重要的参数: rp_filter

该参数实现的一个朴素的功能是: 源包进来后会在FORWARD前进行回包判断,如果回包不是走原来进来的网卡回去则丢了

看机器上的配置:

[root@linux-213 ~]# sysctl -a | grep rp_filter
net.ipv4.conf.all.arp_filter = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.cali3c2a3d3f133.arp_filter = 0
net.ipv4.conf.cali3c2a3d3f133.rp_filter = 1
net.ipv4.conf.cali44b062d5b0d.arp_filter = 0
net.ipv4.conf.cali44b062d5b0d.rp_filter = 1
net.ipv4.conf.cali46bf9beb5cb.arp_filter = 0
net.ipv4.conf.cali46bf9beb5cb.rp_filter = 1
net.ipv4.conf.cali79f6990ed79.arp_filter = 0
net.ipv4.conf.cali79f6990ed79.rp_filter = 1
net.ipv4.conf.default.arp_filter = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.ens18.arp_filter = 0
net.ipv4.conf.ens18.rp_filter = 1
net.ipv4.conf.lo.arp_filter = 0
net.ipv4.conf.lo.rp_filter = 1
net.ipv4.conf.tunl0.arp_filter = 0
net.ipv4.conf.tunl0.rp_filter = 1

默认是关了的,但是tunl0正好是开的,我们包就是从tunl0过来,但是回去的时候回包肯定是走ens18主网卡回去的,刚好符合拦截条件,执行echo 0 > /proc/sys/net/ipv4/conf/tunl0/rp_filter 后再次验证,果然通了,问题解决

结语

  • tunl设备这个参数设定值是calico设定的,需要看看怎么从上层来修改配置,不能靠手工改系统参数
  • 使用IP-IP模式可能是错误的方式,后续看看使用BGP模式
  • 从家里到旁路由只指向一个node节点非常不可靠,后续需要看看用keepalived来保证可用性
  • 通过iptables看到了这块到复杂性和难维护性,使用集群最好是抛弃kube-proxy本身的这些规则,尽量的避免使用iptables这套东西

参考资料

iptables基础链:
https://cloudnative.to/blog/k8s-node-proxy/

iptables 日志跟踪
https://damiansheldon.github.io/blog/remembering-a-docker-network-issue-troubleshooting.html
https://www.jianshu.com/p/099ecf623eb5
https://www.lijiaocn.com/%E6%8A%80%E5%B7%A7/2014/04/16/linux-net-iptables.html#iptables-%E8%A7%84%E5%88%99%E8%B0%83%E8%AF%95

rp_filter
https://zhuanlan.zhihu.com/p/414499521

防火墙:
https://bbs.huaweicloud.com/blogs/300564
http://arthurchiao.art/blog/deep-dive-into-iptables-and-netfilter-arch-zh/

标签: none

添加新评论