簡易自動化部署服務器集羣

前提

目前公司使用多個服務器對外提供服務。其中只有一臺服務器有外網帶寬,有幾臺內網業務服務器。這帶有兩個問題:python

  • 怎麼管理內網的服務器
  • 怎麼自動化部署服務器,減小人工參與的工做量和失誤

針對這兩個問題,咱們使用SSH/SCP和PEXPECT來解決。linux

SSH/SCP

咱們使用SSH經過有外網的服務器創建起本地和沒有外網的服務器的隧道,以後全部的操做均可以經過這個隧道來進行操做。正則表達式

首先進行隧道的建立。windows

ssh -L [bind_address:]tunnelport:host:hostport <SSH hostname>
  • bind_address 指定綁定的IP地址,默認狀況會綁定在本地的迴環地址(即127.0.0.1),若是空值或者爲*會綁定本地全部的IP地址,若是但願綁定的端口僅供本機使用,能夠指定爲localhost。
  • tunnelport 指定本地綁定的端口
  • host 指定目標地址的IP,若是目標主機和ssh server是同一臺主機時該參數指定爲localhost
  • host_port 指定目標端口。當咱們要使用SCP/SSH時都爲22
  • SSH hostname指有外網帶寬的服務器

而後就是經過隧道使用SSH登錄無外網帶寬的服務器服務器

ssh -p tunnelport x@127.0.0.1

這裏使用-p參數,把ssh使用的端口爲以前綁定的隧道端口tunnelport。ssh

使用SCP進行文件的傳輸操做。函數

scp -P tunnelport src_file x@127.0.0.1:dst_file

這裏使用-P參數,把SCP使用的端口設置爲以前綁定的隧道端口tunnelport。測試

  • src_file本地文件
  • dst_file須要拷貝到的文件路徑或文件名

Pexpect

Pexpect 是一個用來啓動子程序並對其進行自動控制的 Python 模塊。 Pexpect 能夠用來和像 ssh、ftp、passwd、telnet 等命令行程序進行自動交互,方便在工做中實現與命令行交互的自動化。spa

在作實驗的過程主要使用了spawn、sendline和expect三個函數來實現咱們的要求。命令行

  • spawn

    class spawn:
          def __init__(self,command,args=[],timeout=30,maxread=2000,searchwindowsize=None, logfile=None, cwd=None, env=None)
    
    spawn是Pexpect模塊主要的類,用以實現啓動子程序,它有豐富的方法與子程序交互從而實現用戶對子程序的控制。它主要使用 pty.fork() 生成子進程,並調用 exec() 系列函數執行 command 參數的內容。
  • expect

    expect(self, pattern, timeout=-1, searchwindowsize=None)
    在參數中: pattern 能夠是正則表達式, pexpect.EOF , pexpect.TIMEOUT ,或者由這些元素組成的列表。須要注意的是,當 pattern 的類型是一個列表時,且子程序輸出結果中不止一個被匹配成功,則匹配返回的結果是緩衝區中最早出現的那個元素,或者是列表中最左邊的元素。使用 timeout 能夠指定等待結果的超時時間 ,該時間以秒爲單位。當超過預訂時間時, expect 匹配到pexpect.TIMEOUT。
  • sendline
    這些方法用來向子程序發送命令,會額外在後面多加個回車來模擬操做。與之相關的還有send和sendcontrol兩個函數

實驗例程

#!/usr/bin/env python
# -*- coding: utf-8 -*- 

import pexpect
import os
import time

#cmd須要向命令行輸入的命令
#passeword host的密碼
def ssh_login (cmd, password):
    ssh_newkey = 'Are you sure you want to continue connecting'
    new_ssh = pexpect.spawn(cmd)
    i = new_ssh.expect([pexpect.TIMEOUT, ssh_newkey, 'password: '])    #登錄的時候會有兩種狀態
    if i == 0:      #若是是超時
        print 'ERROR!'
        print 'SSH could not login. Here is what SSH said:'
        print new_ssh.before, new_ssh.after
        return None
    if i == 1:   #若是須要輸入信息登錄
        new_ssh.sendline ('yes')
        i = new_ssh.expect([pexpect.TIMEOUT, 'password: '])
        if i == 0:
            print 'ERROR!'
            print 'SSH could not login. Here is what SSH said:'
            print new_ssh.before, new_ssh.after
            return None

    new_ssh.sendline(password)

    return new_ssh

#登出
def ssh_logout(newpexpect):
    newpexpect.close()

#等待終端出輸入符號
def ssh_wait_prompts(ssh):
    ssh.expect([pexpect.EOF, '[$#>]'])

#因爲scp須要在沒有創建連接的時候須要驗證,因此封裝一下
def scp_run(cmd, password):
    new_scp = ssh_login(cmd, password)
    ssh_wait_prompts(new_scp)
    ssh_logout(new_scp)

def main ():
        #建立隧道,這個須要一直保持,直到不用這個隧道時才能釋放
    server1_tunnel = ssh_login ("ssh -L 8082:192.168.132.144:22 x@192.168.132.141","x")   
    slb = ssh_login('ssh x@192.168.132.144', 'x')   #正常登錄服務器
    server1 = ssh_login('ssh -p 8082 x@127.0.0.1', 'x') #經過隧道登錄服務器
    scp_run('scp -P 8082 /home/x/test.py x@127.0.0.1:/home/x/tesdt.py', 'x')  #經過隧道傳送文件

    ssh_wait_prompts(server1)
    #經過ssh運行一個腳本,刪除文件
    server1.sendline('/home/x/del.sh')

    #測試連通性 
    ssh_wait_prompts(server1)
    server1.sendline('ls')
    ssh_wait_prompts(server1)

    print server1.before

    ssh_logout(server1)
    ssh_logout(slb)
    ssh_logout(server1_tunnel)

if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print 'fail', str(e)
        traceback.print_exc()
        os._exit(1)

參考資料

  • http://www.ibm.com/developerworks/cn/linux/l-cn-sshforward/
  • http://www.ibm.com/developerworks/cn/linux/l-cn-pexpect1/
  • http://www.ibm.com/developerworks/cn/linux/l-cn-pexpect2/
相關文章
相關標籤/搜索