在正式开始介绍监控之前,我们先来了解下我们微服务架构使用的两个组件。
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(阿波罗)是一款可靠的分布式配置管理中心,诞生于携程框架研发部,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
而Spring Cloud配置中心集成git仓库、svn仓库等配置源统一获取系统配置参数,由Spring Cloud Config Client消费。其配置文件必须遵循严格的语法格式,即使多余的空格都会造成Client 无法读取相应的配置参数,一旦出问题很容易被忽视。
基于Spring Cloud配置中心这种缺陷,我们使用Apollo进行了逐步的替换,使运维、开发对于不同环境之间的配置文件管理更加方便。
借助Apollo可以实现配置修改实时生效(热发布)
,即用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序。
Apollo热发布功能满足了我们在更改配置属性需求时,应用可以实时感知。但在使用过程中,如果Eureka 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 所有的application
http://192.168.3.123:1180/eureka/apps
# 获取某个application下所有的instance
http://192.168.3.123:1180/eureka/apps/application名/
因为需要解析Eureka API返回的数据,所以我们使用python来解析json数据。
# 执行脚本自动发现application名和Instance ip地址
python eureka-instance.py discovery
{
"data":[
{
"{#APP}":"TEST1",
"{#HOSTNAME}":"192.168.3.10"
},
{
"{#APP}":"TEST1",
"{#HOSTNAME}":"192.168.3.11"
}
]
}
通过获取{#APP}
和{#HOSTNAME}
,我们就可以组合成对应命名规则的监控项。
# 监控项组合
TEST1/192.168.3.10
TEST1/192.168.3.11
经过自动发现后的数据,我们可以进一步获取监控项的状态。
# 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
根据不同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]")
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.状态为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