CI/CD支撑运维自动化:系统监控级原子模块
圣诞节快乐
读完需 8 分钟
速读需 4 分钟
简述
《CI/CD如何支撑运维自动化》讲述了借助Jenkins 通过CI/CD的方式来实现运维自动化的想法,并规划了以下几个原子模块:
操作系统级
Java应用级
Apollo配置中心级
监控系统级
CMDB级
通过pipeline对以上原子模块进行编排来满足不同场景的需求。为让大家更深入的理解,下面来介绍下系统监控级原子模块的应用。
本文涉及功能在运维自动化所处的位置如图:
监控场景
对于系统应用的监控,我们一般多采用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监控平台为例,自动添加监控项涉及到的方法如下:
获取集中管理机上监控项,从而判断是否需要新增;
在集中管理机上新增监控项;
具体流程图如下:
场景实现
我们在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参数化构建
填写运行参数:
构建结果:
总结
我们通过对扩展共享库中的系统监控级原子模型进行编排,来实现监控项的自动增加,为以后的系统应用自动上线场景做好了铺垫。