CI/CD支撑运维自动化:系统监控级原子模块

简述

《# CI/CD如何支撑运维自动化》 通过CI/CD的方式来实现运维自动化的想法,并规划了以下几个原子模块:

  • 操作系统级
  • Java应用级
  • Apollo配置中心级
  • 监控系统级
  • CMDB级

通过pipeline对以上原子模块进行编排来满足不同场景的需求。为让大家更深入的理解,下面来介绍下系统监控级原子模块的应用。

本文涉及功能在运维自动化所处的位置如图:

image.png

监控场景

对于系统应用的监控,我们一般多采用URL监控或端口监控,这是两种完全不同的方式。端口监控无法在应用假死的情况下进行告警,因此建议以URL监控为主、端口监控为辅的方式来满足监控需求。

另为了URL监控和端口监控的统一管理,因此我们使用集中监控主机10.10.10.250,在Zabbix监控场景下我们统一通过10.10.10.250自动发现URL及端口。

1.URL监控

# 1.站点文件
vim  http_site.conf
#格式:APP_NAME|MONITOR_URL
#标准url
TEST1|10.10.10.1:8080
#非标准url
TEST2|10.10.10.1:8080/XXXXX

# 2.站点监控脚本
# vim url_check.py
#!/usr/bin/env python
#comment:
#1.兼容http_site文件中标准及非标url
#2. 正常状态码返回200;超时返回10000
import pycurl
import json
import sys
import os
import StringIO

mulu = '/App/scripts/zabbix'
website = os.path.join(mulu, 'http_site.txt')


def website_discovery():
    sitelist = []
    with open(website, 'r') as url:
        for line in url:
        line=line.strip('\n')
        line=line.split('|')
        if len(line) == 2:
            service = line[0]
            link = line[1]
        else:
            link = line + "format error"        
        site = {}
      
        site['{#LINK}'] = link
        site['{#SERVICE}'] = service
        sitelist.append(site)
    #json序列化
    discovery_site_info = {"data":sitelist}
    print json.dumps(discovery_site_info, sort_keys=True, indent=4, separators=(',', ':'))


def website_response_code():
    if len(sys.argv) == 3:
        try:
            link=sys.argv[2]
            num=link.split('/')
            if len(num) > 1:
                url="http://" + link
            else:
                url="http://" + link + "/monitor"
            c=pycurl.Curl()
            b=StringIO.StringIO()  
            c.setopt(pycurl.WRITEFUNCTION, b.write)
            c.setopt(pycurl.FOLLOWLOCATION, 0)
            c.setopt(pycurl.URL, url)
            #c.setopt(pycurl.PROXY, proxy)
            #c.setopt(pycurl.CUSTOMREQUEST, "GET")
            #c.setopt(pycurl.POSTFIELDS, query)
            c.setopt(pycurl.CONNECTTIMEOUT, 50)
            c.setopt(pycurl.TIMEOUT, 50)
            c.perform()
        except Exception:
            print "1000"
        else:
            status=c.getinfo(c.HTTP_CODE)
            b.close()
            c.close()
            print status
    else:
        print "Usage: python http_response_code_status.py website_response_code website or proxy"
    

if __name__ == '__main__':

  if sys.argv[1] == 'website_discovery':
    website_discovery()
  elif sys.argv[1] == 'website_response_code':
    website_response_code()
  else:
    print "Usage: python http_response_code_status.py [website_discovery]|[website_response_code website]"
复制代码

通过脚本我们做出的规划是

  • 标准化监控URL为10.10.10.1:8080/monitor

  • 非标准化监控URL为10.10.10.2:8080/XXXX

我们希望监控脚本可以兼容未标准化的老应用,也能够接入标准化的新应用。当状态码返回非200时,能够通过APP_NAME 及 IP:PORT 直接区分关联应用:

告警内容为:
# 标准应用50s超时
TEST1|10.10.10.1:8080 状态码为1000
# 非标准应用返回为502
TEST2|10.10.10.2:8080/XXXXX 状态码为502
复制代码

2.端口监控

# 1.端口文件
vim  port.conf
#格式:APP_NAME|IP:PORT
TEST1|10.10.10.1:8080
TEST2|10.10.10.2:8080

# 2.端口监控脚本
# vim port_check.sh
#!/bin/bash
discovery() {  
    printf "{\n"
    printf "\t\"data\": [\n"
    info=(`cat /App/script/zabbix/port.conf`)
    for ((num=0;i<${#info[@]};++i))
    do
        service=`echo ${info[$num]} | awk -F'|' '{print $1}'`
        link=`echo ${iinfo[$num]} | awk -F'|' '{print $2}'`
        if [[ $num -eq $((${#ip_port[@]}-1)) ]]
        then
            printf "\t\t{ \"{#LINK}\":\"${link}\", \"{#SERVICE}\":\"${service}\" }\n"
        else
            printf "\t\t{ \"{#LINK}\":\"${link}\", \"{#SERVICE}\":\"${service}\" },\n"
            ((num++))
        fi
    done
    
    printf "\t]\n"
    printf "}\n"
}

test_port() {
    ip=`echo $1 | awk -F':' '{print $1}'`
    port=`echo $1 | awk -F':' '{print $2}'`
    nc -z -w 2 $1 $2 2> /dev/null && echo 0 || echo 1
}

if [[ $1 == 'discovery' ]]
then
    discovery
elif [[ $1 == 'test_port' ]]
then
    test_port $2
else
    echo "Usage: /App/script/zabbix/port_check.sh discovery | test_port"
fi

复制代码

我们希望监控脚本可以兼容未标准化的老应用,也能够接入标准化的新应用。当状态码返回非0时,能够通过APP_NAME 及 IP:PORT 直接区分关联应用:

告警内容为:
# 响应非0则告警
TEST1|10.10.10.1:8080 状态码为1
复制代码

场景需求

当我们每上线一个应用都需要手动添加URL监控或端口监控,这对团队成员的素质要求很高,一旦有遗漏,很可能导致应用故障无法及时发现。因此就有了自动添加监控项的需求。

还是以Zabbix监控平台为例,自动添加监控项涉及到的方法如下:

  • 获取集中管理机上监控项,从而判断是否需要新增;
  • 在集中管理机上新增监控项;

具体流程图如下:

image.png

场景实现

image.png

我们在Jenkins扩展共享库中定义了一个系统监控级的原子模型,目的是定义和系统监控相关的方法,用于和其他自动场景进行联动。虽然我们以Zabbix监控为例子,但不限于此,如:

  • Zabbix
  • 夜莺
  • Prometheus
  • Grafana

在此我们初步定义的功能有:

  • 监控项新增
  • 监控项屏蔽、恢复

其他可根据实际需求自行补充。

1.扩展共享库模目录结构

shared-library
|--resources
|--src
|--vars
   |--zabbix.groovy
复制代码

2.模块功能实现

vim zabbix.groovy
/*
用途:
1.zabbix 添加监控
*/
import groovy.json.JsonSlurper

// 获取管理机监控项
def Zabbix_ItemKey_Status(APP_Item_Key) {
     def data = """
     {
        "jsonrpc":"2.0",
        "method":"item.get",
        "params":{
            "output": "extend",
            "hostids": "10355",
            "search": {
                 "key_": "${APP_Item_Key}"
            }
        },
        "auth":"c919301e492b03f66673f2274e9ba9db",
        "id":1
    }
    """ 
    
    def response = httpRequest contentType: 'APPLICATION_JSON', httpMode: 'POST', requestBody: data, url: "http://10.164.194.54:8888/api_jsonrpc.php"
    def jsonSlurper = new JsonSlurper()
    def content = jsonSlurper.parseText(response.content)
    println('Response: '+response.content)
    return content.result.isEmpty()
}

// 新增URL监控、端口监控至相关配置文件
def Zabbix_Config(AppName,APP_Alter_TYPE,APP_Item_Key) {
    if (Zabbix_ItemKey_Status(APP_Item_Key)) {
        if (APPLICATION_JSON == "IP_PORT"){
            sh """
                sudo su - root -c 'ansible 10.10.10.250 -b -m lineinfile -a \"
            """
            Println("True: IP_PORT")
        }else {
            sh """
                sudo su - root -c 'ansible 10.10.10.250 -b -m lineinfile -a \"
            """
            Println("True: HTTP_URL")
        }
    }else {
        Println("监控项已存在无需操作")
    }
}
复制代码

由于是在集中管理机上新增监控项,在此我们省略了获取集中管理机的hostid操作。而是直接通过固定的hostid来查找对应的监控项是否存在,最后执行新增操作。

3.Jenkins pipeline script

@Library('shared-library')
pipeline {
    agent any
    options {
        timestamps()
    }
    stages{
        stage('添加端口监控') {
            when {
                expression { IP_PORT != '' }
            }
            steps {
                script {
                    zabbix.Zabbix_Config("${APP_NAME}","IP_PORT","${IP_PORT}")
                }
            }
        }
        stage('添加url监控') {
            when {
                expression { HTTP_URL != '' }
            }
            steps {
                script {
                    zabbix.Zabbix_Config("${APP_NAME}","HTTP_URL","${HTTP_URL}")
                }
            }
        }        
    }
}

复制代码

从pipeline 脚本看,参数化构建过程中会根据参数是否为空来判断是否执行相应的动作。如果为空,则跳过;如果不为空,则调用扩展共享库进一步操作。

4.Jenkins参数化构建

填写运行参数:

image.png 构建结果:

image.png

总结

我们通过对扩展共享库中的系统监控级原子模型进行编排,来实现监控项的自动增加,为以后的系统应用自动上线场景做好了铺垫。

おすすめ

転載: juejin.im/post/7046546054392578079