记一次k8s压测发生SLB 499的串流问题

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 对k8s集群中的pod进行压测,压测方式是直接访问k8s前的SLB, 压测表现是 SLB (CLB 7层监听)偶发返回499报错。 最终确认问题根因是五元组复用导致串流。关键词: 偶发499 、压测、k8s

问题描述

阿里云ACK集群中的pod进行压测,压测方式是直接访问集群前的SLB, 压测表现是 SLB (CLB 7层监听)偶发返回499报错。


补充抓包后看到的表现:SLB后端抓包表现是,SLB传到后端的SYN包被后端服务节点直接ACK而结束三次握手,建联失败。


关键词: 偶发 、压测、SYN-ACK、ExternalTrafficPolicy:Cluster、七层SLB


环境

数据流向:

七层SLB -> Node: NodePort ( Svc ExternalTraffic:Cluster) -> Pod

环境:

集群版本:阿里云ACK 1.20.11-aliyun.1

节点操作系统:Aliyun Linux 2

SLB: CLB 7层监听

SLB 后端:手动挂的nodeport类型svc, 且ExternalTrafficPolicy:Cluster

架构:

SLB后端手动挂多集群的nodeport类型svc, 实现多集群的负载均衡。(此处不对业务架构设计做讨论)


问题分析

异常复杂网络问题,且偶发可复现,自然要抓包分析。但是该类问题通常只有tcpdump抓包是不够的,本文略掉中间的摸索过程,直接上有效的所有分析步骤。


1. 抓包准备:

为便于抓取后端数据,测试环境中slb svc 后缩容只有一个pod。

1) Node进行tcpdump抓包(200M一个 抓10个包 问题复现之前涵盖五分钟):

sudotcpdump-iany-nnvv-C200-W10-w/alios_499.pcap

2)Node抓ipvsadm 以及contrack表项状态 ;Pod抓netstat状态综合分析:

//podforiin{1..2000}doecho$(date)>>node.txtsudoipvsadm-Ln-c|egrep"nodeip:nodeport">>node.txtsudoegrep"nodeport"/proc/net/nf_conntrack>>node.txtsleep1sdone//nsenterpodnsenter-n-p<pid> ,podnetstatforiin{1..2000}doecho$(date)>>pod.txtnetstat-antpl|greppod-port>>pod.txtsleep1sdone

2. 问题复现:

通过监控SLB 499的日志,确认问题复现时间2022-xx-xx 17:05:42

则之前部署的有效抓包时间段 2022.xx.xx 17:00:42-17:06:01


3. 数据包分析:

1) 如何从海量数据包中找到问题数据流

方法一: 使用wireshark- statics->conversation -> limit to display-》packets排序找 “1”  

由于三次握手没完成,SYN后没收到SYN,ACK, 因此使用 tcp.flags.syn == 1以及node/pod ip 过滤,找非成对单数的数据流,可以定位看到SLB源端口33322 , node 源端口30552  对应的数据流符合条件:


进而使用过滤条件 tcp.port == 33322 or tcp.port == 30552 可以定位到问题数据流 tcp.stream in {24182  24183},问题时间戳是17:05:41。

SLB-NODE是一个stream 24182, node-pod是新的stream 24183,其实只分析node向后转发给pod的24183即可。



方法二:当数据包很大时,可使用命令行tshark分析

过滤条件tcp.flags.syn == 1 and (ip.src == 10.1.72.105 or ip.dst == 10.1.72.105)

tshark -t ad -r alios_499.pcap6 -Y"tcp.flags.syn == 1 && (ip.src == 10.0.1.107 || ip.dst== 10.0.1.107) && (ip.src == 10.1.72.105 || ip.dst== 10.1.72.105)"-T fields -e"tcp.stream"-e"frame.number"-e"ip.addr"-e"tcp.port"| awk'{print $1}' |sort|uniq -c |awk '{ if ( $1 % 2 != 0) print $2 }'



基于上述两种方法,找到SLB 499对应的异常数据流为tcp.stream in {24182  24183},问题时间戳是17:05:41,看数据链路的端口为:

stream 24182:SLB源端口33322 -> Node目的端口:30718

stream 24183:Node源端口30552-> Pod目的端口7001


小知识:

  • 由于SLB是7层,因此数据包流经SLB后,SLB会跟后端新起一个新stream,源IP 会SNAT修改为SLB内部IP后转发给后端。因此抓包看不到源客户端IP,而是SLB的IP。
  • ExternalTrafficPolicy:Cluster 模式,数据包经过node转发给pod时,会被SNAT, 源IP被SNAT为node IP,因此节点上抓包会有两个stream (SLB-NODE ; NODE-POD)。
  • 同一条数据流流经 SLB-Node-Pod 三个环节时的tcp.seq相同,也可以先找到node->pod的异常包后根据tcp.seq找slb-node包。


2)查node的ipvs连接以及contrack表定位

针对异常stream 24183:Node源端口30552-> Pod目的端口7001,可以发现,在异常数据包于17:05:41到达之前有一条使用相同端口五元组的“TIME_WAIT”状态的连接还没结束。新的SYN包经node转发给pod时,复用了端口30552,导致node到pod的两次数据流五元组相同 。查pod的netstat,也可以看到异常数据包到达pod时,前一条五元组相同的连接处于 “TIME_WAIT”。


基于节点内部ipvs/conntrack表的分析,最终找到node -pod 发生串流的两个 stream:

tcp.stream in {19697 24183}  //NODE-POD的stream

17:05:36TIME_WAIT100.117.95.144:9742-10.0.1.1079742->SNAT->10.0.1.10730552->TCP->10.1.72.105:700117:05:41SYN_SENT100.117.95.189:33322-10.0.1.10733322->SNAT->10.0.1.10730552->TCP->10.1.72.105:7001




数据分析详情:

  • 查node的contrack表:

可以发现node-pod这一段的五元组(红框中所示)中,17:05:36的表项正处于TIME_WAIT ,对应的SLB source 是100.117.95.144:9742 (slb-node段没重复);5秒后17:05:41的表项是新的stream,状态为SYN_SENT (该表项可以看出问题数据流是node-pod五元组重复的数据流),对应的slb source是100.117.95.189:33322。



  • 查node的ipvs连接:

17:05:36的时候,在ipvs的session 中该五元组正在倒计时 01:58,还没结束。五秒后17:05:41新的session来的时候,前一个五元组倒计时还剩01分54秒。因此新syn包命中五元组重复的场景。



  • 分析 pod中 netstat

可以看到pod:7001到node:30552端口的连接处于time_wait.


小知识:

  • 五元组为 【源IP+源端口+协议+目的IP+目的端口】 组合。


  • ExternalTrafficPolicy两种模式解析:

若SLB后端nodeport类型的svc采用ExternalTraffic:Cluster模式,SLB转发的数据包在经node转发给pod时会被SNAT,传入pod的数据流的源IP会变为node IP。节点的端口有限,因此在压测流量大的场景中,对于相同的pod ip:pod port,很容易发生nodeip:node port 五元组重复。


若SLB后端nodeport类型的svc采用ExternalTraffic:Local模式,SLB转发的数据包经node只会转发给本机pod,且数据包不会被SNAT,传入pod的数据流的源IP源端口依旧是SLB的IP跟端口。由于SLB是公有云的7层负载均衡,底层实则是整个集群做支撑,因此slb的源端口不容易发生重复。在以上抓包看到node-pod五元组重复的场景中也可以看到,slb-node的stream中,slb port其实每次都不同。


  • TIME_WAIT:TCP连接中断过程中,主动关闭的一方在发送最后一个 ack 后就会进入 TIME_WAIT 状态停留2MSL(max segment lifetime),这个是TCP/IP链接关闭过程中必不可少的状态。如果在TIME_WAIT 状态继续收到相同五元组的SYN数据包,协议栈如何处理?本案例中是服务端回复SYN包一个ACK结束建联。



方案建议


本文不讨论架构优化,针对本次案例中串流问题的方案建议:

image.png

  1. 针对五元组重复的串流问题,通用解法是采用 externalTrafficPolicy =Local,这样node-pod的数据包不会被SNAT为node的ip:port,那么pod接收到数据包都会是slb的IP:PORT,由于slb位于公有云七层负载均衡的集群中,slb ip通常也是整个集群ip列表中的一个,就可以大大减少出现ip跟port都重复的概率。


  1. 若是terway模式的集群,SLB后端可以采用terway eni模式直接挂pod eni在SLB后端,slb 转发的数据包可以不用在node做周转,省去了ipvs/iptables这一层转换,slb后直达pod,除了对性能也可以有所提升,也可以避免被node snat后五元组重复的串流问题。


  1. 该问题只有在流量很密集的场景中偶发,比如上文中的压测场景,正常业务场景中很少命中这个问题,若不做配置改动,可视业务需求而忽略。



结尾

本次问题排查中参考k8s网络诊断之我的流量去哪了中对ipvs session/ conntrack表状态的跟踪,也对海量网络报文快速分析定位方法做了讲解。


相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
1月前
|
负载均衡 Kubernetes 算法
K8s服务发现与负载均衡的技术探索
【7月更文挑战第15天】K8s通过Service资源对象和kube-proxy组件实现了高效、灵活的服务发现和负载均衡机制。通过合理选择Service类型、优化kube-proxy配置以及使用Ingress进行高级路由,可以确保应用在K8s集群中高效、可靠地运行。随着云原生技术的不断发展,K8s的服务发现和负载均衡机制也将不断完善和优化,为更多场景提供强大的支持。
|
3月前
|
Kubernetes 负载均衡 应用服务中间件
k8s 二进制安装 优化架构之 部署负载均衡,加入master02
k8s 二进制安装 优化架构之 部署负载均衡,加入master02
|
3月前
|
存储 Kubernetes 应用服务中间件
容器服务ACK常见问题之SLB公网改成ALB失败如何解决
容器服务ACK(阿里云容器服务 Kubernetes 版)是阿里云提供的一种托管式Kubernetes服务,帮助用户轻松使用Kubernetes进行应用部署、管理和扩展。本汇总收集了容器服务ACK使用中的常见问题及答案,包括集群管理、应用部署、服务访问、网络配置、存储使用、安全保障等方面,旨在帮助用户快速解决使用过程中遇到的难题,提升容器管理和运维效率。
|
3月前
|
Kubernetes 负载均衡 应用服务中间件
k8s学习-CKA真题-七层负载均衡Ingress
k8s学习-CKA真题-七层负载均衡Ingress
78 0
|
3月前
|
Kubernetes 负载均衡 应用服务中间件
k8s学习-CKA真题-负载均衡service
k8s学习-CKA真题-负载均衡service
54 0
|
11月前
|
Kubernetes 负载均衡 网络协议
青云LB(负载均衡)与k8s实战(一)
青云LB(负载均衡)与k8s实战(一)
242 0
青云LB(负载均衡)与k8s实战(一)
|
运维 负载均衡 Kubernetes
云原生时代,如何从 0 到 1 构建 K8s 容器平台的 LB(Nginx)负载均衡体系
通过本文可以对 Kubernetes 容器平台的 LB(Nginx)负载均衡了然于心,并且可以快速深入建设 Kubernetes LB(Nginx)负载均衡体系。还可以了解到,一个中大型公司,是如何从 0 到 1 来构建大规模 Kubernetes 容器平台的 LB(Nginx)负载均衡体系的一些非常宝贵的实战经验。
云原生时代,如何从 0 到 1 构建 K8s 容器平台的 LB(Nginx)负载均衡体系
|
11月前
|
Kubernetes 负载均衡 应用服务中间件
青云LB(负载均衡)与k8s实战(二)
青云LB结合Ingress实现访问k8s集群内部容器服务。
350 0
|
Kubernetes 监控 Cloud Native
K8s Ingress 为什么选择 MSE 云原生网关?|学习笔记(二)
快速学习 K8s Ingress 为什么选择 MSE 云原生网关?
233 0
K8s Ingress 为什么选择 MSE 云原生网关?|学习笔记(二)