• 漏了监控:Zabbix对Eureka instance状态监控


    背景

    在正式开始介绍监控之前,我们先来了解下我们微服务架构使用的两个组件。

    Eureka

    Eureka作为Spring Cloud的注册中心,主要提供服务注册与发现的能力。

    Eureka 采用 CS(Client/Server,客户端/服务器) 架构,它包括以下两大组件:

    • Eureka Server
      Eureka服务注册中心,主要用于提供服务注册功能。当微服务启动时,会将自己的服务注册到 Eureka Server。Eureka Server 维护了一个可用服务列表,存储了所有注册到 Eureka Server 的可用服务的信息,这些可用服务可以在 Eureka Server 的管理界面中直观看到。

    • Eureka Client
      Eureka 客户端,通常指的是微服务系统中各个微服务,主要用于和 Eureka Server 进行交互。在微服务应用启动后,Eureka Client 会向 Eureka Server 发送心跳(默认周期为 30 秒)。若 Eureka Server 在多个心跳周期内没有接收到某个 Eureka Client 的心跳,Eureka Server 将它从可用服务列表中移除(默认 90 秒)。

    Apollo

    Apollo(阿波罗)是一款可靠的分布式配置管理中心,诞生于携程框架研发部,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

    而Spring Cloud配置中心集成git仓库、svn仓库等配置源统一获取系统配置参数,由Spring Cloud Config Client消费。其配置文件必须遵循严格的语法格式,即使多余的空格都会造成Client 无法读取相应的配置参数,一旦出问题很容易被忽视。

    基于Spring Cloud配置中心这种缺陷,我们使用Apollo进行了逐步的替换,使运维、开发对于不同环境之间的配置文件管理更加方便。

    问题

    借助Apollo可以实现配置修改实时生效(热发布),即用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序。

    Apollo热发布功能满足了我们在更改配置属性需求时,应用可以实时感知。但在使用过程中,如果Eureka Client 自动更新配置时,进行全量更新,则会导致Eureka Server在心跳周期内的健康检查状态如下:

    • client服务发现状态“UNKNOWN”
    • client后台仍然正常运行,无法向Eureka Server发送正常心跳

    由于Eureka Server服务发现状态异常,此时是无法正常对外提供服务的。如果运维不及时检查Eureka管理界面每个client状态时,那么就会差生一起生产事故。

    注意:每个client对应一个instance,下面我们统一将其称为instance。

    需求

    针对以上情况,虽然我们已经对instance接入了健康检查,但由于instance正常运行并没有产生告警,看来我们的监控还是有漏洞,因此我们需要通过Zabbix对Eureka instance状态监控来实现应用监控的全覆盖。。

    思路

    Eureka Server注册的服务以application维度进行分组,每个application下有多个instance。因此我们借助Zabbix的自动发现,通过Eureka API 可以获取所有的分组信息,而不必每次都手动再次添加监控项。

    由于Zabbix监控项是不能重复的,因此我们通过application名/Instance ip地址进行命名,区分不同的instance,这就要求我们的应用不能在一台服务器上部署多个应用,否则会导致监控项重复。

    注意: 其实我们可以通过InstanceId作为区分更合理,但是InstanceId的使用往往不规范,如包含ip、主机名等等,由于字符过长可能导致不必要的麻烦。

    Eureka API

    # 获取Eureka 所有的application
    http://192.168.3.123:1180/eureka/apps
    
    # 获取某个application下所有的instance
    http://192.168.3.123:1180/eureka/apps/application名/
    
    • 1
    • 2
    • 3
    • 4
    • 5

    具体实现

    因为需要解析Eureka API返回的数据,所以我们使用python来解析json数据。

    instance自动发现

    # 执行脚本自动发现application名和Instance ip地址
    python eureka-instance.py discovery
    {
        "data":[
            {
                "{#APP}":"TEST1",
                "{#HOSTNAME}":"192.168.3.10"
            },
            {
                "{#APP}":"TEST1",
                "{#HOSTNAME}":"192.168.3.11"
            }
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    通过获取{#APP}{#HOSTNAME},我们就可以组合成对应命名规则的监控项。

    # 监控项组合
    TEST1/192.168.3.10
    TEST1/192.168.3.11
    
    • 1
    • 2
    • 3

    获取监控项状态

    经过自动发现后的数据,我们可以进一步获取监控项的状态。

    # 1.获取instance 10状态
    python eureka-instance.py status TEST1 192.168.3.10
    # 执行结果
    UP
    
    # 2.获取instance 11状态
    python eureka-instance.py status TEST1 192.168.3.11
    # 执行结果
    UP
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    根据不同Instance的状态,只要结果非“UP”则进行告警。

    最终脚本

    #!/usr/local/miniconda/bin/python
    #-*- coding:utf-8 -*-
    #comment:
    #1.zabbix自动发现eureka instance
    #2.对instance的状态进行监控告警
    
    import requests
    import json
    import sys
    from copy import deepcopy
    
    # 返回json格式数据,否则返回xml格式数据
    headers = {'Accept':'text/html, application/xhtml+xml, application/json;q=0.9, */*;q=0.8'}
    
    def instance_discovery():
        app_list = []
        url="http://192.168.3.123:1180/eureka/apps/" 
        try:
           response=requests.get(url, headers=headers)
           if response.status_code == 200:
               instance_dic = {}
               #for app in response.json()["applications"]["application"][1:2]:
               for app in response.json()["applications"]["application"]:
                   for instance in app['instance']:
                       instance_dic['{#APP}'] = instance['app']
                       instance_dic['{#HOSTNAME}'] = instance['hostName']
                       # 深copy
                       app_list.append(deepcopy(instance_dic))
               #print(app_list)
               #json序列化
               discovery_app_info = {"data":app_list}
               print(json.dumps(discovery_app_info, sort_keys=True, indent=4, separators=(',', ':')))
        except Exception as e:
           print(e)
    
    def instance_status():
        if len(sys.argv) == 4:
            try:
                url="http://192.168.3.123:1180/eureka/apps/%s/" % (sys.argv[2])
                response=requests.get(url, headers=headers)
                if response.status_code == 200:
                    instance_dic = {}
                    for instance in response.json()["application"]["instance"]:
                        if sys.argv[3] == instance["hostName"]:
                            print(instance["status"])
            except Exception as e:
                print(e)
                
        else:
            print("Usage: python eureka-instance.py status app hostName")
    
    
    if __name__ == '__main__':
        if sys.argv[1] == 'discovery':
            instance_discovery()
        elif sys.argv[1] == 'status':
            instance_status()
        else:
            print("Usage: python eureka-instance.py [discovery]|[status app hostName]")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    接入Zabbix

    1.配置文件

    vim eureka.conf
    UserParameter=instance_discovery,/usr/local/miniconda/bin/python /etc/zabbix/monitor_scripts/eureka-instance.py discovery
    UserParameter=instance_status[*],/usr/local/miniconda/bin/python /etc/zabbix/monitor_scripts/eureka-instance.py status "$1" "$2"
    
    
    • 1
    • 2
    • 3
    • 4

    2.自动发现

    在这里插入图片描述

    3.监控项配置

    在这里插入图片描述

    4.告警信息

    # 1.状态为DOWN,发生告警
    告警主机:中间件_eureka_192.168.3.123
    主机IP: 192.168.3.123
    主机组: 中间件_eureka
    告警时间:2022.06.01 14:58:23
    恢复时间:2022.06.01 15:13:24
    告警等级:High
    告警信息:Eureka/TEST1/192.168.3.10:状态为DOWN
    告警项目:instance_status[TEST1,192.168.3.10]
    问题详情:
    TEST1/192.168.3.10: DOWN
    
    当前状态:
    发生告警
    
    
    # 2.状态为UP,恢复告警
    告警主机:中间件_eureka_192.168.3.123
    主机IP: 192.168.3.123
    主机组: 中间件_eureka
    告警时间:2022.06.01 14:58:23
    恢复时间:2022.06.01 15:13:24
    告警等级:High
    告警信息:Eureka/TEST1/192.168.3.10:状态为DOWN
    告警项目:instance_status[TEST1,192.168.3.10]
    问题详情:
    TEST1/192.168.3.10: UP
    
    当前状态:
    告警恢复: UP
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    一篇了解springboot3请求参数种类及接口测试
    【教程】MySQL数据库学习笔记(五)——约束(持续更新)
    尝试改善科研
    IT生活总结(一)
    云计算模式的优势
    猿辅导联合新华出版社打造新一代教辅,助力青少年读写能力提升
    VAPS XT开发入门教程06:新建文件介绍
    spark插入动态分区代码报错
    快速学习MyBatisPlus
    [附源码]计算机毕业设计springboot交通事故档案管理系统
  • 原文地址:https://blog.csdn.net/yanggd1987/article/details/125609736