本文导读:

  • 微服务技术架构选型介绍
  • 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 输出指定的字段

执行结果:

来段文本:

[root@mas-manager-eureka-es1-66cb79bfb7-snmxm 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

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

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

  1. 搭建高可用的Eureka注册中心

    搭建高可用的Eureka注册中心 一.搭建高可用的Eureka的作用 当服务器因种种原因导致Eureka注册中心(后面简称Eureka)服务当机(服务器跪了,异常关闭停止服务).这样就会影响到整个业务 ...

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

    在演示spring cloud之前得要知道我们为什么需要微服务框架. 先讲讲我的经历,以前我们做项目时所有功能都写在一起,只是做了分层(模型,数据,业务),所有业务逻辑都写在业务层,刚开始还好,等时间 ...

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

    在虚拟机安装ubuntu12.04自动获取IP 一切都没有问题 ssh连接也正常.关机重启后郁闷的发现网络已经不通了,于是开始了以下的摸索. 1.配置静态IP 网关: ip段: 命令: Vim /et ...

  4. tcp/ip网络里的客户端和服务器端 信息交流 与 安全

    ISP(Internet Service Provider) 互联网服务提供商, 即向广大用户综合提供互联网接入业务.信息业务.和增值业务的电信运营商. 通过wireshark学习tcp/ip. 用w ...

  5. IP网络设计

    一.总体规划 网络设计的分层思想 按照网络设计的分层思想,通常将网络分为:核心层.汇聚层和接入层三个部分.这三部分在功能上有明显差别 ,因此在IP设计上,有必要对这三个部分区别对待. 二.核心层 核心 ...

  6. Linux学习(1)- TCP/IP网络协议基础

    Linux学习(1)- TCP/IP网络协议基础 一.TCP/IP 简介 学习内容 TCP/IP(Transmission Control Protocol/Internet Protocol)是传输 ...

  7. TCP/IP网络知识点总结

    学完了计算机网络是时候整理一篇总结了,温故知新.注意:这篇博客很长长长(2.5万字+50图). TCP/IP网络知识点总结 一.总述 1.定义:计算机网络是一些互相连接的.自治的计算机的集合.因特网是 ...

  8. TCP/IP网络编程之优于select的epoll(一)

    epoll的理解及应用 select复用方法由来已久,因此,利用该技术后,无论如何优化程序性能也无法同时接入上百个客户端.这种select方式并不适合以web服务端开发为主流的现代开发环境,所以要学习 ...

  9. TCP/IP网络编程之优于select的epoll(二)

    基于epoll的回声服务端 在TCP/IP网络编程之优于select的epoll(一)这一章中,我们介绍了epoll的相关函数,接下来给出基于epoll的回声服务端示例. echo_epollserv ...

随机推荐

  1. MSIL实用指南-生成while语句

    本篇讲解怎样生成while语句.while语句是编程语言中很重要的循环语句,它的结构是while(<表达式>) <语句或语句块> 当表达式的结果为true时就一直执行语句或语句 ...

  2. python控制窗口口字形运动

    import win32con import win32gui import time import math notepad = win32gui.FindWindow("Photo_Li ...

  3. Elasticsearch 在docker和centos下的安装教程

    前言 新版本的Elasticsearch不能以root用户来运行.因此,MAC下建议使用Docker来安装. 国内各版本镜像:点击这 Centos7.4 64位 第一步 下载.tar.gz的安装包 不 ...

  4. P3317 [SDOI2014]重建 变元矩阵树定理 高斯消元

    传送门:https://www.luogu.org/problemnew/show/P3317 这道题的推导公式还是比较好理解的,但是由于这个矩阵是小数的,要注意高斯消元方法的使用: #include ...

  5. hdu2222 Keywords Search (AC自动机板子

    https://vjudge.net/problem/HDU-2222 题意:给几个模式串和一个文本串,问文本串中包含几个模式串. 思路:贴个板子不解释. #include<cstdio> ...

  6. yzoj P2044 数字游戏 题解

    题意 dfs骗了30分,一开始想的距离正解差一点啊,贪心加dp就可以过的水题,真正太蒻了 解析 代码 #include<bits/stdc++.h> using namespace std ...

  7. 【Offer】[13] 【机器人的运动范围】

    题目描述 思路分析 Java代码 代码链接 题目描述 地上有一个m行和n列的方格.一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和 ...

  8. Java,哈希码以及equals和==的区别

    从开始学习Java,哈希码以及equals和==的区别就一直困扰着我. 要想明白equals和==的区别首先应该了解什么是哈希码,因为在jdk的类库中不管是object实现的equals()方法还是S ...

  9. Java中存储金额用什么数据类型

    Java面试高频问题:你会用什么数据类型来存储金额? 如果这个时候你回答float,double那么恭喜你,又可以省出时间来准备别的公司的面试了,当面试官说float,和double不行的时候你可能还 ...

  10. Spring boot出现Cannot determine embedded database driver class for database type NONE

    在spring boot项目中,我们在pom.xml文件中添加了mysql和mybatis的依赖,我们常常遇到下面这样的问题: Description: Cannot determine embedd ...