【自动化运维】ZabbixAPI,一键拉取所有机器的监控参数(内存负载、磁盘负载、IP地址等等)

需求

旧平台中有一个业务模块叫做服务器巡检作业,需要迁移,但我想将原本服务器巡检的作业用代码来代替,服务器巡检的内容包括内存负载、CPU负载、IP地址、磁盘负载,PC巡检机器合起来大概300-500台左右,要求能一键拉取这些机器的监控项,时间尽量短一些。

 

选型

旧平台中,服务器巡检作业是每天登到机器上查询相关参数,再填上去的,最多多做了一个复制新增的功能。

新平台中,延续了之前旧平台当中的新建一条记录和复制新增的功能。虽然一条巡检记录有多个选项和参数,但是有的数据变化大,并且更重要,有的数据,例如应用系统备份、系统备份是几乎每天都一样的,所以假如自动生成,我着重生成3个参数,分别是CPU负载(百分比)、IP地址、内存负载(百分比),并且由于当初旧平台上的数据和巡检系统里头的数据不统一,导致同一个IP地址对应的系统名不一样,因此想尽量和新系统的数据进行结合。

查询条件里头,希望能包括 时间区段、巡检人、IP地址查询三种。

公司有两种监控平台,都是目前还算主流的监控平台,一个是ZStack,可以看我的这篇文章https://blog.csdn.net/qq_34093082/article/details/106096743,另一个是Zabbix。两种平台都监控了当前的几乎所有的机器,我看了一下zstack的文档,好的那没事了,再看了一下zabbix的api接口,然后找到了这个github上的神奇API。

 

来琢磨一下这个项目的文档,哇,使用过程就五个步骤

 

request部分,哇,筛选条件放request再call就行耶

filter.put("host", new String[] { host });
Request getRequest = RequestBuilder.newBuilder()
	.method("host.get").paramEntry("filter", filter)
	.build();
JSONObject getResponse = zabbixApi.call(getRequest);

那就决定是这个了

 

 

实现

1、引入dependency

<dependency>
    <groupId>io.github.hengyunabc</groupId>
    <artifactId>zabbix-api</artifactId>
    <version>0.0.2</version>
</dependency>

 

2、先查得到数据

这部分代码肯定要写在工具类中,我们先写一个ZabbixUtil的工具类,定义全局静态变量分别放url,userName,password,连接到zabbix的内置API操作对应的php。

编写静态代码块,把连接的代码写到静态代码块中。

ZabbixUtil


    private static DefaultZabbixApi zabbixApi;
    private static String zabbixUserName = "Admin";
    private static String zabbixPassword = "*******";
    private static String zabbixURL = "http://172.18.196.38/zabbix/api_jsonrpc.php";

    static {
        zabbixApi = new DefaultZabbixApi(zabbixURL);
        zabbixApi.init();
        try {
            zabbixApi.login(zabbixUserName, zabbixPassword);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

登录失败会有提示消息,记得用户名和密码就写你zabbix的用户名和密码就行。

然后我们来查一段数据,先查所有的数据。

Request request = RequestBuilder.newBuilder().method("item.get")
                    .build();
JSONObject resJson = zabbixApi.call(request);

得到的是一个JsonObject,你需要经历JsonObject--》JsonArray--》List<Map>,才可以将它转化为能够处理的数据,实际上就是key-value,item是范例给的方法名,得到的结果看得出来是key(监控选项)对应的值,因此item这个方法名对应的就是zabbix里头的监控项,挺好,我们一开始就找到了痛点。

但是仅仅知道这个是不够的,由于zabbix的数据库是mysql,我想弄清其中的过程,就直接去找了后台的数据库。

hosts:主机表,存放了主机的信息,主键为hostid

items:监控选项表,存放了监控项对应的信息,关联hostid好知道是哪台主机的监控项,主键为itemid

history:监控值表,关联itemid,一个监控项对应一个值,带有时间戳作为不同时期的监控值

host_groups:分组表,关联hostid,主键为groupid,详情见下图

所以他应该就是主机-监控项-监控记录,如果要查询某个组底下的所有机器,查询对应的分组id就行。正好我们这次要查的是PC巡检组底下的,并且zabbix上已经做好了分组。

 

3、查询某个组底下所有的Host

上面提到了method为item.get,综合表名,我们试试hostgroup.get,看能不能得到某个分组底下所有的机器(提前通过ip地址知道了我们要查的组id为48)

try {
            JSONObject json2 = new JSONObject();
            json2.put("groupid",  new String[]{groupid});
            Request request = RequestBuilder.newBuilder().method("hostgroup.get")
                    .paramEntry("selectHosts",new String[]{"hostid","host"})
                    .paramEntry("filter",json2)
                    .build();
            JSONObject resJson = zabbixApi.call(request);
            String error = String.valueOf(resJson.get("error"));
            if (!StringUtils.isEmpty(error) && error != "null") {
                System.out.println("调用zabbix接口出错");
            } else {
                JSONArray jsonArray = resJson.getJSONArray("result");
                resultHost = (List) jsonArray;
                System.out.println("查询成功");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

这个resultHost就是我们查询到的对应主机列表,由于过滤条件可以放多个分组,但我这只放了一个,因此,取到这个值之后,我们需要先get(0),然后再getHost,得到hostid的列表。如果你想要同时获得对应hostid的ip地址是哪个,你可以在request里同时输出host,也就是它的ip地址,然后分两个list存放就OK了

            Map<String,Object> groupMap = resultHost.get(0);
            List list1 = (List) groupMap.get("hosts");
            for (Object hostObjects : list1){
                Map<String,Object> hostMaps = JSONObject.parseObject(JSON.toJSONString(hostObjects));
                idResultList.add(hostMaps.get("hostid"));
                hostResultList.add(hostMaps.get("host"));
            }

 

4、根据hostid获得监控项

值得一提的是,虽然我们前面说history是存放监控值的表,但实际上,我们在用items.get的方法的时候,就已经获得的是最新的检测值了,它是key-value的形式。于是我们用  Request request = RequestBuilder.newBuilder().method("item.get"),获取监控项。

 /***
     * 根据某组当中所有的主机的id获取对应的主机信息
     * Host name of Zabbix agent running  ip地址
     * Available memory /  Total memory   剩余内存百分比
     * Processor load (1 min average per core)  cpu负载
     */
    public List<Map<String, Object>> getAllItems(String[] hostStringArgs) throws ParseException {
        List<Map<String, Object>> resultItem = null;
        try {
            JSONObject json1 = new JSONObject();
//            json1.put("name",  new String[]{"Host name of Zabbix agent running","Processor load (1 min average per core)",
//                    "Available memory","Total memory","Free disk space on / (percentage)","Free disk space on C: (percentage)"});
            Request request1 = RequestBuilder.newBuilder().method("item.get")
                    .paramEntry("output", new String[]{"hostid","name","lastvalue"})
                    .paramEntry("hostids", hostStringArgs)
                    .paramEntry("filter",json1).build();

            JSONObject resJson1 = zabbixApi.call(request1);
            String error = String.valueOf(resJson1.get("error"));
            if (!StringUtils.isEmpty(error) && error != "null") {
                System.out.println("调用zabbix接口出错");
            } else {
                JSONArray jsonArray = resJson1.getJSONArray("result");
                resultItem = (List) jsonArray;
                System.out.println("查询成功");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return resultItem;
    }

 

来分析一下这段json。

itemid自增的一个不管,lastValue是值,name是监控项,hostid是主机的id。的确是一个萝卜一个坑,一个监控指标一个值。

我们怎么去获取cpu负载、内存负载和ip地址这三个数据呢?当然是通过name来获取了。

这边和zabbix的管理员对比过了,name是什么样子,取决于你zabbix的模板里头叫啥名字,zabbix本身给你配了两个通用的模板,一个是Linux的,一个是Windows的,它们对于同一个监控项的name是不同的,例如说cpu负载,Linux的模板叫CPU load per core(1 min),Windows 叫做 CPU load (1 min) ,所以你一定要先在zabbix 客户端中,把模板内这个监控项的名字改成相同的。这样子你才能一段代码,同时获得两种机器的参数。

然后我写了以下这个复杂度超高的代码。

	//遍历查询到的主机参数表将他们放在pc_check的表格当中
	public String createZabbixData(String groupID) throws Exception {
		ZabbixUtil zabbixUtil = new ZabbixUtil();
		List<Map<String, Object>> allItems =  new ArrayList<>();
		ControllerUtil controller = new ControllerUtil();
		String nowDateTime = DateTools.getNowDateTime();
		List<String> ipAddressList = new ArrayList<>();
		try {
			String[] allGroupHostID = (String[]) (zabbixUtil.getAllGroupHostByGruopId(groupID).get(0));
			String[] allGroupHostIpAddress = (String[]) (zabbixUtil.getAllGroupHostByGruopId(groupID).get(1));
			if(allGroupHostID != null){
				//1、搜索hostid,查找匹配hostid的数据
				//2、写if else语句将对应的字段相应的数据库字段当中
				allItems = zabbixUtil.getAllItems(allGroupHostID);
				String cpuLoad = null;  
				String memoryLoad = null;
				String host = null;
				int insertRes = 0 ;
				String diskResource = null;
				for (int hostIdIndex = 0; hostIdIndex < allGroupHostID.length; hostIdIndex ++) {
					cpuLoad = null;
					memoryLoad = null;
					totalMemory = null;
					availableMemory = null;
					host = null;
					diskResource = "" ;
					String hostid = allGroupHostID[hostIdIndex];
					String ipAddress = allGroupHostIpAddress[hostIdIndex];
					for (int itemIndex = 0; itemIndex < allItems.size(); itemIndex++) {
						if(!String.valueOf(allItems.get(itemIndex).get("hostid")).equals(hostid)){
							continue;
						}else if(String.valueOf(allItems.get(itemIndex).get("name")).equals("Host name of Zabbix agent running")){
							host = ipAddress;
						}else if(String.valueOf(allItems.get(itemIndex).get("name")).equals("Processor load (1 min average per core)")){
							cpuLoad = String.valueOf(allItems.get(itemIndex).get("lastvalue"));
						}else if(String.valueOf(allItems.get(itemIndex).get("name")).equals("内存使用率")){
							memoryLoad = String.valueOf(allItems.get(itemIndex).get("lastvalue"))+"%";
						}else if (String.valueOf(allItems.get(itemIndex).get("name")).contains("Free disk space on  (percentage)")){
							diskResource += String.valueOf(allItems.get(itemIndex).get("name")).replace("Free disk space on  (percentage) ", "")+"的剩余容量是"+ String.valueOf(allItems.get(itemIndex).get("lastvalue")+"%"+"\n");
						}
					}
					//加入查询到的zabbix数据
					insertRes = yfPcCheckDao.insertZabbixData(cpuLoad, memoryLoad,host, nowDateTime,diskResource);
				}
			}else {
				return "error";
			}
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return "success";
	}

 

咱就是找到这个分组底下的所有host,然后它们的ip地址连着监控参数和当前日期,一起插到了数据库里头。

里头怎么会有个内存使用率的监控指标呢,还是中文,因为内存负载这个指标,原生的模板是不提供的,你只能查到Total memory 和 available memory,想要负载就自己除咯,我之前做了除了,但是管理员那边说他可以在那把除法 的工作做好给我,你到时候获取内存使用率就行。因此我这边的这几个name,都是在 zabbix客户端修改过模板值的,你们那的name,一定不是叫这个名字。由此,我们就顺利的把zabbix的监控项插入进去了。

 

5、磁盘负载怎么查询

来看看这两张图。

 

 

注意到了吗,磁盘负载那一栏,是我们每次巡检的重点对象,但是每台机器的盘的数量不同,盘名字也各有所别,之前我们的参数都是固定名字的,现在名字不同了,怎么办?

这就涉及到了string 模糊匹配了,前面说到模板的name是可以修改的,我们先保证Windows 和 Linux对应监控项的name是一样的,这边改成   Free disk space on (percentage)  ,然后用模糊匹配,后面是什么盘 我们不管,就把一个hostid底下的同个前缀的参数对应的值拼接成一个字符串就完事了。

if (String.valueOf(allItems.get(itemIndex).get("name")).contains("Free disk space on  (percentage)")){
   diskResource += String.valueOf(allItems.get(itemIndex).get("name")).replace("Free disk space on  (percentage) ", "")+"的剩余容量是"+ String.valueOf(allItems.get(itemIndex).get("lastvalue")+"%"+"\n");
						}
 

6、速度问题

做到这里,zabbix查询耗费1秒多,就已经能够拉取所有机器的数据插入到数据库中了。

紫色代表拉取下来并插入到数据库的数据。现在我想将系统中原本的该IP地址对应的系统名和巡检人一起放进去怎么办,我这边选择都插入完之后做update操作,为啥不在insert的时候就查到一起插入呢,因为这样子 查询的操作会乘以循环的倍数,这个循环是所以所有item循环一次。我把ip地址查到,再做update,那就少了很多次循环,这是第一次速度考虑。

感受到查询速度慢,我给数据表的主机资料表和巡检表的对应字段做了单值索引,速度直接提升2倍多,这是第二次速度考虑。

之前工具类静态代码块中写连接zabbix,减少初始化的时间,这是第三次速度考虑。

 

7、每天只能拉取一次数据

这里是纯前端的一个知识点,这边就直接放代码了。

<button class="add" id="pullZabbixBtn" onclick="createZabbixData()">拉取巡检数据</button>

<input type="hidden" id="findZabbixDataIfExist" value="${findZabbixDataIfExist}"/>


//监听是否当日已经拉取过zabbix上的数据
	$(function () {
		var findZabbixDataIfExist = $("#findZabbixDataIfExist").val()
		if(findZabbixDataIfExist == 1){
			$("#pullZabbixBtn").attr("disabled",true);
			$("#pullZabbixBtn").addClass("end");
			$("#pullZabbixBtn").text('今日数据已拉取');
		}
	})

 

以上,就是如何使用ZabbixAPI实现自动化运维中拉取数据这个功能的我的做法。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值