咱们通常的项目中,大多数已经容器化部署。服务之间访问,即同一个集群的东西向流量,直接经过 service 访问。而南北向流量,是经过 ingress 来解决。git
在东西向流量访问中,有一些大流量和高并发的业务,尤为是在pod扩缩的时候,常常出现丢包而致使接口错误率比较高。咱们service 的backend 选择的是iptabels。因此咱们怀疑是iptables带来的问题。github
首先怀疑是端口复用,当端口耗尽的时候,没有多余的端口用于SNAT操做,就会出现数据包都丢弃或是拒绝的状况。咱们看了一下conntrack表, 可是实际值远远没有达到上限值。网络
conntrack -S
发现大量的 nf_conntrack insert failed
错误。并发
这个问题在k8s早期,仍是比较常见的。负载均衡
NAT代码在POSTROUTING链上hook了两次。首先,经过更改源IP和PORT(IP或PORT)来修改数据包结构,而后,若是未在中间丢弃数据包,则将转换记录在conntrack表中。若是存在冲突,这意味着SNAT端口分配和表中的插入之间存在延迟,就会致使 nf_conntrack insert failed,结果致使数据包丢失
less
不少网络组件已经支持了 NF_NAT_RANGE_PROTO_RANDOM_FULLY
, 好比Flannel。dom
经过 NF_NAT_RANGE_PROTO_RANDOM_FULLY
,能够大大减小插入错误的数量。在具备默认假装规则和链接到同一主机的10至80个线程的Docker测试虚拟机上,conntrack表中插入失败的发生率为2%至4%。
在内核中强制采用彻底随机性后,错误降至0(后来在实时群集中接近0)。高并发
咱们生产环境使用的是EKS,关于ENI也实现了相似的方案。对于ENI,在其部署选型中,有个配置项 AWS_VPC_K8S_CNI_RANDOMIZESNAT
。测试
翻译官方文档大体内容以下:google
指定SNAT iptables规则是否应将链接的出端口随机化。当AWS_VPC_K8S_CNI_EXTERNALSNAT = false 时,应使用此选项。启用后(hashrandom),--random标志将添加到SNAT iptables规则。要使用伪随机数生成而不是基于散列(即--random-fully),请使用prng做为环境变量。对于不支持--random-彻底的旧版本的iptables,此选项将退回到--random。若是您依赖于出站链接的顺序端口分配,请禁用(无)此功能。
固然出于生产环境稳定性,咱们在已经有的集群当中选择了另一个方案--绕过iptables。
咱们知道pod网络自己就是相通的, 大体实现方案有隧道,路由等。
咱们能够获取到Pod列表,而后作客户端负载均衡。因为咱们这些项目自己协议就是grpc,service的4层负载均衡是没有意义的,因此咱们的客户端已经实现了客户端负载均衡。
在Kubernetes中,有一种称为headless服务的特定服务。Headless服务不会为底层Pod提供单个IP和负载平衡,而只是具备DNS配置,该配置为咱们提供了一个A记录,其中包含与标签选择器匹配的全部Pod的Pod IP地址。
不过该方案须要注意:
配置ndots。咱们知道设置pod的dnsPolicy为ClusterFirst以后,全部的dns解析请求,都会请求到coredns。默认的ndots:5, 会致使接口的延时变大,不少时间浪费在dns请求上。经过抓包分析:
ndots为5的时候:
03:52:25.216518 IP access-6bf77d68df-sm87n.42375 > kube-dns.kube-system.svc.cluster.local.domain: 11402+ A? www.baidu.com.tsa.svc.cluster.local. (53) 03:52:25.216916 IP access-6bf77d68df-sm87n.40506 > kube-dns.kube-system.svc.cluster.local.domain: 64764+ PTR? 10.0.100.10.in-addr.arpa. (42) 03:52:25.217360 IP kube-dns.kube-system.svc.cluster.local.domain > access-6bf77d68df-sm87n.42375: 11402 NXDomain*- 0/1/0 (146) 03:52:25.217489 IP access-6bf77d68df-sm87n.42327 > kube-dns.kube-system.svc.cluster.local.domain: 39517+ A? www.baidu.com.svc.cluster.local. (49) 03:52:25.217660 IP kube-dns.kube-system.svc.cluster.local.domain > access-6bf77d68df-sm87n.40506: 64764*- 1/0/0 PTR kube-dns.kube-system.svc.cluster.local. (118) 03:52:25.217877 IP kube-dns.kube-system.svc.cluster.local.domain > access-6bf77d68df-sm87n.42327: 39517 NXDomain*- 0/1/0 (142) 03:52:25.217906 IP access-6bf77d68df-sm87n.48023 > kube-dns.kube-system.svc.cluster.local.domain: 56925+ A? www.baidu.com.cluster.local. (45) 03:52:25.218056 IP kube-dns.kube-system.svc.cluster.local.domain > access-6bf77d68df-sm87n.48023: 56925 NXDomain*- 0/1/0 (138) 03:52:25.218102 IP access-6bf77d68df-sm87n.35164 > kube-dns.kube-system.svc.cluster.local.domain: 13509+ A? www.baidu.com.ap-southeast-1.compute.internal. (63) 03:52:25.219213 IP kube-dns.kube-system.svc.cluster.local.domain > access-6bf77d68df-sm87n.35164: 13509 NXDomain 0/0/0 (63) 03:52:25.219280 IP access-6bf77d68df-sm87n.56466 > kube-dns.kube-system.svc.cluster.local.domain: 22697+ A? www.baidu.com. (31) 03:52:25.221500 IP kube-dns.kube-system.svc.cluster.local.domain > access-6bf77d68df-sm87n.56466: 22697 4/0/0 CNAME www.a.shifen.com., CNAME www.wshifen.com., A 45.113.192.101, A 45.113.192.102 (181) 03:52:25.223936 IP access-6bf77d68df-sm87n.59668 > kube-dns.kube-system.svc.cluster.local.domain: 33885+ PTR? 101.192.113.45.in-addr.arpa. (45) 03:52:25.225784 IP kube-dns.kube-system.svc.cluster.local.domain > access-6bf77d68df-sm87n.59668: 33885 NXDomain 0/1/0 (122)
ndots为2的时候:
04:06:19.030624 IP access-d6f56b8db-2gxfd.34877 > kube-dns.kube-system.svc.cluster.local.domain: 21513+ A? www.baidu.com. (31) 04:06:19.032784 IP kube-dns.kube-system.svc.cluster.local.domain > access-d6f56b8db-2gxfd.34877: 21513 4/0/0 CNAME www.a.shifen.com., CNAME www.wshifen.com., A 45.113.192.102, A 45.113.192.101 (181) 04:06:19.035098 IP access-d6f56b8db-2gxfd.53602 > kube-dns.kube-system.svc.cluster.local.domain: 37598+ PTR? 102.192.113.45.in-addr.arpa. (45) 04:06:19.036743 IP kube-dns.kube-system.svc.cluster.local.domain > access-d6f56b8db-2gxfd.53602: 37598 NXDomain 0/1/0 (122)
对于一些外部域名,默认ndots为5,每次解析都要所有搜索一遍。ndots为2的时候 只搜索一次。
FQDN是完整域名,通常来讲,域名最终以.结束表示是FQDN,例如google.com.是FQDN,但google.com不是。
对FQDN,操做系统会直接查询DNS server。那么非FQDN呢?这里就要用到search和ndots了。
ndots表示的是域名中必须出现的.的个数,若是域名中的.的个数不小于ndots,则该域名为一个FQDN,操做系统会直接查询;若是域名中的.的个数小于ndots,操做系统会在search搜索域中进行查询。
例如上面的例子,ndots为5,查询的域名google.com不以.结尾,且.的个数少于5,所以操做系统会依此在default.svc.cluster.local svc.cluster.local cluster.local lan四个域中进行了搜索,其中前面3个搜索域是由kubernetes注入的,最后的lan是操做系统默认的搜索域。
ndots默认值为1,也就是说,只要域名中有一个.,操做系统就会认为是绝对域名,直接查询。
ndots上限为15。
coredns 配置文件增长forward插件。
forward . /etc/resolv.conf
将外部的域名按照/etc/resolv.conf的解析规则解析。
固然终极方案service mesh这种服务治理方案是最好的,若是是南北向流量,其实使用ingress就能够避免iptables。