zabbix的所有监控模板里默认自带关于系统文件挂载路径以及网络接口检测的自动发现规则

       在主动模式下,agent主动获取与自动发现规则中与键值匹配的系统监控项,例如zabbix自带的Network interface dicscovery规则会主动系统中存在的网口信息,lo eth0 eth1 docker0 ...,并将这些网口信息以变量传递给与之关联的监控原型项,从而实现系统资源的自动监控的功能。
简而言之,自动发现规则主要分为两步:
1.自动发现列出所有网口
2.通过监控原型项监控网口出入流量等

       关于自动发现的最早使用是因为Mounted filesystem discovery该规则,通过规则名不难发现,其并不是去发现和监控磁盘块设备的parttions分区使用、读写、IOPS情况,而是基于文件系统,如果你的业务都已实现容器化那么会出现一个问题,就是该规则不仅仅将会去监控linux文件系统层面磁盘parttions对应的挂载目录,docker 文件系统的挂载目录也通通被纳入了监控范围

      我们简单看一个通过模板自带自动发现规则进行监控的主机上关于文件系统的监控项信息:

       作者初步算了一下,以kubernetes集群的一个work node节点为例,仅docker文件系统各类挂载路径的监控项就多出300多项,如果集群节点数量大,那么无论对zabbix agent而言,还是server端都是一笔不小的性能开销,例如主动模式下,zabbix server会因为agent 采集上报的items数据太多从而导致队列过长,从而形成监控数据积压的情况

解决方法

        zabbix从3.4版本以后开始支持自定义的自动发现规则的编写,通过自定义仅监控磁盘partitions分区使用、IOPS、读写情况的发现规则脚本来实现,规则和监控原型项的编写详细参考:https://www.centos.bz/2017/07/zabbix-auto-discovery-disk-io-perf/

拓展

       之前作者通过k8s集群内部的pod的health check健康检查来对pod状态进行监测,但其只是k8s集群内部pod本地的自检,对于pod是否会因为网络波动、延时等原因而不能在集群内部进行正常通信存在变数,所以这里依葫芦画瓢,去自定义一个自动获取k8s集群各deployment网络映射service的name,clusterIP以及端口的发现规则,并以zabbix模板自带监控项net.tcp.service.perf[service,<ip>,<port>]

为监控原型项实现对集群内工作负载tcp状态的自动发现及监控

实现

初代脚本

cat    ${PWD}/auto_pods_status.sh 
#!/bin/bash
#names=($(kubectl get services |grep -v 'NAME'|awk -F'[ ./]+' '{print $1}'))
#echo $names
nums=$(sudo kubectl get services |grep -v 'NAME'|awk -F'[ ./]+' '{print $1}'|wc -l)
i=1
let nums--
function services {
printf '{\n'
printf '\t"data":[\n'
for name in $(sudo kubectl get services |grep -v 'NAME'|awk -F'[ ./]+' '{print $1}')
do
	if [[ ${name} =~ ^'consul' ]] || [[ ${name} =~ ^'jaejer' ]]; then
		let i--
        let nums--
		continue
	elif [ $i -lt $nums ]; then   
                        clusterip=$(sudo kubectl get service ${name} |awk 'NR==2{print $3}')
                        clusterport=$(sudo kubectl get service ${name} |awk -F'[ /]+' 'NR==2{print $5}')
			printf '\t\t{\n'
			printf "\t\t\t\"{#SERVICENAME}\":\"${name}\",\n"
			printf "\t\t\t\"{#SERVICEIP}\":\"${clusterip}\",\n"
			printf "\t\t\t\"{#SERVICEPORT}\":\"${clusterport}\"\n"
			printf "\t\t},\n"
		  	let i++
	elif [ $i -eq $nums ]; then
			clusterip=$(sudo kubectl get service ${name} |awk 'NR==2{print $3}')
                        clusterport=$(sudo kubectl get service ${name} |awk -F'[ /]+' 'NR==2{print $5}')
                        printf '\t\t{\n'
                        printf "\t\t\t\"{#SERVICENAME}\":\"${name}\",\n"
                        printf "\t\t\t\"{#SERVICEIP}\":\"${clusterip}\",\n"
                        printf "\t\t\t\"{#SERVICEPORT}\":\"${clusterport}\"\n"
                        printf "\t\t}\n"

	fi
done
printf '\t ]\n'
printf '}\n'
}
$1

        如果kubernetes集群内的负载数量较多(作者这边有100多个),services函数前后会执行200次kubectl命令意味着集群default命名空间下的service相关接口短时间会被调用这么多次,所以整个自动发现规则脚本执行的时间是非常长的,可以打印一下脚本执行的时间进行观察,zabbix agent该自动发现规则上报到server端也会报超时相关的错误。

改进

       通过awk里进行for循环以关联数组的形式一次性拿到将我们要取值的每一个应用负载的service的name,clusterIP以及port信息,通过这种方式脚本的执行效率大大提高,测试后发现,执行时间为毫秒级。

改进后脚本

shell版 适合在kubernetes集群中装有kubectl客户端的节点

#!/bin/bash
#servies=`kubectl get services |awk -F '[ /]+' '{service[$1" "$3" "$5]++} END{for(i in service){print i}}'
dm_name=${ZABBIX_HOME}/scripts/deployment_name.list
rm -rf ${dm_name} 
sh ${ZABBIX_HOME}/scripts/services_array.sh array >${dm_name}
#for service  in  `kubectl get services |awk -F '[ /]+' '{service[$1" "$3" "$5]++} END{for(i in service){print i}}'`
nums=$(sudo kubectl get services |grep -v 'devicemanager' |grep -v 'NAME'|wc -l)
function service_name {
i=1
printf '{\n'
printf '\t"data":[\n'
sudo kubectl get services |grep -v 'NAME'|awk -F '[ /]+' '{service[$1" "$3" "$5]++} END{for(i in service){print i}}' |while read line || [[ -n ${line} ]];
do
	service_name=$(echo $line |awk '{print $1}')
	service_ip=$(echo $line |awk '{print $2}')
	service_port=$(echo $line |awk '{print $3}')
	grep -w -q  "${service_name}" ${dm_name}
	if [ $? -eq 0 ];then
	    if [[ ${service_name} =~ ^'consul' ]] || [[ ${service_name} =~ ^'devicemanager' ]]; then
		    let i++
		    continue
	    elif [ $i -lt $nums ]; then   
			    printf '\t\t{\n'
			    printf "\t\t\t\"{#SERVICENAME}\":\"${service_name}\",\n"
			    printf "\t\t\t\"{#SERVICEIP}\":\"${service_ip}\",\n"
			    printf "\t\t\t\"{#SERVICEPORT}\":\"${service_port}\"\n"
			    printf "\t\t},\n"
		  	    let i++
	    elif [ $i -eq $nums ]; then
                printf '\t\t{\n'
			    printf "\t\t\t\"{#SERVICENAME}\":\"${service_name}\",\n"
                printf "\t\t\t\"{#SERVICEIP}\":\"${service_ip}\",\n"
                printf "\t\t\t\"{#SERVICEPORT}\":\"${service_port}\"\n"
                printf "\t\t}\n"

	    fi
	fi

done 
printf '\t ]\n'
printf '}\n'
}
$1

再增加一个service与deployment对比的脚本,防止deployment删除之后,service漏删从而造成的错误告警

cat services_array.sh 
#!/bin/bash
function array() {
    declare -A deployment
    num=1
    for dm_name in `sudo  kubectl get deployment |grep -v 'NAME'|awk -F '[ /]+' '{deployment[$1]++} END{for(i in deployment){print i}}'`
        do
            deployment[${num}]=${dm_name}
            let  num=${num}+1
        done
    for i in $(seq ${#deployment[@]})
        do 
            echo  ${deployment[$i]}
        done
}
$1

python版 通过云平台k8s集群api接口进行调用

import json
import requests
class K8s_nodes_status():
    def __init__(self):
        self._iamendpoints = '**********'
        self._cceendpoints = '**********'
        self._token_uri = '/v3/auth/tokens'
        self._header = {"Content-Type" : 'application/json;charset=utf8'}
        self._cluster_id = '************'
        self._project_id = '************'
        self._token = self.earn_token()
    @property
    def earn_token(self):
        url = self._iamendpoints + self._token_uri
        body = {
	"auth": {
		"identity": {
			"methods": ["password"],
			"password": {
				"user": {
					"name": "*********",
					"password": "*********",
					"domain": {
						"name": "********"
					}
				}
			}
		},
		"scope": {
			"project": {
				"name": "********"
			}
		}
	}
        }
        response = requests.post(url=url,headers=self._header,json=body)
        token = response.headers['X-Subject-Token']
        return token
    def acquire_services(self):
        headers = {
                  "Content-Type": 'application/json',
                  "X-Cluster-ID": '{0}'.format(self._cluster_id),
                  "X-Auth-Token": '{0}'.format(self.earn_token)
                  }
        uri = '/api/v1/namespaces/default/services'
        url = 'https://' + self._cluster_id + '.' + '{0}'.format(self._cceendpoints) + uri
        yield  requests.get(url=url,headers=headers)

    def account(self):
        service_info = {}
        response = next(self.acquire_services())
        response_json = json.loads(response.text)
        nums = int(len(response_json['items']))
        for num in range(0,nums):
            service_name = response_json['items'][num]['metadata']['name']
            service_port = response_json['items'][num]['spec']['ports'][0]['port']
            cluster_ip =  response_json['items'][num]['spec']['clusterIP']
            service_info.update({service_name:[service_port,cluster_ip]})
        self._service_info = service_info
        result = (nums,service_info)
        return result

def output_date():
    num =1
    service_info = K8s_nodes_status().account()[1]
    nums = K8s_nodes_status().account()[0]
    print('{')
    print('"' +"data" + '"' + ':' +'[')
    for key in service_info.keys():
        print('{"{#' + 'SERVICENAME}"' + ':'+ '"' + key + '"' + ',' + '\n'  + '"{#SERVICEIP}"' + ':' + '"' + service_info[key][1] + '"' + ',' + '\n' + '"{#SERVICEPORT}"' + ':' + '"' + str(service_info[key][0]) + '"' + '\n' +  '},' )
        num = num + 1
        if num == nums+1:
            print('{"{#' + 'SERVICENAME}"' + ':'+ '"' + key + '"' + ',' + '\n'  + '"{#SERVICEIP}"' + ':' + '"' + service_info[key][1] + '"' + ',' + '\n' + '"{#SERVICEPORT}"' + ':' + '"' + str(service_info[key][0]) + '"' + '\n' +  '}' )

    print(']' + '\n' + '}')


if __name__ == "__main__":
    output_date()

 以shell 脚本为例在zabbix agent本地执行该脚本,查看效果,监控资源的输出必须为json格式

[root@test-k8s-yw-01 scripts]# sh ${PWD}/auto_pods_status.sh services
{
        "data":[
                {
                        "{#SERVICENAME}":"ac-test",
                        "{#SERVICEIP}":"10.247.130.56",
                        "{#SERVICEPORT}":"9195"
                },
                {
                        "{#SERVICENAME}":"ad-test",
                        "{#SERVICEIP}":"10.247.86.180",
                        "{#SERVICEPORT}":"10250"
                },
                {
                        "{#SERVICENAME}":"ab-test",
                        "{#SERVICEIP}":"10.247.90.52",
                        "{#SERVICEPORT}":"8083"
                },
                ...
                ...
                ...
                {
                        "{#SERVICENAME}":"af-test",
                        "{#SERVICEIP}":"10.247.99.183",
                        "{#SERVICEPORT}":"80"
                }
         ]
}  

zabbix agent加入如下配置,并修改zabbix目录权限,给zabbix用户提权能够sudo kubectl 配置如下:

[root@test-k8s-yw-01 conf.d]#cat ${PWD}/auto_pods_status.conf 
UserParameter=k8sapp.status,bash ${ZABBIX_HOME}/scripts/auto_pods_status.sh services

[root@test-k8s-yw-01 conf.d]#chown -R zabbix:zabbix ${ZABBIX_HOME}

[root@test-k8s-yw-01 conf.d]# echo "zabbix ALL=(root) NOPASSWD:/bin/kubectl">>/etc/sudoers
[root@test-k8s-yw-01 conf.d]# sed -i 's/^Defaults.*.requiretty/#Defaults requiretty/' /etc/sudoers



最后重启zabbix agent

 

zabbix server端配置

首先创建一个模板用于配置自动发现规则,配置完成之后关联到我们刚刚有编写自动发现规则脚本并属于k8s节点的agent上就可以了

1.规则配置

2.监控原型项配置

3.触发器原型项配置

4.图形原型配置

 

 

一些问题

关于监控资源的过滤问题的解决

例如不想监控或者过滤掉集群内的某些副本控制器的services,既可以如上面脚本中那样通过条件判断做过滤,也可以通过界面自动发现的过滤器的正则匹配来实现,具体参考官网相关内容 自动发现过滤器

 

创建过滤条件的正则表达式,然后在过滤器以@的方式进行引用

这里创建一个过滤services名称为consul和jaeger的正则匹配表达式

测试

引用

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐