> Linux集群 > 服务器集群 >

python加shell快速部署集群

最近痛感在集群里逐台部署ganglia, cacti这些监控的东西很麻烦,就写了个小程序去批量自动部署。原理是通过python的pexpect用ssh去复制文件和执行系统命令,我用它来部署ganglia等,但是其他的东西也可以通过这个脚本来批量部署,只要自己编写部署脚本就可以了。主要是提出一个解决思路,看对大家是否有所帮助。

 

先约定一个概念,我们把放置python和脚本的服务器叫做主控节点或者server,把需要安装的节点叫做受控节点或者client。以下均以server和client代称。

 

首先是配置文件,我需要先定义一个配置文件,来约定server的一些路径,和client具体信息。

1
2
3
4
5
6
7
8
9
10
#-*-coding:UTF-8 -*-
log_dir = './logs/'             #定义日志路径,不过我还没写,打算用log4py来做
client_tmp_dir = '/tmp/'        #定义client端存放脚本路径
ssh_port = '22'                 #SSH端口
script_dir = './shells/'        #server端脚本存放路径
node_list = [
    {'ip':'192.168.1.1', 'user':'root', 'passwd':'123456', 'cmd':'sh /tmp/dpkg_client_ubuntu_x.x86_64.sh'},
    #cmd为在client端执行的命令,别的不解释
    {'ip':'192.168.1.2', 'user':'root', 'passwd':'123456', 'cmd':'sh /tmp/dpkg_client_ubuntu_x.x86_64.sh'},
]

 

接下来是主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/usr/bin/env python
#-*-coding:UTF-8 -*-
import os
import sys
import platform
#用import方式引入conf.py
import conf
import subprocess
'''
判断server(本机)操作系统类型和版本的类
'''
class System:
    def GetBranch(self):
        Branch = platform.dist()[0]
        return Branch
    def GetRelease(self):
        Release = platform.dist()[1]
        return Release
    def GetInstaller(self):
        if self.GetBranch() in ['Ubuntu', 'debian']:
            installer = 'apt-get'
        elif self.GetBranch() in ['redhat', 'fedora', 'centos']:
            installer = 'yum'
        elif self.GetBranch() in ['SuSE']:
            installer = 'zypper'
        else:
            installer = 'unknown'
        return installer
'''
以操作系统版本为依据获取相应的pexpect包并尝试引入,因pexpect非默认操作系统安装,这部分支持RH,Ubuntu,Debian,SuSE
'''
try:
    import pexpect
except ImportError:
    installer = System()
    inst = install.GetInstaller()
    if (inst == 'apt-get') or (inst == 'zypper'):
        cmd = '%s install python-pexpect' % (inst)
    elif inst == 'yum':
        cmd = '$s install pexpect' % (inst)
    else:
        cmd = 'echo "Not support yet:)"';
    try:
        fd = subprocess.Popen( cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
        out = fd.stdout.readlines()
        err = fd.stderr.readlines()
        all = out+err
        all = "".join(all)
    except OSError,e:
        all = "Cannot run command, Exception:"+e+os.linesep
    import pexpect
#print all
'''
pexpect执行类,分两个方法,ssh和scp,自动判断是否首次连接,并自动完成yes或输入密码的应答。
'''
class Expect:
    #定义ssh方法,入口变量包括ip, port,username,password,执行命令
    def ssh(self, ip, port, user, passwd, cmd):
        #创建连接子进程对象
        ssh = pexpect.spawn('ssh -p %s %s@%s "%s"' % (port, user, ip, cmd))
        r = ''
        try:
            #判断是否首次连接,如果是首次,则回答yes并输入密码,否则直接输入密码
            i = ssh.expect(['password:', 'continue connecting (yes/no)?'], timeout=5)
            if i == 0 :
                ssh.sendline(passwd)
            elif i == 1:
                ssh.sendline('yes')
                ssh.expect('password:')
                ssh.sendline(passwd)
        except pexpect.EOF:
            ssh.close()
        else:
            r = ssh.read()
            ssh.expect(pexpect.EOF)
            ssh.close()
        return r
    #定义scp方法,入口变量包括ip,port,username,password,需要复制到client的文件名,复制到client的路径
    def scp(self, ip, port, user, passwd, srcfile = "index.html", distpath):
        #创建连接子进程对象
        ssh = pexpect.spawn('scp -P %s %s %s@%s:%s ' % (port, file, user, ip, distpath))
        r= ''
        try:
            #判断是否首次连接,如果是首次,则回答yes并输入密码,否则直接输入密码
            i = ssh.expect(['password:', 'continue connecting (yes/no)?'], timeout=5)
            if i == 0:
                ssh.sendline(passwd)
            elif i == 1:
                ssh.senline('yes')
                ssh.expect('password:')
                ssh.sendline(passwd)
        except pexpect.EOF:
            ssh.close()
        else:
            r = ssh.read()
            ssh.expect(pexpect.EOF)
            ssh.close()
        return r
#创建conf中的对象,只是为了写起来方便。不创建直接用也行
packages = conf.package_dir
logs = conf.log_dir
c_tmp = conf.client_tmp_dir
port = conf.ssh_port
scripts = conf.script_dir
nodes = conf.node_list
expect = Expect()
#在本机执行server端脚本。该脚本会安装Ganglia gmetad,gmond,cacti,nagios,gangliaweb,mysql,apache等等
os.system("sh " + scripts + "dpkg_server_ubuntu_x.x86_64.sh")
#循环列出conf的列表中配置的主机,用户名,密码,执行命令
for i in range(len(nodes)):
    ip = nodes[i]['ip']
    user = nodes[i]['user']
    passwd = nodes[i]['passwd']
    cmd = nodes[i]['cmd']
    #将本机的client执行脚本复制到client端的conf.py中定义的路径client_tmp_dir
    r = expect.scp(ip, port, user, passwd, scripts+'dpkg_client_ubuntu_x.x86_64.sh', c_tmp)
    print r
    #在client端执行刚复制过去的脚本,脚本中包含gmond,nagios-client,snmpd等等
    r = expect.ssh(ip, port, user, passwd, cmd)
    print r

 

我还没写按自动判断client端操作系统安装不同脚本的逻辑,有兴趣的可以自己改改,其实本身脚本并不难,核心都是在pexpect,我主要是不想用puppet,不想用puppet的主要原因是不想学ruby和他那复杂的配置文件。不光是部署监控,自己写shell脚本,随便部署什么都可以。nginx,php,反正能用脚本完成的事都可以干。



(责任编辑:IT)