最近痛感在叢集裡逐台部署ganglia, cacti這些監控的東西很麻煩,就寫了個小程式去批量自動部署。原理是通過python的pexpect用ssh去複製檔案和執行系統命令,我用它來部署ganglia等,但是其他的東西也可以通過這個指令碼來批量部署,只要自己編寫部署指令碼就可以了。主要是提出一個解決思路,看對大家是否有所協助。
先約定一個概念,我們把放置python和指令碼的伺服器叫做主控節點或者server,把需要安裝的節點叫做受控節點或者client。以下均以server和client代稱。
首先是設定檔,我需要先定義一個設定檔,來約定server的一些路徑,和client具體資訊。
#-*-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'},]
接下來是主程式
#!/usr/bin/env python#-*-coding:UTF-8 -*-import osimport sysimport platform#用import方式引入conf.pyimport confimport 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 pexpectexcept 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_dirlogs = conf.log_dirc_tmp = conf.client_tmp_dirport = conf.ssh_portscripts = conf.script_dirnodes = conf.node_listexpect = 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,反正能用指令碼完成的事都可以幹。
本文出自 “實踐檢驗真理” 部落格,謝絕轉載!