Linux高级技巧之集群部署(七)
计算机原理系列
欢迎大家关注「海拉鲁知识大陆」 多交流不迷路
Linux高级技巧之集群部署(七)
看过我前面的系列文章,相信大家已经掌握了一些基础指令的使用方法,今天我们继续聊哈——用Linux指令管理一个集群。这属于Linux指令的高级技巧,高级技巧并不是我们要学习更多的指令,而是要把之前所学的指令进行排列组合融会贯通。从最初只能写几条指令、执行然后看结果,成长到具备书写一个拥有几十行、甚至上百行的bash脚本的能力时,也就意味着大家具备了解决复杂问题的能力。在性能测试过程中就是要这种能解决各种复杂问题的能力。
1. 集群环境介绍
本次只用了3台服务器作为演示示例哈,但是接下来我们要写的脚本和解决问题的思路是可以在很多台服务器之间复用的。在性能测试过程中可能要配置监控,安装监控,收集指标等等可能都是大量服务器的环境哈。所以还是有必要了解哈。
2. 循环遍历IP列表
一个局域网中有很多服务器需要管理,它们彼此之间网络互通,我们通过一台主服务器对它们进行操作。
在主服务器(192.168.21.210)上我们维护一个ip地址的列表,保存成一个文件,如下图所示:
目前iplist中只有两个,如果我们是大量的机器就可以在里面放成百上千个哈。接下来思考下shell如何遍历这些ip?
先尝试实现一个最简单的程序,从文件iplist中读出这些ip并尝试用for循环遍历这些ip,具体程序如下:
#!/usr/bin/env bash
readarray -t ips < iplist
for ip in "${ips[@]}"; doecho "$ip"
done
首行的#!叫作Shebang。Linux的程序加载器会分析Shebang的内容,决定执行脚本的程序。这里我们希望用bash来执行这段程序,readarray指令是bash 4.0后才有的能力哈。
readarray指令将iplist文件中的每一行读取到变量ips中。ips是一个数组,可以用echo ips[@]打印其中全部的内容:@代表取数组中的全部内容;{ips[@]}打印其中全部的内容:@代表取数组中的全部内容;ips[@]打印其中全部的内容:@代表取数组中的全部内容;符号是一个求值符号。不带$的话,ips[@]会被认为是一个字符串,而不是表达式。
for循环遍历数组中的每个ip地址,echo把地址打印到屏幕上。
如果用shell执行上面的程序会报错,因为readarray是bash 4.0后支持的能力哈,我们用chomd为foreach.sh增加执行权限,然后直接利用shebang的能力用bash执行,如下图所示:
3. 创建集群管理账户
为了方便集群管理,通常使用统一的用户名管理集群。这个账号在所有的集群中都需要保持命名一致。比如这个集群账号的名字就叫作jmeter。
接下来我们探索一下如何创建这个账户jmeter,如下图所示:
上面我们创建了jmeter账号,然后把jmeter加入root分组。这样jmeter就有了成为root的能力,并且设置jmeter用户的初始化shell是bash,如下图所示:
接下来,我们编辑一下/etc/sudoers文件,增加一行jmeter ALL=(ALL) NOPASSWD:ALL表示jmeter账号sudo时可以免去密码输入环节。
我们可以把上面的完整过程整理成指令文件,create_jmeter.sh:
sudo useradd -m -d /home/jmeter jmeter
sudo passwd jmeter
sudo usermod -G root jmeter
sudo usermod --shell /bin/bash jmeter
sudo cp ~/.bashrc /home/jmeter/
sudo chown jmeter.jmeter /home/jmeter/.bashrc
sudo sh -c 'echo "jmeter ALL=(ALL) NOPASSWD:ALL">>/etc/sudoers'
可以删除用户jmeter,并清理/etc/sudoers文件最后一行。用指令userdel jmeter删除账户,然后执行create_jmeter.sh重新创建回jmeter账户。如果发现结果一致,就代表create_jmeter.sh功能没有问题啦。
最后我们想在另外2台上都执行create_jmeter.sh这个脚本。但是如果是让程序在成百上千台机器上使用,因此还需要一个脚本将create_jmeter.sh拷贝到需要执行的机器上去。
这里,可以对foreach.sh稍做修改,然后分发create_jmeter.sh文件哈。
#!/usr/bin/bash
readarray -t ips < iplist
for ip in ${ips[@]}
doscp ~/remote/create_jmeter.sh root@$ip:~/create_jmeter.sh
done
这里,我们在循环中用scp进行文件拷贝,然后分别去每台机器上执行create_jmeter.sh。
如果你的机器非常多,上述过程还是非常烦琐。
4. 打通集群权限
接下来我们需要打通从210到211和212的权限。利用主服务器的公钥在各个服务器间登录,避免输入密码。接下来我们聊聊怎么搞哈:
首先需要在210上用ssh-keygen生成一个公私钥对,然后把公钥写入需要管理的每一台机器的authorized_keys文件中。如下图所示:我们使用ssh-keygen在主服务器210中生成公私钥对。
然后使用mkdir -p创建/.ssh目录,-p的优势是当目录不存在时,才需要创建,且不会报错。代表当前家目录。 如果文件和目录名前面带有一个.,就代表该文件或目录是一个需要隐藏的文件。平时用ls的时候,并不会查看到该文件,通常这种文件拥有特别的含义,比如~/.ssh目录下是对ssh的配置。
我们用cd切换到.ssh目录,然后执行ssh-keygen(三次回车)。这样会在~/.ssh目录中生成两个文件,id_rsa.pub公钥文件和is_rsa私钥文件。 如下图所示:
看到id_rsa.pub文件中是加密的字符串,我们可以把这些字符串拷贝到其他机器对应用户的~/.ssh/authorized_keys文件中,当ssh登录其他机器的时候,就不用重新输入密码了。 这个免密公钥的能力,这里可以同样用一个shell脚本执行哈。
我们修改一下foreach.sh,并写一个transfer_key.sh配合foreach.sh的工作。transfer_key.sh内容如下:
foreach.sh
#!/usr/bin/bash
readarray -t ips < iplist
for ip in ${ips[@]}
dosh ./transfer_key.sh $ip
done
tranfer_key.sh
ip=$1
pubkey=$(cat ~/.ssh/id_rsa.pub)
echo "execute on .. $ip"
ssh jmeter@$ip "
mkdir -p ~/.ssh
echo $pubkey >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
"
在foreach.sh中我们执行transfer_key.sh,并且将IP地址通过参数传递过去。在 transfer_key.sh中,用$1读出IP地址参数, 再将公钥写入变量pubkey,然后登录到对应的服务器,执行多行指令。用mkdir指令检查.ssh目录,如不存在就创建这个目录。最后我们将公钥追加写入目标机器的~/.ssh/authorized_keys中。
chmod 700和chmod 600是因为某些特定的linux版本需要.ssh的目录为可读写执行,authorized_keys文件的权限为只可读写。而为了保证安全性,组用户、所有用户都不可以访问这个文件。
我们执行foreach.sh还是需要输入两次密码哈。完成上述操作后,我们再登录这两台服务器就不需要输入密码啦。
接下来,我们试试免密登录:
完美~,我们登录任何一台机器,都不再需要输入用户名和密码。
5. 单机安装 Java 环境
在远程部署Java环境之前,我们先单机完成以下Java环境的安装,用来收集需要执行的脚本。
安装java环境可以直接用apt下载对应版本的jdk哈。
然后执行下面的脚本确认java安装。
which java
java --version
如果是产线最好还是根据最小权限原则,执行Java程序我们考虑再创建一个用户ujava。
sudo useradd -m -d /opt/ujava ujava
sudo usermod --shell /bin/bash jmeter
这个创建用户可以不设置密码,因为我们不会真的登录到这个用户下去做任何事情。接下来我们为用户配置Java环境变量。
export JAVA_HOME=/usr/local/java/jdk-11.0.26/bin/java
Linux的环境变量就好比全局可见的数据,这里我们使用export设置哈。 JAVA_HOME环境变量的指向。如果你想看所有的环境变量的指向,可以使用env指令。
其中有一个环境变量比较重要,就是PATH。
如上图,我们可以使用shell查看PATH的值,PATH中用:分割,每一个目录都是linux查找执行文件的目录。当用户在命令行输入一个命令,Linux 就会在PATH中寻找对应的执行文件。
我们不希望JAVA_HOME配置后重启一次电脑就消失,所以可以把这个环境变量加入ujava用户的profile中。这样只要发生用户登录,就有这个环境变量。
sudo sh -c 'echo "export JAVA_HOME=/usr/local/java/jdk-11.0.26/" >> /opt/ujava/.bash_profile'
将JAVA_HOME加入bash_profile,这样后续远程执行java指令时就可以使用JAVA_HOME环境变量了。
最后,我们再将上面所有的指令整理起来,形成一个install_java.sh。
sudo apt -y install jdk-11.0.26
sudo useradd -m -d /opt/ujava ujava
sudo usermod --shell /bin/bash ujava
sudo sh -c 'echo "export JAVA_HOME=/usr/local/java/jdk-11.0.26/" >> /opt/ujava/.bash_profile'
apt后面增了一个-y是为了让执行过程不弹出确认提示了。
6. 远程安装 Java 环境
最后到了远程安装Java环境这一步,我们又需要用到foreach.sh。为了避免每次修改,你可以考虑允许foreach.sh带一个文件参数,指定需要远程执行的脚本。
foreach.sh
#!/usr/bin/bash
readarray -t ips < iplist
script=$1
for ip in ${ips[@]}
dossh $ip 'bash -s' < $script
done
改写后的foreach会读取第一个执行参数作为远程执行的脚本文件。 而bash -s会提示使用标准输入流作为命令的输入;< $script负责将脚本文件内容重定向到远程bash的标准输入流。
然后我们执行foreach.sh install_java.sh,机器等待几分钟左右,在执行结束后,可以用下面这个脚本检测两个机器中的安装情况。
check.sh
sudo -u ujava -i /bin/bash -c 'echo $JAVA_HOME'
sudo -u ujava -i java --version
check.sh中我们切换到ujava用户去检查JAVA_HOME环境变量和Java版本。
7. 总结
这次我们聊的东西算是运维层面的一点皮毛吧。用脚本文件构建一个又一个小工具的过程中,可以发现复用很重要。在工作中,优秀的性能工程师是擅于积累和复用哈,如果你第一次安装java环境,可以把今天的安装脚本保存在自己的小本本中,以后就能自动化完成了。其实工具和脚本并没有那么重要,只是一种手段罢了,除了积累和总结,更重要的就是我们思维,擅于自己去查资料,自己解决问题的能力。