DEVOPS 運維開發系列六:綠燈測試
綠燈測試,是一種比喻,在運維領域中一般是指對應用服務進行一個或一系列的驗證性測試。當通過全部測試時,我們認為該應用服務處於一個正常執行的狀態。否則,就要亮“紅燈”,即意味著存在部分或整體性的應用服務故障。
綠燈測試的實現方式
早期我們大多是開發一些SHELL或Python指令碼,來幫助做一些例如測試程序是否存活、服務埠是否有響應的工作。
隨著技術系統越來越複雜,應用服務的數量倍增,傳統上的綠燈測試工作也在向更加資訊化、自動化和平臺化的方向發展。
綠燈測試的內容與應用服務方式密切相關,下面介紹的是一個對相對常見的WEB應用服務的綠燈測試功能。
WEB應用服務的測試範圍
通常可以包括以下幾個方面:
- 系統類,如程序是否存活;
- 網路類,頁面能否正常開啟;
- 功能類,埠是否有響應,HTTP狀態碼是否正確,頁面內容是否正確;
- 整合類,與其他存在呼叫關係依賴的應用介面間,是否可以正常呼叫;
等等。
其中,系統、功能和整合類的測試內容,大多需要在應用程式所在主機系統中進行測試驗證,而網路類的測試則需要從系統和應用的外部進行訪問測試。
綠燈測試功能的技術實現
在這裡,我們主要是通過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)
檢測web服務頁面的http返回狀態碼
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
檢測web服務的http響應內容
檢查頁面內容中是否包含預定的字串。
這裡使用到了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
前端頁面的實現
HTML部分
<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>
JavaScript部分
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] + "<p>";
}
$("#green_check").removeAttr("disabled");
$("#lightspan").empty();
if (json.result)
{
output = "<font color='#32cd32'>"+output+"</font>";
$("#app_ops_output").html(output);
$("#lightspan").append("<img src=\"../../../static/img/light-green.png\" />");
}
else
{
$("#app_ops_output").html(output);
$("#lightspan").append("<img src=\"../../../static/img/light-red.png\" />");
}
}
});
綠燈測試功能的演示
預設狀態下的頁面效果
正在執行測試
通過測試後的效果
未通過綠燈測試時亮起紅燈
全文完!