绿灯测试,是一种比喻,在运维领域中一般是指对应用服务进行一个或一系列的验证性测试。当通过全部测试时,我们认为该应用服务处于一个正常运行的状态。否则,就要亮“红灯”,即意味着存在部分或整体性的应用服务故障。
早期我们大多是开发一些SHELL或Python脚本,来帮助做一些例如测试进程是否存活、服务端口是否有响应的工作。
随着技术系统越来越复杂,应用服务的数量倍增,传统上的绿灯测试工作也在向更加信息化、自动化和平台化的方向发展。
绿灯测试的内容与应用服务方式密切相关,下面介绍的是一个对相对常见的WEB应用服务的绿灯测试功能。
通常可以包括以下几个方面:
在这里,我们主要是通过WEB框架Django+自动化运维工具SaltStack来实现的。其中Django提供一个应用系统信息化、平台化管理的服务,而SaltStack则作为底层工具,通过接口提供远程执行命令、配置同步、在本地执行命令等功能的支持。在WEB页面中,则通过应用jquery的异常操作来为用户提供更好的使用体验。
提供应用程序信息和维护方法的定义和管理。
如图所示,我们需要预先把一个应用运维、监控相关的信息尽可能完整和准确得输入到系统中。
为应用程序提供基本的程序启、停、配置同步、查看或下载日志的服务。当然,正像本文所描述的,我们还实现了一个绿灯测试功能。
目前暂实现了进程存活检测、服务端口响应检测、WEB页面http状态码检测和http响应返回内容检测共4种验证测试办法,如下所示。
def app_green_check(request):
"""
执行应用服务的绿灯测试
"""
app_id = request.POST.get('app_id', '')
app = get_object(App, id=app_id)
hostname_asset = app.asset.hostname
process_name = app.process_name
monitor_ports = app.monitor_ports
app_ip = app.ip
web_url = app.web_url
response_str = app.response_str
print "%s,%s,%s,%s" % (process_name, monitor_ports, web_url, response_str)
return_value = {}
check_data = {}
# 针对web类应用提供了4种测试方法,支持随意组合使用
step1_result = True
step2_result = True
step3_result = True
step4_result = True
# step1
if process_name:
if app_is_running(hostname_asset, process_name):
step1_value = u'应用进程检测:进程已经在运行!'
else:
step1_value = u'应用进程检测:没有找到该应用的进程!'
step1_result = False
check_data['process_name'] = step1_value
# step2
if monitor_ports:
has_nc, msg = check_nc_command(hostname_asset)
if has_nc:
step2_result, error_ports = app_check_ports(hostname_asset, app_ip, monitor_ports)
if step2_result:
step2_value = u'服务端口[%s]的检测:端口响应正常!' % monitor_ports
else:
step2_value = u'服务端口[%s]的检测:%s 端口没有响应!' % (monitor_ports, error_ports)
else:
step2_value = u'服务端口的检测:' + msg
check_data['monitor_ports'] = step2_value
# step3
if web_url:
http_code = get_http_code(web_url)
if http_code == 200:
step3_value = u'web服务HTTP返回状态码检测: %d!' % http_code
else:
step3_value = u'web服务HTTP返回状态码检测: %d! web服务异常!' % http_code
step3_result = False
check_data['web_url'] = step3_value
# step4
if response_str:
has_str = check_response_str(web_url, response_str)
if has_str:
step4_value = u'web服务的HTTP响应内容检测: 在响应内容中找到了指定的字符串【%s】' % response_str
else:
step4_value = u'web服务的HTTP响应内容检测: 在响应内容中没有找到指定的字符串【%s】' % response_str
step4_result = False
check_data['response_str'] = step4_value
return_value['data'] = check_data
if step1_result and step2_result and step3_result and step4_result:
return_value['result'] = True
else:
return_value['result'] = False
return_response = json.dumps(return_value)
# print return_response
return HttpResponse(return_response)
这里是通过调用salt.client模块接口来实现的,功能为在指定的远程业务主机上执行相关的命令并返回结果。
def app_is_running(hostname_asset, process_name):
"""
查验应用是否已经在运行
"""
local = salt.client.LocalClient()
ps_command = "ps -ef|grep %s|grep -v grep|wc -l" % process_name
salt_output = local.cmd(hostname_asset, 'cmd.run', [ps_command])
if int(salt_output.get(hostname_asset, '0')) == 1:
return True
else:
return False
先对系统命令nc进行检查,因为我们是使用这个命令实现的端口响应测试。
def check_nc_command(hostname_asset):
"""
查验应用主机的系统中是否安装了nc命令工具
"""
local = salt.client.LocalClient()
check_nc = r"command -v nc >/dev/null 2>&1 || { echo >&2 'I require nc but it is not installed. Aborting.'; exit 1; }"
salt_output = local.cmd(hostname_asset, 'cmd.run', [check_nc])
if salt_output[hostname_asset]:
return False, salt_output[hostname_asset]
else:
return True, ""
def app_check_ports(hostname_asset, app_ip, monitor_ports):
"""
查验应用的监听端口是否有响应
"""
local = salt.client.LocalClient()
ports_list = monitor_ports.split(';')
error_ports = []
check_result = True
for aport in ports_list:
check_command = "nc -w 1 %s %s < /dev/null && echo Connecting to tcp port succeeded." % (app_ip, aport)
salt_output = local.cmd(hostname_asset, 'cmd.run', [check_command])
print salt_output
if "succeeded" in salt_output[hostname_asset] or "Connected" in salt_output[hostname_asset]:
pass
else:
error_ports.append(aport)
check_result = False
if check_result:
return True, '0'
else:
return False, ",".join(error_ports)
def get_http_code(web_url):
"""
查验web服务的HTTP返回状态码
"""
curl_command = 'curl -I -m 10 -o /dev/null -s -w \%{http_code} ' + web_url
output = commands.getoutput(curl_command)
if output:
return int(output)
else:
return 0
检查页面内容中是否包含预定的字符串。
这里使用到了SaltStack的RunnerClient模块提供的在Salt Master本机执行管理命令的功能。在这里执行的是一个Salt http.query函数,功能为访问指定的URL并返回响应数据。
def check_response_str(web_url, response_str):
"""
查验应用的web响应内容中是否包含指定的字符串
"""
opts = salt.config.master_config('/etc/salt/master')
runner = salt.runner.RunnerClient(opts)
http_code = get_http_code(web_url)
if http_code == 200:
salt_output = runner.cmd('http.query', [web_url], print_event=False)
check_result = True if response_str in salt_output['body'] else False
else:
check_result = False
return check_result
<tr>
<td class="text-navy">绿灯检测td>
<td>
<span id="greencheck"><input id="green_check" type="button" class="conn btn btn-xs btn-info" value="服务检测" onclick="start_check()"/>span>
<span id="lightspan"><img src="../../../static/img/light-close.png" />span>
td>
tr>
......
<div class="text-left">
<div id="app_ops_output" class="text-warning">div>
div>
function start_check()
{
$("#green_check").attr({"disabled":"disabled"});
$("#app_ops_output").html("正在检测,请稍候...");
$.ajax({
type: "post",
data: {app_id: {{ app.id | safe }}},
url: "{% url 'app_green_check' %}",
success: function(result){
var json = eval("("+result+")");
var output = "";
for(var key in json.data){
output += json.data[key] + ""
;
}
$("#green_check").removeAttr("disabled");
$("#lightspan").empty();
if (json.result)
{
output = ""+output+"";
$("#app_ops_output").html(output);
$("#lightspan").append("");
}
else
{
$("#app_ops_output").html(output);
$("#lightspan").append("");
}
}
});
全文完!