Tsung筆記之分佈式增強跳出SSH羈絆篇

前言

Erlang天生支持分佈式環境,Tsung框架的分佈式壓測受益於此,簡單輕鬆操控子節點生死存亡、派發任務等不費吹灰之力。

Tsung啓動分佈式壓測時,主節點tsung_controller默認情況下需要通過SSH通道連接到遠程機器上啓動從節點,那麼問題便來了,一般互聯網公司基於跳板/堡壘機/網關授權方式訪問機房服務器,那麼SSH機制失效,並且被明令禁止。SSH不通,Tsung主機啓動不了從機,分佈式更無從談起。

那麼如何解決這個問題呢,讓tsung在複雜的機房網絡環境設定下更加如魚得水,將是本文所討論的內容。


RSH:Remote Shell

RSH,remote shell縮寫,維基百科上英文解釋:https://en.wikipedia.org/wiki/Remote_Shell。作爲一個終端工具,Linux界鳥哥曾經寫過 RSH客戶端和服務器端搭建教程

在CentOS下安裝也簡單:

yum install rsh

Erlang藉助於rsh命令行工具通過SSH通道連接到從節點啓動Tsung應用,下面可以看到rsh工具本身失去了原本的含義,類似於exec命令功效。

比如Erlang主節點(假設這個服務器名稱爲node_master,並且已經在/etc/hosts文件建立了IP地址映射)在啓動時指定rsh的可選方式爲SSH:

erl -rsh ssh -sname foo -setcookie mycookie

啓動之後,要啓動遠程主機節點名稱爲node_slave的子節點:

slave:start(node_slave, bar, "-setcookie mycookie").

上面Erlang啓動從節點函數,最終被翻譯爲可執行的shell命令:

ssh node_slave erl -detached -noinput -master foo@node_master -sname bar@node_slave -s slave slave_start foo@node_master slave_waiter_0 -setcookie mycookie

erl命令Erlang的啓動命令,要求主機node_slave自身也要安裝了Erlang的運行時環境纔行。

從節點的啓動命令最終依賴於SSH連接並遠程執行,其通用一般格式爲:

ssh HOSTNAME/IP Command

這就是基於Erlang構建的Tsung操控從節點啓動的最終實現機制。

其它語言中,Master啓動Slave也是如此機制

SSH爲通用方案,但不是最好的方案

業界選用SSH機制連接遠程Unix/Linux服務器主機,分佈式環境下要能夠自由免除密碼方式啓動遠程主機上(這裏指的是內部Lan環境)應用,一般需要設置公鑰,需要傳遞公鑰,需要保存到各自機器上,還有經常遇到權限問題,很是麻煩,這是其一。若要取消某臺服務器登陸授權,則需要被動修改公鑰,也是不夠靈活。

另外一般互聯網公司處於安全考慮都會禁止公司內部人員直接通過SSH方式登錄到遠程主機進行操作,這樣導致SSH通道失效,Tsung主機通過SSH連接到從機並執行命令,也就不可能了。

其實,在基於分佈式壓測環境下,快速租賃、快速借用/歸還的模型就很適合。一般公司很少會存在專門用於壓測的大量空閒機器,但是線上會運行着當前負載不高的服務器,可以拿來用作壓測客戶端使用,用完就歸還。因爲壓測不會是長時間運行的服務,其爲短時間行爲。這種模式下就不適合複雜的SSH公鑰滿天飛,後期忘記刪除的情況,在壓測端超多的情況下,無疑也將造成運維成本激增,安全性降低等問題。

SSH替換方案:一種快速租賃模式遠程終端方案

現在需要尋找一種新的代替方案,一種適應快速租賃的遠程終端實現機制。

替換方案要求點

  1. 類似於SSH Server,監聽某個端口,能夠執行傳遞過來的命令
  2. 能夠根據IP地址授權,這樣只有Tsung Master才能夠訪問從節點,從節點之間無法直接對連
  3. 需要接受一些操控指令,可以判斷是否存活
  4. 一到兩個腳本/程序搞定,儘量避免安裝,開箱即用
  5. 總之配置、操作一定要簡單,實際運維成本一定要低

沒找到很輕量的實現,可以設計並實現這樣一種方案。

服務器端守護進程

輕量級服務端守護進程 = 一個監控端口的進程(rsh_daemon.sh) + 執行命令過濾功能(rsh_filter)

rsh_daemon.sh 負責守護進程的管理:

  • 基於CentOS 6/7默認安裝的ncat程序
  • 主要用於管理19999端口監聽
  • start/stop/restart 負責監控進程啓動、關閉
  • status 查看進程狀態
  • kill 提供手動方式關閉並刪除掉自身
  • rsh_filter用於檢測遠程傳入命令並進行處理
    • 接收ping指令,返回pong
    • 執行Erlang從節點命令,並返回 done 字符串
    • 對不合法命令,直接關閉

rsh_daemon.sh代碼很簡單:

#!/bin/bash
# the script using for start/stop remote shell daemon server to replace the ssh server
PORT=19999
FILTER=~/tmp/_tmp_rsh_filter.sh
# the tsung master's hostname or ip
tsung_controller=tsung_controller
SPECIAL_PATH=""
PROG=`basename $0`

prepare() {
    cat << EOF > $FILTER
#!/bin/bash

ERL_PREFIX="erl"

while true
do
    read CMD
    case \$CMD in
        ping)
            echo "pong"
            exit 0
            ;;
        *)
            if [[ \$CMD == *"\${ERL_PREFIX}"* ]]; then
                exec $SPECIAL_PATH\${CMD}
            fi
            exit 0
            ;;
    esac
done
EOF
    chmod a+x $FILTER
}

start() {
    NUM=$(ps -ef|grep ncat | grep ${PORT} | grep -v grep | wc -l)

    if [ $NUM -gt 0 ];then
        echo "$PROG already running ..."
        exit 1
    fi

    if [ -x "$(command -v ncat)" ]; then
        echo "$PROG starting now ..."
        ncat -4 -k -l $PORT -e $FILTER --allow $tsung_controller &
    else
        echo "no exists ncat command, please install it ..."
    fi
}

stop() {
    NUM=$(ps -ef|grep ncat | grep rsh | grep -v grep | wc -l)

    if [ $NUM -eq 0 ]; then
        echo "$PROG had already stoped ..."
    else
        echo "$PROG is stopping now ..."
        ps -ef|grep ncat | grep rsh | grep -v grep | awk '{print $2}' | xargs kill
    fi
}

status() {
    NUM=$(ps -ef|grep ncat | grep rsh | grep -v grep | wc -l)

    if [ $NUM -eq 0 ]; then
        echo "$PROG had already stoped ..."
    else
        echo "$PROG is running ..."
    fi
}

usage() {
    echo "Usage: $PROG <options> start|stop|status|restart"
    echo "Options:"
    echo "    -a <hostname/ip>  allow only given hosts to connect to the server (default is tsung_controller)"
    echo "    -p <port>         use the special port for listen (default is 19999)"
    echo "    -s <the_erl_path> use the special erlang's erts bin path for running erlang (default is blank)"
    echo "    -h                display this help and exit"
    exit
}

while getopts "a:p:s:h" Option
do
    case $Option in
        a) tsung_controller=$OPTARG;;
        p) PORT=$OPTARG;;
        s) TMP_ERL=$OPTARG
            if [ "$OPTARG" != "" ]; then
                if [[ "$OPTARG" == *"/" ]]; then
                    SPECIAL_PATH=$OPTARG
                else
                    SPECIAL_PATH=$OPTARG"/"
                fi
            fi
            ;;
        h) usage;;
        *) usage;;
    esac
done
shift $(($OPTIND - 1))

case $1 in
        start)
            prepare
            start
            ;;
        stop)
            stop
            ;;
        status)
            status
            ;;
        restart)
            stop
            start
            ;;
        *)
            usage
            ;;
esac

總結一下:

  • 基於ncat監聽19999端口提供bind shell機制,但限制有限IP可訪問
  • 動態生成命令過濾腳本rsh_filter.sh,執行Erlang從節點命令

請參考:https://github.com/weibomobile/tsung_rsh/blob/master/rsh_daemon.sh

客戶端連接方案

服務器端已經提供了端口接入並準備好了接收指令,客戶端(rsh_client.sh)可以進行連接和交互了:

  • 類似SSH客戶端接收方式:rsh_client.sh Host/IP Command
  • 完全基於nc命令,連接遠程主機
  • 連接成功,發送命令
  • 得到相應,流程完成

一樣非常少的代碼呈現。

#!/bin/sh

PORT=19999

if [ $# -lt 2  ]; then
    echo "Invalid number of parameters"
    exit 1
fi

REMOTEHOST="$1"
COMMAND="$2"

if [ "${COMMAND}" != "erl"  ]; then
    echo "Invalid command ${COMMAND}"
    exit 1
fi

shift 2

echo "${COMMAND} $*" | /usr/bin/nc ${REMOTEHOST} ${PORT}

Erlang主節點如何啓動

有了SSH替換方案,那主節點就可以這樣啓動了:

erl -rsh ~/.tsung/rsh_client.sh -sname foo -setcookie mycookie

比如當Tsung需要連接到另外一臺服務器上啓動從節點時,它最終會翻譯成下面命令:

/bin/sh /root/.tsung/rsh_client.sh node_slave erl -detached -noinput -master foo@node_master -sname bar@node_slave -s slave slave_start foo@node_master slave_waiter_0 -setcookie mycookie

客戶端腳本rsh_client.sh則最終需要執行連接到服務器、併發送命的命令:

echo "erl -detached -noinput -master foo@node_master -sname bar@node_slave -s slave slave_start foo@node_master slave_waiter_0 -setcookie mycookie" | /usr/bin/nc node_slave 19999

這樣就實現了和SSH一樣的功能了,很簡單吧。

Tsung如何切換切換?

爲tsung啓動添加-r參數指定即可:

tsung -r ~/.tsung/rsh_client.sh -f tsung.xml start

進階:可指定運行命令路徑

rsh_client.sh腳本最後一行修改一下,指定目標服務器erl運行命令:

#!/bin/sh

PORT=19999

if [ $# -lt 2  ]; then
    echo "Invalid number of parameters"
    exit 1
fi

REMOTEHOST="$1"
COMMAND="$2"

if [ "${COMMAND}" != "erl"  ]; then
    echo "Invalid command ${COMMAND}"
    exit 1
fi

shift 2
exec echo "/root/.tsung/otp_18/bin/erl $*" | /usr/bin/nc ${REMOTEHOST} 19999

上面腳本所依賴的上下文環境可以是這樣的,機房服務器操作系統和版本一致,我們把Erlang 18.1整個運行時環境在一臺機器上已經安裝的目錄(比如目錄名爲otp_18),拷貝到遠程主機/root/.tsung/目錄,相比於安裝而言,可以讓Tsung運行依賴的Eralng環境完全可以移植化(Portable),一次安裝,多次複製。

代碼託管地址

本文所談及代碼,都已經託管在github:
https://github.com/weibomobile/tsung_rsh

後續代碼更新、BUG修復等,請直接參考該倉庫。

小結

簡單一套新的替換SSH通道無密鑰登陸遠程主機C/S模型,雖然完整性上無法與SSH相比,但勝在簡單夠用,完全滿足了當前業務需要,並且其運維成本低,無疑讓Tsung在複雜服務器內網環境下適應性又朝前多走了半里路。

下一篇將介紹爲Tsung增加IP直連特性支持,使其分佈式網絡環境下適應性更廣泛一些。

轉自:http://www.blogjava.net/yongboy/archive/2016/07/27/431340.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章