面前部署在 Kubernetes 中的劳动小宝 探花,通过 Calico BGP 将 Service 与集群外收集买通,并在外部的 nginx 中设立 Service 地址对外进行劳动知晓。经过一段时分的不雅察,发当今 Deployment 逶迤更新中及之后一段时分,偶现劳动走访 502 的问题。
[[418415]]
问题配景和慷慨现时 Kuberntes 集群使用 Calico 算作 CNI 组件,并使用 BGP 模式将 Pod IP 和 Service IP 与集群外收集买通,通过集群外的 Nginx 作反向代理对外提供劳动,诈欺王人是以 Deployment 面容部署。通过一段时分的不雅察,部分诈欺响应,在诈欺发布后一段时安分,劳动有一定几率出现 502 报错。
问题排查最径直的测度,是否问题只发生在逶迤更新经过中,即诈欺莫得作念好查验检测的设立,导致劳动莫得着实可用,Pod 却已经处于 ready 现象。
约略的测试后很快排斥这个可能,对设立了有用健康查验探针的 Deployment 进行逶迤更新,并使用 ab 通过 Nginx 设立的域名进行不时申请(此时无并发),发当今诈欺逶迤更新竣事后,并通过 pod IP 东说念主工说明了劳动莫得问题,仍有概率出现 502 罅隙,且出现罅隙的慷慨会不时几分钟以致十几分钟的时分,光显远远跳动了逶迤更新所需时分。
上头的初步测试的慷慨,排斥了诈欺自己的问题。下一个怀疑的指标指向了 Nginx。既然慷慨是通过 Nginx 代理走访产生的,那么径直申请 Service 有莫得问题呢,由于现时集群 Service 地址和外部收集作念了买通,测试起来很浅陋,我准备了如下的测试:
ab 不时申请域名通过 Nginx 走访劳动,并触发逶迤更新(ab -r -v 2 -n 50000 http://service.domain.com/test) ab 不时申请 serviceIP:port 走访劳动,并触发逶迤更新(ab -r -v 2 -n 50000 http://10.255.10.101/test)经过测试,案例 1 出现了 502 罅隙,案例 2 未出现。是以,问题是在 Nginx 嘛?
找到负责 Nginx 的共事进行分析,论断是 Nginx 似乎不会形成雷同的问题。那为什么上头测试中只好案例1 复现了问题呢?于是我决定再行进行测试,此次在 ab 申请的时候加上了并发(-c 10),恶果,两个案例王人出现了 502 的罅隙。这么,问题似乎又回到了 Kubernetes 集群自己,而且似乎在申请量较大的情况下才会出现。
这时,我运转怀疑是否可能是因为某种原因,逶迤发布后的一段时分里,一些申请会罅隙的被分发到已经被杀掉的老得 podIP 上。为了考据这一测度小宝 探花,我进行了如下实验:
创建一个测试的 Deployment,副本数为 1,提供约略的 http 劳动,并在经受到申请时输出日记,并创建对应 Service。 使用 ab 并发申请该劳动 Service 地址。 使用 kubectl patch 修改 Pod 的 label,使其和 Deployment 不一致,触发 Deployment 自动拉起一个新的 Pod。 跟踪新的 Pod 和老的 Pod 的日记,不雅察申请进来的情况。第三步 patch pod 的 label,是为了保留原本的 pod 实例,以便不雅察申请是否会分发到老的 Pod。(patch Pod 的 label 不会使 Pod 重启或退出,然而转变了 label,会使 Pod 脱离原 Deployment 的放肆,因此触发 Deployment 新建一个 Pod)。
恶果和预期一致,当新的 Pod 已经 ready,Endpoint 已经出现了新的 Pod 的 IP,申请仍然会进到原本的 Pod 中。
基于以上的恶果,又通过屡次实验,不雅察 Kubernetes 节点上的 IPVS 律例,发当今逶迤更新及之后一段时分,老的 podIP 还会出当今 IPVS 律例中,不外 weight 为 0,手动删除后 weight 为 0 的 rs 后,问题就不再出现。到此,找到问题场地是 IPVS,然而为什么会这么呢,在搜索了关联的著作后,约略找到了原因。
诡异的 No route to host,讲到了 IPVS 的一个脾性:
也即是 IPVS 模块处理报文的主要进口,发现它会先在腹地聚拢转发表看这个包是否已经有对应的聚拢了(匹配五元组),要是有就说明它不是新聚拢也就不会调遣,径直发给这个聚拢对应的之前已经调遣过的 rs(也不会判断权重);要是没匹配到说明这个包是新的聚拢,就会走到调遣这里 (rr,wrr 等调遣计策)。即:五元组(源IP地址、主张IP地址、条约号、源端口、主张端口)一致的情况下,IPVS 有可能不经过权重判断,径直将新的聚拢当成存量聚拢,转发到原本的 real server(即 PodIP)上。表面上这种情况在单一客户端无数申请的场景下,才有可能触发,这亦然诡异的 No route to host一文中模拟出的场景,即:
不同申请的源 IP 持久是疏导的,要道点在于源端口是否可能疏导。由于 ServiceA 向 ServiceB 发起无数短聚拢,ServiceA 场地节点就会有无数 TIME_WAIT 现象的聚拢,需要等 2 分钟(2*MSL)才会计帐,而由于聚拢量太大,每次发起的聚拢王人会占用一个源端口,当源端口不够用了,就会重用 TIME_WAIT 现象聚拢的源端口,这个时候当报文参预 IPVS 模块,检测到它的五元组跟腹地聚拢转发表中的某个聚拢一致(TIME_WAIT 现象),就合计它是一个存量聚拢,然后径直将报文转发给这个聚拢之前对应的 rs 上,关联词这个 rs 对应的 Pod 早已焚烧,是以合手包看到的慷慨是将 SYN 发给了旧 Pod,而况无法收到 ACK,伴跟着复返 ICMP 示知这个 IP 弗成达,也被诈欺评释为 “No route to host”。
原因分析这里分析一下之前的测试中为何会出现两种不同的恶果。我一共进行了两次对比实验。
邓紫棋换脸第一次,未加并发,通过 Nginx 和 通过 Service IP 进行走访并对比。这组实验中,通过 Nginx 走访复现了问题,而通过 Service IP 莫得,这个恶果也几乎将排查引入邪道。而当今分析一下,原因是因为面前的 Kubernetes 劳动走访进口的策动,是集群外 Nginx 为统共这个词 Kubernetes 集群共用,是以 Nginx 的走访量很高,这也导致 Nginx 向后端的 upstream(即 Service IP)发起聚拢时,表面上源端口重用的概率较高(事实上经过合手包不雅察,如实几分钟内就会不雅察到屡次端口重用的慷慨),因而更容易出现五元组叠加的情况。
第二次,一样的对比,此次加了并发,双方的案例王人复现了问题。这么,和上头著作中的场景雷同,由于加了并发,发布 ab 申请的机器,也出现了源端口不及而重用的情况,因此也复现了问题。
而认真环境出现的问题响应,和我第一次实验通过 Nginx 走访得到复现,是归拢个原因,天然单个诈欺的申请量远莫得达到能够触发五元组叠加的量级,然而集群中的统共诈欺申请量加起来,就会触发此问题。
处理决议几种处理决议,上头援用的著作中也王人提到,另外可参考isuue 81775,对这一问题及关联的处理景色有好多的商讨。
鉴于面前咱们的本领才能和集群限制,暂时无法也无需进行 linux 内核级别的功能修改和考据,而况调研了业务诈欺,绝大部分以短聚拢为主,咱们领受了一种约略径直的景色,在一定程度上幸免该问题。开辟一个自界说的程度,并以 Daemonset 的景色部署在每个 Kubernetes 的每个节点上。该程度通过 informer 机制监听集群 Endpoint 的变化,一朝监听到事件,便取得 Endpoint 过甚对应 Service 的信息,并由此找到其在本节点上对应产生的 IPVS 律例,要是发当今 Virtual Service 下有 weight 为 0 的 Real Service,则立即删除此 Real Service。然而这一处理景色,弗成幸免的阵一火了部分优雅退出的脾性。然而在详细了业务诈欺的特色衡量之后,这如实是面前可接纳的一种处理景色(天然极其不优雅)。
想考是否应该如斯使用 Service?
回归问题的原因,在咱们单一业务的申请量远未达到会触发五元组叠加这种小概率事件的瓶颈时,过早的遭受这一问题,和咱们对 Kubernetes 劳动的进口网关的策动有很大关系,买通 Service 和诬捏机的收集,使用外部 Nginx 算作进口网关,这种用法,在 Kubernetes 的试验中应该算辱骂常突出(以致可称为仙葩),然而这还是营,亦然由于面前业务的实例,存在无数诬捏机和容器混布的场景。教养是,在践诺和诞生 Kubernetes 这种复杂系统时,尽量紧靠社区及大厂公开的分娩最好试验,减少仅凭资格的或机械延用的景色进行架构策动,不然很容易踩坑,事倍功半。
小宝 探花