
面前部署在 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 这种复杂系统时,尽量紧靠社区及大厂公开的分娩最好现实,减少仅凭阅历的或机械延用的风光进行架构筹画,不然很容易踩坑,事倍功半。