这个注册的 IP 网络都不通了,Eureka 注册中心竟然无法踢掉它!

本文导读:

  • 微服务技术架构选型介绍
  • k8s 容器化部署架构方案
  • Eureka 注册中心问题场景
  • 问题解决手段及原理剖析

阅读本文建议先了解:

  1. 注册中心基本原理
  2. K8s(Kuberneters)基本概念


我们的微服务目前都是在服务器上部署的,也是基于 Docker 来部署的。

运维部门基于 K8s 自研了一套容器云管理平台,平台名称叫做 Ares,我们也开始准备将微服务迁移到这平台上,降低虚拟机或实体机服务器运维成本,提高服务器资源利用效率。

Ares:阿瑞斯(战神)

希腊神话中为战争而生的神,奥林匹斯十二神之一,被视为尚武精神的化身。看起来很牛逼的样子!

1、微服务技术架构选型介绍

微服务框架使用了流行的 Spring Cloud 框架。

框架技术组件如下:

  • 注册中心选择的是 Eureka
  • 网关使用的 Zuul
  • 配置中心使用的 Apollo
  • 熔断限流使用了 Sentienl + Hystrix

网关 Zuul 层使用了 Ribbon 做负载均衡、Hystrix 做限流熔断。
后端微服务使用了阿里巴巴开源的 Sentinel 做限流熔断。

由于当时服务器的配置不同,比如有低配置的虚拟机,还有高配置的物理机服务器。

所以呢,我们基于当时的服务器配置现状,基于 Ribbon 自行扩展了按照权重的负载均衡策略,对 Eureka 注册中心管理界面做了一点改造,能够支持动态对每台服务器变更权重。

因为本文的问题跟 Eureka 注册中心有关,对 Eureka 架构做个介绍下。

Eureka 注册中心简易架构图:

上图简要描述了 Eureka 的基本架构,由3个角色组成:

1)Eureka Server

提供服务注册、发现、健康检查。

2)Service Provider

服务提供方,
将自身服务注册到 Eureka,从而使服务消费方能够找到,
我们将容器可以作为服务提供者,会注册到 Eureka。

3)Service Consumer

服务消费方,
从Eureka获取注册服务列表,从而能够消费服务
我们可以将 Zuul 网关作为服务消费者。

2、K8s 容器化部署架构方案

考虑到使用的 Spring Cloud 框架,结合运维提供的容器平台。

制定容器化部署架构如下:

从容器创建到可访问流程:

1)创建容器

选择镜像及版本、CPU、内存配置、配置健康检查、日志收集、Pod 副本数量。提交创建容器。

2)服务注册

容器启动时,申请 SLB VIP,作为服务注册 IP,向 Eureka 上发起注册。

3)网关发起请求

域名请求,DNS 解析经过 GSLB(全局软负载均衡)负载到 Zuul 网关,Zuul 网关从 Eureka 注册中心拉取服务注册表,通过 Ribbon 负载均衡,从本地服务注册列表中,选择其中一台 Server,发起 Http 调用。

4)容器提供服务

容器内注册的是 SLB VIP(软负载均衡),这个 SLB 通过内部的 Nginx 负载均衡机制,轮询到后端的容器的多个 Pod IP 上,Pod IP 正是我们部署的微服务业务。

为什么要使用SLB VIP呢?

当时我们对接口压测时,发现使用 K8s 内部的 Service IP 存在性能瓶颈,该问题还在研究中。后来运维内部商榷,使用 SLB 来达到负载均衡的效果。

另外说明一点:
运维基于 K8s 自研的这套容器平台,网络层面做了重新架设和优化,打通了各个机房的网络。

这样做给我们的架构部署带来了好处:
前期目标仅为了迁移微服务业务,考虑到稳定性等因素,正式上线的Zuul网关和Eureka 注册中心部署在 K8s 集群外,微服务业务部署在容器内,因网络可通,容器启动后申请的 VIP,可以直接注册到 Eureka 上。

仿真环境(预上线环境)是直接将Eureka注册中心,也部署在了容器平台中,接下来会说下,因此导致的一些问题,以及解决该问题的方式。

** 3、Eureka 注册中心问题场景**

容器测试阶段结束,由于运维调整为了 SLB VIP,将以前的应用(一个应用下包含多个 Pod 容器)都删除掉,我们重新搭建一套仿真环境用于,上线前的性能测试环境。

但是当我们部署完 Eureka 后,发现以前删除掉的应用VIP 也注册上来了,而且这个 VIP 网络是不通的,无法访问的。

Eureka 管理控制台示意图:

telnet 命令测试:

telnet 10.11.195.197 80
Trying 10.11.195.197...
telnet: connect to address 10.11.195.197: Network is unreachable
telnet: Unable to connect to remote host

结果提示 10.11.195.197 这个 VIP,网络是不可达的。

那为什么这个服务能注册上来呢?

起初,跟运维老哥请教,经过容器内排查后,也暂时没有太多眉目,确定是这个 VIP,已经下线了,网络也不通。

按照这个推测,是不太可能注册到 Eureka 上来的。

开始考虑到以为是 Eureka 机制是不是有问题,但仔细用「屁股」猜想论思考一下,结合 Eureka 框架底层原理来看,是不应该出现这个情况。

根据 Eureka 续约机制,一定是有哪个「哥们」在默默给这个服务 IP 发续约(向注册中心发送心跳)。

我们在 Eureka Server 服务端,也有监听各个动作的机制,如注册服务、续约服务、下线服务,根据日志看,也的确是有这个服务 IP 一直在发送续约动作。

续约监听代码:

@EventListener
public void listen(EurekaInstanceRenewedEvent event) {
    InstanceInfo instanceInfo = event.getInstanceInfo();
    if (instanceInfo != null) {
        logger.info("renew ...." + instanceInfo.getInstanceId());
    } else {
        logger.info("renew ....instanceInfo is null");
    }
}
4、问题解决手段及原理剖析

既然引出了上述问题,当然不能放任不管,一定要一探究竟。
这种问题你若不理他,早晚会搞出点别的事情来的。

Eureka 服务端已经收到了注册和一直续约的请求,说明一定是有哪个服务一直在偷偷发送心跳。

到底是谁干的啊?

到底如何找到这个上报的服务呢?

运维老哥暂时比较忙,看来只能先查找网络链路,抓取网络数据包看看到底是怎么回事了。

网络工具一般常用的就是 tcpdump、Wireshark。

Wireshark 小故事:
大概发生在 10 几年前,主导 Ethereal(应该听说过吧)的大佬跳槽了,然后这个商标就不能继续使用了,但是这个工具在当时来说人气很旺,后来大佬就将项目更名为 Wireshark 了。
服务器上命令行的抓包程序 tethereal 更名为了 tshark。

容器镜像中默认是不会自带这些工具的。镜像中 Linux 操作系统使用的是 CentOS,通过自带的 yum 源安装网络工具包,比较方便。

安装 wireshark:

yum install -y wireshark

安装 tcpdump:

yum install -y tcpdump

这里我们使用的是 Wireshark 工具,简单介绍下这个工具:

如果你要看到全部网络数据包,直接执行tshark命令即可。

1)获得 tshark 命令帮助

tshark --help

2)tshark 抓包模式参数一览

3)tshark 命令实战使用

打印源目标 Host 及 Http 协议信息:

tshark -s 512 -i eth0 -n -f ‘tcp dst port 80‘ -t ad -R ‘http.host and http.request.uri‘ -T fields -e "frame.time" -e "ip.src" -e "http.host" -e "http.request.method" -e "http.request.uri" | tr -d ‘ ‘

参数解释:

-i 捕获 eth0 网卡;

-n 禁止所有地址名字解析(默认为允许所有)

-t 设置解码结果的时间格式。
"ad"表示带日期的绝对时间,"a"表示不带日期的绝对时间,"r"表示从第一个包到现在的相对时间,“d”表示两个相邻包之间的增量时间(delta)

-R 设置读取(显示)过滤表达式(read filter expression)

-T -e 输出指定的字段

执行结果:

来段文本:

[[email protected] manager]# tshark -n -t a -R http.request -T fields -e "frame.time" -e "ip.src" -e "http.host" -e "http.request.method" -e "http.request.uri" | grep 10.11
Running as user "root" and group "root". This could be dangerous.
Capturing on eth0
Sep 27, 2019 00:22:05.174770971 10.124.12.169   10.124.14.4     PUT     /eureka/apps/LETV-MAS-CALLER-TVPROXY-USER/10.11.195.197:80?status=UP&lastDirtyTimestamp=1569490783397
Sep 27, 2019 00:22:13.814821143 10.124.11.125   10.124.14.4     PUT     /eureka/apps/LETV-MAS-CALLER-TVPROXY-USER/10.11.195.197:80?status=UP&lastDirtyTimestamp=1569407741389
Sep 27, 2019 00:22:15.180243816 10.124.11.123   10.124.14.4     PUT     /eureka/apps/LETV-MAS-CALLER-TVPROXY-USER/10.11.195.197:80?status=UP&lastDirtyTimestamp=1569490783397

通过抓包,根据问题 IP 过滤得到的结果,我们看到了
/eureka/apps/LETV-MAS-CALLER-TVPROXY-USER/10.11.195.197:80?status=UP&lastDirtyTimestamp=1569490783397
说明是有 IP 在上报,上报来源 IP 就是第二列的 IP。

将这个信息提供给运维同学,根据来源 IP 去继续查找线索。

但是,发现第二列 IP 并不是实际的服务器节点 IP,查不到。因为这些 IP 都是主机上的虚拟 IP,每次上报来源 IP 不同,所以还要反向查找实际归属的主机节点。

为什么要有虚拟 IP?

实际是 SLB 节点上虚拟 IP,因为会负载到它所属主机节点,这台主机上默认只能支撑最大 65535 个 TCP 连接,所以为了单机能支撑更高的 TCP 连接数,会虚拟出来很多个 IP。假设有 10 个虚拟 IP,每个虚拟 IP 支撑 65535 个 TCP 连接,这台主机总共可以支撑 10 * 65535 = 60万以上的连接数了。

K8s 有多套集群,每个集群中有很多台主机节点,茫茫主机池中怎么去查找这些虚拟 IP 呢,

其实我们的目的是为了找到,谁往注册中心发送请求了,还是可以继续通过抓取网络数据包来定位这个问题。

这些网络数据包一定会经过 K8s 集群里的某一台节点,一台一台去找,很麻烦,编写简单脚本抓包查找

#!/bin/bash
tcpdump -i any host 10.124.14.4 -n -s 0 -X -l | grep 10.11.195>/tmp/1.txt &
sleep 20s
kill -2 %1
cat /tmp/1.txt

网络抓包结果:

截取了关键的抓包信息:

11:41:56.598204 IP 10.110.157.81.54078 > 10.124.14.4.http: Flags [P.], seq 273:622, ack 218, win 245, options [nop,nop,TS val 3348483954 ecr 1420800289], length 349: HTTP: PUT /eureka/apps/LETV-MAS-CALLER-TVPROXY-USER/10.11.195.197:80?status=UP&lastDirtyTimestamp=1569394834392 HTTP/1.1

这里的 10.110.157.81.54078 就是主机节点 IP,目标地址 10.124.14.4 就是容器内的 Eureka 注册中心地址。

发送的请求是 /eureka/apps/LETV-MAS-CALLER-TVPROXY-USER/10.11.195.197:80?status=UP&lastDirtyTimestamp=1569394834392
这个请求地址上就带了 10.11.195.197 这个网络不可达的 IP 地址。

到这里,其实我们已经基本定位到了,一定是从 K8s 集群容器内发出来了。
根据这个有价值的节点信息,连接到 K8s 集群内,查找该节点上部署的容器。

查找 K8s 集群内 Pod 命令行:
kubectl get pod --all-namespaces -o wide |grep 10.110.157.81

部署在改节点上的容器:

运维根据 Eureka 上名称大概猜测一下,终于找到这个「罪魁祸首」的容器了。
进入容器内,查看配置 SVIP (Eureka上的注册IP)就是 10.11.195.197 这个IP。

将这个问题容器彻底关闭后,没有再继续发送续约请求,Eureka 注册中心上过了一段时间摘掉了 IP。

大家可能有疑问,这么繁琐,为啥不直接到 K8s 集群内去找,因为 K8s 集群内目前已有业务在运行着,集群内有几百个容器在跑着。当时运维一起测试时,容器名称都是自定义的,所以不是很好查找。

咱们经过这个过程的排查,确认了这个 Eureka 注册中心上的地址虽然不通,但是一直是有容器在上报,而上报的「ServerId」指向的 10.11.195.197:80 地址。

我们也可以结合底层源码了解下。

Eureka 续约时序图:

接口实现方式跟注册服务类似,更新自身状态后,会同步到其他集群节点。

PeerAwareInstanceRegistryImpl 类的 renew 方法会调用到 AbstractInstaceRegistry 抽象实例注册类的 renew 方法。

AbstractInstaceRegistry#renew 方法源码:

会根据服务 id 从注册表中获取 Lease 对象,如果不为空,则完成续约,更新 lastUpdateTimestamp 字段。

Eureka 注册表的数据结构:

private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
= new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();

是一个 ConcurrentHashMap 结构,Key 就是应用名称,Value 也是一个 Map 结构。
Map 结构中的 Key 是注册ID(IP + 端口),Value 是 Lease 服务续约对象,里面包含了动作类型,最后上报(心跳)更新时间戳等等信息。

容器服务作为 Eureka Client,每隔一定时间间隔(默认60秒)向注册中心发起一次续约。

Eureka Server 会定时检测服务实例心跳是否正常,如果间隔一定时间(90秒),还没有来续约,就会将这个服务从注册中心摘除掉。

最后总结:

总结上述分析过程,一图胜千言:

重要的不是结果,而是这个过程,希望你也能享受这个过程。

参考资料:

Wireshark 使用文档:
https://www.wireshark.org/docs/man-pages/tshark.html

Netflix Eureka 源代码:
https://github.com/netflix/eureka

欢迎关注我的公众号,更多精彩文章,与你一同成长,扫码二维码关注~

原文地址:https://www.cnblogs.com/ldws/p/11600555.html

时间: 2024-10-22 17:06:50

这个注册的 IP 网络都不通了,Eureka 注册中心竟然无法踢掉它!的相关文章

SpringCloud系列(一):Eureka 注册中心

在演示spring cloud之前得要知道我们为什么需要微服务框架. 先讲讲我的经历,以前我们做项目时所有功能都写在一起,只是做了分层(模型,数据,业务),所有业务逻辑都写在业务层,刚开始还好,等时间长了,代码量大,各业务代码之间有交集维护起来超级麻烦,每次改动都担心会不会对其他模块造成影响,只改动一个模块也要停系统发一次包,我们以前包含了订单,支付,会员,预定,库存,物流等模块,时间越长心里越慌,出问题的频次也越高,就这样恶性循环.后来我们尝试着把会员剥离了出去,发现效果还不错,后来慢慢的都一

ubuntn 虚拟机NAT 静态IP 网络配置

在虚拟机安装ubuntu12.04自动获取IP 一切都没有问题 ssh连接也正常.关机重启后郁闷的发现网络已经不通了,于是开始了以下的摸索. 1.配置静态IP 网关: ip段: 命令: Vim /etc/network/interface auto lo iface lo inet loopback auto eth0 # Assgin static IP by eric on 26-SEP-2012 iface eth0 inet static address 192.168.91.200 #

理解TCP/IP网络栈&amp;编写网络应用(下)

1.摘要 这是<翻译:理解TCP/IP网络栈&编写网络应用>的下篇,文章中会通过讲解TCP的代码实现帮助大家理解发送.接收数据的流程,也描述了一些网卡.驱动等网络栈底层的原理. 原文地址:原文地址 原作者:Hyeongyeop Kim 2.数据结构 以下是一些关键数据结构.我们了解一下这些数据结构再开始查看代码. 2.1.sk_buff_structure 首先,sk_buff结构或skb结构代表一个数据包.图6展现了sk_buff中的一些结构.随着功能变得更强大,它们也变得更复杂了.

【TCP/IP网络编程】:06基于UDP的服务器端/客户端

本篇文章简单描述了UDP传输协议的工作原理及特点. 理解UDP UDP和TCP一样同属于TCP/IP协议栈的第二层,即传输层. UDP套接字的特点 UDP的工作方式类似于传统的信件邮寄过程.寄信前应先在信封上填好寄信人和收信人的地址,之后贴上邮票放进邮筒即可.当然信件邮寄过程可能会发生丢失,我们也无法随时知晓对方是否已收到信件.也就是说信件是一种不可靠的传输方式,同样的,UDP所提供的也是一种不可靠的数据传输方式(以信件类比UDP只是通信形式上一致性,之前也以电话通信的方式类比了TCP的通信方式

TCP/IP网络协议

什么是协议?协议就是双方约定的规则.同理,在网络中,计算机之间的相互通信需要共同遵守一定的规则,这些规则就称为网络协议. 下面是我百度的定义: TCP/IP是“transmission Control Protocol/Internet Protocol”的简写,中文译名为传输控制协议/互联网络协议)协议, TCP/IP(传输控制协议/网间协议)是一种网络通信协议,它规范了网络上的所有通信设备,尤其是一个主机与另一个主机之间的数据往来格式以及传送方式.TCP/IP是INTERNET的基础协议,也

IP网络存储ISCSI

指导老师:双星科技曾勇老师 一.ISCSI的工作原理 1.1.iSCSI的组成一个简单的iSCSI系统大概是由以下部分组成:    iSCSI Initiator 或者iSCSI HBA    iSCSI Target    以太网交换机    一台或者多台服务器 1.2.iSCSI Initiator iSCSI Initiator是一个安装在计算上的软件或者硬件设备,它负责与iSCSI存储设备进行通信iSCSI 服务器与iSCSI存储设备之间的连接方式有两种:第一种基于软件的方式:是iSCS

【linux基础】17、TCP/IP网络基础(下)

一.IP地址 1.IANA IANA 就是指(Internet Assigned Numbers Authority) ,Internet号分配的机构.负责对IP地址分配规划以及对TCP/UDP公共服务的端口定义. 2.IP地址的基本格式 IPv4使用32位(4字节)地址,因此整个地址空间中有4,294,967,296(2^32)个地址,也就是近43亿个地址.IPv4地址在计算机内部是以二进制形式表示,是一个32位的二进制数,通常被分割为4个"8位二进制数"(也就是4个字节). IP地

TCP/IP 网络编程 (抄书笔记 2) -- UDP

TCP/IP 网络编程 (抄书笔记 2) – UDP TCP/IP 网络编程 (抄书笔记 2) – UDP Table of Contents server client connect 来源: <TCP/IP 网络编程> 抄书: TCP 协议若要向 10 个客户端提供服务, 除了需要 listen 套接字外, 还需要 10 个服务器端套接字 (accept), 但是在 UDP 中, 不管是服务器端还是客户端都只需要 1 个套接字 udp 的 client 不需要 bind, 调用 sendt

TCP/IP网络编程系列之三

TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值,而是为了区分程序中创建的套接字而分配给套接字的序号. 网络地址 网络地址分为IPV4和IPV6,分别你别为4个字节地址簇和6个字节地址簇.IPV4标准的4个字节的地址分为网络地址和主机地址,且分为A.B.C.D.E 等类型.一般很少用到E类型.如下图所示:net-id指网络ID,host-id指主机