1. 程式人生 > >DEVOPS 運維開發系列六:綠燈測試

DEVOPS 運維開發系列六:綠燈測試

綠燈測試,是一種比喻,在運維領域中一般是指對應用服務進行一個或一系列的驗證性測試。當通過全部測試時,我們認為該應用服務處於一個正常執行的狀態。否則,就要亮“紅燈”,即意味著存在部分或整體性的應用服務故障。

綠燈測試的實現方式

早期我們大多是開發一些SHELL或Python指令碼,來幫助做一些例如測試程序是否存活、服務埠是否有響應的工作。
隨著技術系統越來越複雜,應用服務的數量倍增,傳統上的綠燈測試工作也在向更加資訊化、自動化和平臺化的方向發展。
綠燈測試的內容與應用服務方式密切相關,下面介紹的是一個對相對常見的WEB應用服務的綠燈測試功能。

WEB應用服務的測試範圍

通常可以包括以下幾個方面:

  • 系統類,如程序是否存活;
  • 網路類,頁面能否正常開啟;
  • 功能類,埠是否有響應,HTTP狀態碼是否正確,頁面內容是否正確;
  • 整合類,與其他存在呼叫關係依賴的應用介面間,是否可以正常呼叫;
    等等。
    其中,系統、功能和整合類的測試內容,大多需要在應用程式所在主機系統中進行測試驗證,而網路類的測試則需要從系統和應用的外部進行訪問測試。

綠燈測試功能的技術實現

在這裡,我們主要是通過WEB框架Django+自動化運維工具SaltStack來實現的。其中Django提供一個應用系統資訊化、平臺化管理的服務,而SaltStack則作為底層工具,通過介面提供遠端執行命令、配置同步、在本地執行命令等功能的支援。在WEB頁面中,則通過應用jquery的異常操作來為使用者提供更好的使用體驗。

需要實現一個應用程式資訊的管理功能

提供應用程式資訊和維護方法的定義和管理。
green-light1
如圖所示,我們需要預先把一個應用運維、監控相關的資訊儘可能完整和準確得輸入到系統中。

提供一個應用的釋出管理功能

為應用程式提供基本的程式啟、停、配置同步、檢視或下載日誌的服務。當然,正像本文所描述的,我們還實現了一個綠燈測試功能。
這裡寫圖片描述

實現綠燈測試功能的主函式

目前暫實現了程序存活檢測、服務埠響應檢測、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\" />");
                }
            }
        });

綠燈測試功能的演示

預設狀態下的頁面效果

這裡寫圖片描述

正在執行測試

這裡寫圖片描述

通過測試後的效果

這裡寫圖片描述

未通過綠燈測試時亮起紅燈

這裡寫圖片描述

全文完!