批量配置计算机集群SSH免登录

来源:互联网 发布:西昊 ergomax 知乎 编辑:程序博客网 时间:2024/05/02 02:45

本文所有代码可在https://github.com/alphg/zookeeper-hadoop-hbase-setup-tools查看
今天准备在自已电脑上使用5台虚拟机搭建一个zookeeper+hadoop+hbase的一个完全分布模式的实验环境。每台机器都安装ubuntu server 12.04版本的linux系统,并正确安装ssh。
给5台机器分别起名为h1、h2、h3、h4、h5。
新建一个虚拟机名称为dns,安装bind9用作域名解析(问问度娘,确保五台机器能够互联)
现在我想让这五台机器之间实现ssh互联免密码登录,手工使用使命ssh-keygen进行配置虽然简单(网上有很多相关教程)但如果我们在一个生产环境中有几百台机器,这个工作量还是很大的,所以我就想能不能使用python脚本代替手工一台台的配置呢?结果发现还真是可以,下面就是我的实现过程。
首先 需要安装python 的 paramiko模块,我使用一个比较简单的方法安装这个模块。

apt-cache search paramiko
搜索看有没有这个模块,结果发现还真有如下图所示
这里写图片描述
这就很简单了,我们直接安装这个模块就好了
apt-get install python3-paramiko(本机安装的ptython3)
我们使用python3命令行进行一个测试看看能不能正常导入
这里写图片描述
可以看到正常import 没有报错
(如果不能正常安装大家可以参考http://www.cnblogs.com/linuxliu/articles/4756067.html 进行手动安装)

在安装完成后我们就可以在python3的命令行中依次执行以下命令来简单学习paramiko的使用

import paramikossh=paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())ssh.connect("h2",22,"hadoop","password")//h2 主机名 22 端口号 hadoop 用户名 "password" 密码stdin,stdout,stderr=ssh.exec_command("echo hello!")for o in stdout:    print(o)

可以看到屏幕上正常打印出”hello!”说明我们的ssh连接程序起作用

接下来我们写一段脚本为h1创建hadoop用户,并生成ssh的公钥和私钥

import paramikossh=paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy());ssh.connect("h1",22,"root","rootwd");/*先使用root用户连接 h1 主机名 22端口号  root 用户名 rootwd 密码*/ssh.exec_command("addgroup hadoop"); /*由于集群是用来运行hadoop的,所以这里添加一个名为hadoop的用户组*/ssh.exec_command("adduser -ingroup hadoop -home /home/hadoop hadoop") /*创建用户 家目录指定在/home/hadoop*/ssh.exec_command("echo -e 'hadooppassword\nhadooppassword'|passwd hadoop")/*为用户hadoop指定用户密码,你可以将‘hadooppassword’替换成任何你需要的密码(注意可能由于linux 系统版本不同,以\n分隔的hadooppassword的数量可能会不同,可以在命令行下运行passwd hadoop 进行测试,看需要输入几次密码)*/ssh.exec_command("sudo -u hadoop ssh-keygen -t rsa -P '' -f /home/hadoop/.ssh/id_rsa") /*在/home/hadoop/.ssh目录下生成 id_rsa 和id_rsa.pub的加密文件*/

我们可以去主机h1的 /home/hadoop/.ssh目录下检查一下是否出现id_rsa 和id_rsa.pub两个文件,如果出现说明上述代码运行成功,如果没有,则需要逐行执行上述代码,并观查其每一步执行是否成功。
接下来我们改写上述代码使其自动为集群中所有电脑自动生成ssh公钥和私钥

#!/usr/bin/python3import paramikohosts=["h1","h2","h3","h4","h5"] //集群中所有主机名(此处为方便将主机名写在代码当中,如果主机名有很多 比如成百上千个,可以将所有主机名全部写在一个文件中,安装的时候去读取文件即可。rootpwd="900614"//集群中所有主要的root用户的密码,为便于管理,所有root帐户密码均相同hadooppwd="900614" //为集群中新建的hadoop用户设置密码,所有主机一样都使用此密码def installSSH(host)://定义ssh安装函数        try:                ssh=paramiko.SSHClient()                ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())                ssh.connect(host,22,'root',rootpwd)                stdin,stdout,stderr=ssh.exec_command("addgroup hadoop")                print(stdout.readlines())                print(stderr.readlines())//查看命令行的输出可以看到命令是否执行成功                stdin,stdout,stderr=ssh.exec_command("adduser -ingroup hadoop -home /home/hadoop hadoop")                print(stdout.readlines())                print(stderr.readlines())//查看命令行的输出可以看到命令是否执行成功                stdin,stdout,stderr=ssh.exec_command("echo -e '"+hadooppwd+"\n"+hadooppwd+"' | passwd hadoop")                print(stdout.readlines())                print(stderr.readlines())//查看命令行的输出可以看到命令是否执行成功                ssh.exec_command("sudo -u hadoop ssh-keygen -t rsa -P '' -f /home/hadoop/.ssh/id_rsa")                ssh.close();        except:                print(host+' error')if __name__=='__main__':        for host in hosts:                print(host + ' start install ssh')                installSSH(host)                print(host + ' end instal ssh')

至此,我们已经为集群中的每台机器生成了免登录所需要的ssh公钥和私钥,接下来我们需要的就是把他们全部收集起来,并将他们合并到一个文件中,并将此文件的文件名改为authorized_keys
第一步收集所有的公钥到同一台电脑当中,使用到的代码如下(本人shell编程属于菜鸟水平,读者有什么改进意见可以在下面留言)
第一步使用expect完成scp过程中输入密码等操作的自动交互代码(对于expect的详细用法可以参见下面参考文献中的第1条)

#!/usr/bin/expect -fset timeout 5//设置超时时间 5set host [lindex $argv 0]//读取由shell传递进来的第一个参数  主机名spawn scp root@$host:/home/hadoop/.ssh/*.pub /home/hadoop/${host}_id_rsa.pub   //从远程机上将公钥文件(文件后缀为.pub 这里使用了通配符*)复制到本地的/home/hadoop目录下expect {"(yes/no)" {send "yes\r";exp_continue}   //需要选择yse或者no时,自动回答yes"password:" {send "900614\r"}  //需要提供密码时,自动传入root帐户的密码}expect eofexit

将上述代码保存为/home/hadoop/auto_scp.exp

第二步写一段shell代码循环执行auto_scp.exp,代码如下:

#!/bin/bashhosts=(h1 h2 h3 h4 h5)//主机名数组,如果很多可以写在文件中逐一读取for ((i=0;i<${#hosts[@]};i++))//循环hosts数组,do        /home/hadoop/auto_scp.exp ${hosts[i]}//每次循环都执行第一部分完成的脚本auto_scp.exp并将不同的主机作为参数传递给它。done

将此段代码保存为/home/hadoop/collect_pubkeys.sh
接下来为两段代码添加可执行权限并运行 collect_pubkeys.sh

chmod +x auto_scp.expchmod +x collect_pubkeys.sh/home/hadoop/collect_pubkeys.sh

执行结果看起来应该是下面这个样子
这里写图片描述

如果一切都成功那么我们在/home/hadoop/目录下看到一系列后缀为.pub的文件,接下来我们合并所有的.pub文件到authorized_keys中(这个很简单了)

cat /home/hadoop/*.pub >> /home/hadoop/authorized_keys  //将所有后缀为.pub的文件的文件内容合并到文件authorized_keys中

现在到这一步中,我们完成了集群自动认证的公钥文件authorized_keys 接下来我们需要把这个文件再分发到各个计算机中,分发的代码类似于collect_pubkeys.sh和auto_scp.exp。
我们修改一下auto_scp.exp 修改后代码如下

#!/usr/bin/expect -fset timeout 5set host [lindex $argv 0]spawn scp /home/hadoop/authorized_keys hadoop@$host:/home/hadoop/.ssh  //意为将本机的authorized_keys使用用户hadoop 复制到相应主机的/home/hadoop/.ssh目录下 expect {"(yes/no)" {send "yes\r";exp_continue}"password:" {send "900614\r"}}expect eofexit

collect_pubkeys.sh 不需要修改 执行完成后应该如下图所示
这里写图片描述

(图片中所示目录与文章所述不符大家可不用在意只肆出现100%就说明复制成功了)

至此我们就完成了所有ssh免登录的配置工作。

实用代码片段收集

1、在我们修改新建的hadoop用户的登录密码时使用的是以下代码(未测试)

ssh.exec_command("echo -e 'hadooppassword\nhadooppassword'|passwd hadoop")

在这其中使用了 linux 的管道。
但是有一些情况可能不适合使用管道
我们可能参考下列代码进行更改

stdin, stdout, stderr=ssh.exec_command('sudo su')stdin.write('123456')stdin.flush()

2、推荐python各种实用模块下载地址
https://pypi.python.org/pypi

错误处理

1、由于paramiko的版本问题 有可能会出现以下错误
这里写图片描述
解决该问题需要更新paramiko版本。可在https://pypi.python.org/pypi 搜索不同的版本进行测试(一般最新版本不会出问题)
2、在最后测试是否可以免密码登陆时,有可能出列
sign_and_send_pubkey: signing failed: agent refused operation 错误,仍需要输入密码才能使出
大家参考 https://help.github.com/articles/error-agent-admitted-failure-to-sign/
需要执行下列代码

eval "$(ssh-agent -s)"ssh-add

参考文献

  1. http://blog.csdn.net/zhuying_linux/article/details/6902135 shell expect的简单用法
  2. http://www.cnblogs.com/lottu/p/4011743.html shell脚本编程之循环语句
  3. http://blog.chinaunix.net/uid-20769502-id-3011711.html linux shell 数组建立及使用技巧

最后

在结束之后发现本文代码在实际运行过程中还是会有很多问题,事情往往就是这样,当你引入新的方法去解决旧的问题的时候总是会出现新的问题需要解决,所以这篇文章暂时就先这样吧,在此留一些我暂时能相到的改进思路

  1. 将exec_command()使用invoke_shell()代替,这样改的好处是我们可以对各个命令的输入输出进行更细化的管理,减少命令执行失败的机会
0 0