简易自动化部署服务器集群

前提

目前公司使用多个服务器对外提供服务。其中只有一台服务器有外网带宽,有几台内网业务服务器。这带有两个问题:

  • 怎么管理内网的服务器
  • 怎么自动化部署服务器,减少人工参与的工作量和失误

针对这两个问题,我们使用SSH/SCP和PEXPECT来解决。

SSH/SCP

我们使用SSH通过有外网的服务器建立起本地和没有外网的服务器的隧道,之后所有的操作都可以通过这个隧道来进行操作。

首先进行隧道的创建。

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。

使用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 等命令行程序进行自动交互,方便在工作中实现与命令行交互的自动化。

在做实验的过程主要使用了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)

参考资料

posted @ 2014-11-17 07:34  山坡上的人们  阅读(1173)  评论(0编辑  收藏  举报