• 全网最新~使用Shell脚本搭建双机高可用的Lustre集群


    1.环境说明:

    主机名系统挂载情况网卡ens33,Lnet的IP地址Lustre集群名内存
    mds005Centos7.9(共享磁盘)1个mgs,1个MDT,2个OST192.168.10.25/209.21global2G
    mds006Centos7.9(共享磁盘)1个mgs,1个MDT,2个OST192.168.10.26/209.22global2G
    managerCentos7.9192.168.10.52G

    注意:自动化脚本lustre_auto.sh在manager节点上,五块4G共享磁盘(/dev/sdb,/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf),两个网卡(管理IP地址,Lnet通信IP地址)

    2.变量定义:

    1. # ens33上的IP地址,即集群管理IP地址
    2. host_address=(192.168.10.25 192.168.10.26)      
    3. # ens38上的IP地址(其他也可以,两个节点网卡名一样就行),即Lnet通信IP地址
    4. lnet_address=(192.168.209.25 192.168.209.26)    
    5. # 集群节点的主机名和域名
    6. host_hostname=(mds005 mds006)
    7. # ssh连接密码,集群认证密码
    8. host_passwd=110119

    3.安装expect命令:

    1. # 安装expect命令
    2. expect -v &> /dev/null
    3. if [ `echo $?` -ne 0 ];then
    4. echo "没有expect,安装expect命令"
    5. yum install -y expect
    6. fi

    4.配置免密登录:

    1. # 配置免密登录
    2. echo "########################## 本地开始配置ssh ##########################"
    3. if [ `test -a ~/.ssh/id_rsa.pub;echo $?` == 0 ];then
    4. echo "ssh公钥已创建"
    5. else
    6. echo "ssh公钥未创建,开始创建"
    7. /usr/bin/expect << eof
    8. # 设置捕获字符串后,期待回复的超时时间
    9. set timeout 10
    10. spawn ssh-keygen -t rsa -b 1024
    11. ## 开始进连续捕获
    12. expect {
    13. "connecting (yes/no)?" { send "yes\n"; exp_continue }
    14. "s password:" { send "${host_passwd}\n"; exp_continue }
    15. ".ssh/id_rsa)" { send "\n"; exp_continue }
    16. "Overwrite (y/n)?" { send "y\n"; exp_continue }
    17. "no passphrase):" { send "\n"; exp_continue }
    18. "passphrase again:" { send "\n"; exp_continue }
    19. }
    20. eof
    21. fi
    22. # 本地的密钥开始加入被控制主机
    23. for ((j=0;j<2;j++));do
    24. echo "########################## ${host_address[j]}正在被添加公钥 ##########################"
    25. /usr/bin/expect << eof
    26. # 设置捕获字符串后,期待回复的超时时间
    27. set timeout 10
    28. spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@${host_address[j]}
    29. ## 开始进连续捕获
    30. expect {
    31. "connecting (yes/no)?" { send "yes\n"; exp_continue }
    32. "s password:" { send "${host_passwd}\n"; exp_continue }
    33. }
    34. eof
    35. echo "############# ${host_address[j]}配置完毕 #############"
    36. done
    37. # 被控制主机开始创建密钥
    38. for ((j=0;j<2;j++));do
    39. echo "########################## ${host_address[j]}开始创建密钥 ##########################"
    40. if [ `ssh root@${host_address[j]} 'test -a ~/.ssh/id_rsa.pub;echo $?'` == 0 ];then
    41. echo "ssh公钥已创建"
    42. else
    43. echo "ssh公钥未创建,开始创建"
    44. /usr/bin/expect << eof
    45. # 设置捕获字符串后,期待回复的超时时间
    46. set timeout 10
    47. spawn ssh root@${host_address[j]} "ssh-keygen -t rsa -b 1024"
    48. ## 开始进连续捕获
    49. expect {
    50. "connecting (yes/no)?" { send "yes\n"; exp_continue }
    51. "s password:" { send "${host_passwd}\n"; exp_continue }
    52. ".ssh/id_rsa)" { send "\n"; exp_continue }
    53. "Overwrite (y/n)?" { send "y\n"; exp_continue }
    54. "no passphrase):" { send "\n"; exp_continue }
    55. "passphrase again:" { send "\n"; exp_continue }
    56. }
    57. eof
    58. fi
    59. echo "############# ${host_address[j]}配置完毕 #############"
    60. done
    61. # 被控制主机开始分配密钥
    62. for ((j=0;j<2;j++));do
    63. for((k=0;k<2;k++));do
    64. echo "########################## ${host_address[j]}开始分配公钥给${host_address[k]} ##########################"
    65. /usr/bin/expect << eof
    66. # 设置捕获字符串后,期待回复的超时时间
    67. set timeout 10
    68. spawn ssh -t root@${host_address[j]} "ssh root@${host_address[k]}"
    69. spawn ssh -t root@${host_address[j]} "ssh-copy-id -i /root/.ssh/id_rsa.pub root@${host_address[k]}"
    70. ## 开始进连续捕获
    71. expect {
    72. "connecting (yes/no)?" { send "yes\n"; exp_continue }
    73. "s password:" { send "${host_passwd}\n"; exp_continue }
    74. }
    75. eof
    76. echo "############# ${host_address[j]}配置完毕 #############"
    77. done
    78. done

    5.主机名域名映射:

    1. for ((i=0;i<100;i++));do
    2. read -p "修改主机名和配置域名映射?(Y/n): " flag
    3. if [ "${flag}" == "Y" ];then
    4. sleep 3
    5. echo "########################## 开始配置主机名和域名映射 ##########################"
    6. for ((j=0;j<2;j++));do
    7. if [ `ssh root@${host_address[j]} "hostname"` != "${host_hostname[j]}" ];then
    8. ssh root@${host_address[j]} "hostnamectl set-hostname ${host_hostname[j]}"
    9. fi
    10. ssh root@${host_address[j]} "cat << eof > /etc/hosts
    11. 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
    12. ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
    13. eof"
    14. for ((k=0;k<2;k++));do
    15. ssh root@${host_address[j]} "echo '${host_address[k]} ${host_hostname[k]}' >> /etc/hosts"
    16. done
    17. done
    18. echo "############# 配置完毕 #############"
    19. break
    20. elif [ "${flag}" == "n" ];then
    21. echo "############# 已跳过步骤-修改主机名和配置域名映射 #############"
    22. break
    23. elif [ ${i} -eq 99 ];then
    24. echo "############# 已退出 #############"
    25. exit
    26. else continue;fi
    27. done
    28. # 测试被控主机ssh连接
    29. for ((j=0;j<2;j++));do
    30. echo "########################## ${host_address[j]}开始测试 ##########################"
    31. for ((k=0;k<2;k++));do
    32. /usr/bin/expect << eof
    33. # 设置捕获字符串后,期待回复的超时时间
    34. set timeout 10
    35. spawn ssh -t root@${host_address[j]} "ssh root@${host_address[k]} 'exit'"
    36. ## 开始进连续捕获
    37. expect {
    38. "connecting (yes/no)?" { send "yes\n"; exp_continue }
    39. }
    40. eof
    41. if [ `echo $?` != 0 ];then
    42. echo "${host_hostname[j]}主机无法免密登录${host_hostname[k]}"
    43. exit
    44. fi
    45. done
    46. echo "############# ${host_address[j]}测试完毕 #############"
    47. done

    6.配置防火墙和selinux:

    1. # 配置防火墙和selinux
    2. for ((i=0;i<100;i++));do
    3. read -p "配置防火墙和selinux?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. echo "########################## 开始配置防火墙和selinux ##########################"
    7. for ((j=0;j<2;j++));do
    8. ssh root@${host_address[j]} "systemctl stop firewalld;systemctl disable firewalld"
    9. ssh root@${host_address[j]} "sed -i 's/SELINUX=.*/SELINUX=disabled/' /etc/selinux/config"
    10. done
    11. echo "############# 配置完毕 #############"
    12. break
    13. elif [ "${flag}" == "n" ];then
    14. echo "############# 已跳过步骤-配置防火墙和selinux #############"
    15. break
    16. elif [ ${i} -eq 99 ];then
    17. echo "############# 已退出 #############"
    18. exit
    19. else continue;fi
    20. done

    7.配置yum源:

    1. # 配置yum源
    2. for ((i=0;i<100;i++));do
    3. read -p "配置yum源?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. echo "########################## 开始配置yum源 ##########################"
    7. for ((j=0;j<2;j++));do
    8. echo "########################## 配置${host_address[j]}的本地yum源 ##########################"
    9. ssh root@${host_address[j]} "mkdir /mnt/cdrom &> /dev/null;mount /dev/cdrom /mnt/cdrom"
    10. if [ -z "`ssh root@${host_address[j]} "grep '^\/dev\/cdrom' /etc/fstab"`" ];then
    11. ssh root@${host_address[j]} "cat << eof >> /etc/fstab
    12. /dev/cdrom /mnt/cdrom iso9660 defaults  0  0
    13. eof"
    14. fi
    15. ssh root@${host_address[j]} "cat << eof > /etc/yum.repos.d/centos-local.repo
    16. [centos7.9]
    17. name=centos7.9
    18. baseurl=file:///mnt/cdrom
    19. enabled=1
    20. gpgcheck=0
    21. eof"
    22. echo "############# ${host_address[j]}配置完毕 #############"
    23. echo "########################## 配置${host_address[j]}的扩展源 ##########################"
    24. ssh root@${host_address[j]} "yum install epel-release -y"
    25. echo "############# ${host_address[j]}配置完毕 #############"
    26. echo "########################## 配置${host_address[j]}的阿里yum源 ##########################"
    27. ssh root@${host_address[j]} "yum install -y wget"
    28. if [ `ssh root@${host_address[j]} 'test -a /etc/yum.repos.d/CentOS-Base.repo;echo $?'` == 0 ];then
    29. ssh root@${host_address[j]} "wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo"
    30. fi
    31. ssh root@${host_address[j]} "yum clean all && yum repolist"
    32. echo "############# ${host_address[j]}配置完毕 #############"
    33. if [ `ssh root@${host_address[j]} "echo $?"` != 0 ];then
    34. echo "yum源配置有误,退出执行脚本"
    35. exit
    36. fi
    37. done
    38. break
    39. elif [ "${flag}" == "n" ];then
    40. echo "############# 已跳过步骤-配置yum源 #############"
    41. break
    42. elif [ ${i} -eq 99 ];then
    43. echo "############# 已退出 #############"
    44. exit
    45. else continue;fi
    46. done

    8.配置chrony时间服务器:

    1. # 配置chrony时间服务器
    2. for ((i=0;i<100;i++));do
    3. read -p "配置chrony时间服务器?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. echo "########################## 开始配置chrony ##########################"
    7. for ((j=0;j<2;j++));do
    8. if [ `ssh root@${host_address[j]} "systemctl restart chronyd;echo $?"` != 0 ];then
    9. echo "${host_address[j]} 安装chrony"
    10. ssh root@${host_address[j]} "yum install -y chrony && systemctl restart chronyd"
    11. if [ `echo $?` != 0 ];then
    12. echo "安装失败,请排错!"
    13. exit
    14. fi
    15. fi
    16. echo "${host_address[j]}配置chrony"
    17.   ssh root@${host_address[j]} "sed -i '/^server/d' /etc/chrony.conf"
    18. if [ ${host_address[j]} == ${host_address[0]} ];then
    19. ssh root@${host_address[j]} "sed -i '2a\server '"${host_address[0]}"' iburst\' /etc/chrony.conf"
    20. ssh root@${host_address[j]} "sed -i 's/#allow 192.168.0.0\/16/allow 192.168.10.0\/16/' /etc/chrony.conf"
    21. ssh root@${host_address[j]} "sed -i 's/#local stratum 10/local stratum 10/' /etc/chrony.conf"
    22. sleep 2
    23. else
    24. ssh root@${host_address[j]} "sed -i '2a\server '"${host_address[0]}"' iburst\' /etc/chrony.conf"
    25. fi
    26. ssh root@${host_address[j]} "systemctl restart chronyd && systemctl enable chronyd &> /dev/null"
    27. sleep 5
    28. ssh root@${host_address[j]} "timedatectl set-ntp true && chronyc sources -v | sed -n '/^\^\*/p'"
    29. if [ -z "`ssh root@${host_address[j]} "chronyc sources -v | sed -n '/^\^\*/p'"`" ];then
    30. echo -e "\e[31m此节点${host_address[j]}的chrony配置有误,请手动调试\e[0m"
    31. exit
    32. fi
    33. echo "############# ${host_address[j]}配置完毕 #############"
    34. done
    35. break
    36. elif [ "${flag}" == "n" ];then
    37. echo "############# 已跳过步骤-配置chrony时间服务器 #############"
    38. break
    39. elif [ ${i} -eq 99 ];then
    40. echo "############# 已退出 #############"
    41. exit
    42. else continue;fi
    43. done

    9.安装e2fsprogs:

    1. # 安装e2fsprogs
    2. for ((i=0;i<100;i++));do
    3. read -p "安装e2fsprogs?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. echo "########################## 开始安装e2fsprogs ##########################"
    7. for ((j=0;j<2;j++));do
    8. echo "########################## ${host_address[j]}开始安装e2fsprogs ##########################"
    9. ssh root@${host_address[j]} "rm -rf ~/e2fsprogs1.44.5 && mkdir ~/e2fsprogs1.44.5"
    10. ssh root@${host_address[j]} "wget -c -r -nd https://downloads.whamcloud.com/public/e2fsprogs/1.44.5.wc1/el7/RPMS/x86_64/ -P ~/e2fsprogs1.44.5"
    11. ssh root@${host_address[j]} "rm -rf ~/e2fsprogs1.44.5/index.html* ~/e2fsprogs1.44.5/unknown.gif ~/e2fsprogs1.44.5/*.gif ~/e2fsprogs1.44.5/sha256sum"
    12. ssh root@${host_address[j]} "rpm -Uvh ~/e2fsprogs1.44.5/* --force"
    13. ssh root@${host_address[j]} "rpm -qa | grep e2fsprogs"
    14. if [ `echo $?` != 0 ];then
    15. echo "安装失败,请排错!"
    16. exit
    17. fi
    18. echo "############# ${host_address[j]}配置完毕 #############"
    19. done
    20. break
    21. elif [ "${flag}" == "n" ];then
    22. echo "############# 已跳过步骤-安装e2fsprogs #############"
    23. break
    24. elif [ ${i} -eq 99 ];then
    25. echo "############# 已退出 #############"
    26. exit
    27. else continue;fi
    28. done

    10.安装lustre软件:

    1. for ((i=0;i<100;i++));do
    2. read -p "安装Lustre软件?(Y/n): " flag
    3. if [ "${flag}" == "Y" ];then
    4. sleep 3
    5. echo "########################## 开始安装lustre ##########################"
    6. for ((j=0;j<2;j++));do
    7. echo "########################## ${host_address[j]}开始安装lustre ##########################"
    8. ssh root@${host_address[j]} "yum install -y linux-firmware dracut selinux-policy-targeted kexec-tools libyaml perl"
    9. ssh root@${host_address[j]} "rm -rf ~/lustre2.12.1 && mkdir ~/lustre2.12.1"
    10. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/kernel-3.10.0-957.10.1.el7_lustre.x86_64.rpm -P ~/lustre2.12.1"
    11. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/kmod-lustre-2.12.1-1.el7.x86_64.rpm -P ~/lustre2.12.1"
    12. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/kmod-lustre-osd-ldiskfs-2.12.1-1.el7.x86_64.rpm -P ~/lustre2.12.1"
    13. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/lustre-2.12.1-1.el7.x86_64.rpm -P ~/lustre2.12.1"
    14. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/lustre-osd-ldiskfs-mount-2.12.1-1.el7.x86_64.rpm -P ~/lustre2.12.1"
    15. ssh root@${host_address[j]} "rpm -Uvh ~/lustre2.12.1/*.rpm --force"
    16. ssh root@${host_address[j]} "rpm -qa | grep lustre"
    17. if [ `echo $?` != 0 ];then
    18. echo "安装失败,请排错!"
    19. exit
    20. fi
    21. echo "############# ${host_address[j]}配置完毕 #############"
    22. done
    23. break
    24. elif [ "${flag}" == "n" ];then
    25. echo "############# 已跳过步骤-安装lustre #############"
    26. break
    27. elif [ ${i} -eq 99 ];then
    28. echo "############# 已退出 #############"
    29. exit
    30. else continue;fi
    31. done

    11.控制重启和检测重启:

    1. for ((i=0;i<100;i++));do
    2. read -p "是否重启集群主机(只有重启kernel内核才能更换生效)?(Y/n): " flag
    3. if [ "${flag}" == "Y" ];then
    4. sleep 3
    5. for((j=0;j<2;j++));do
    6. ssh root@${host_address[j]} "reboot"
    7. continue
    8. done
    9. for ((k=0;k<100;k++));do
    10. if [ ${k} -eq 99 ];then
    11. echo "############# 设备连接超时.... #############"
    12. exit
    13. fi
    14. if [ `ssh root@${host_address[0]} -o ConnectTimeout=5 "exit";echo $?` == 0 -a `ssh root@${host_address[1]} -o ConnectTimeout=5 "exit";echo $?` == 0 ];then
    15. echo "############# 设备已重启 #############"
    16. break
    17. else
    18. echo "############# 设备正在重启 #############"
    19. fi
    20. done
    21. break
    22. elif [ "${flag}" == "n" ];then
    23. echo "############# 已跳过步骤-重启 #############"
    24. break
    25. elif [ ${i} -eq 99 ];then
    26. echo "############# 已退出 #############"
    27. exit
    28. else continue;fi
    29. done

    12.加载模块:

    1. # 检查lustre
    2. for((i=0;i<2;i++));do
    3. echo "########################## ${host_address[i]}加载Lustre模块,查看Lustre版本 ##########################"
    4. ssh root@${host_address[i]} "modprobe lustre && lsmod | grep lustre"
    5. ssh root@${host_address[i]} "modinfo lustre"
    6. echo "############# ${host_address[i]}配置完毕 #############"
    7. done

    13.配置和Lustre网络:

    1. # 配置Lnet网卡的IP地址和Lustre网络
    2. for ((i=0;i<100;i++));do
    3. read -p "是否继续配置Lnet网卡的IP地址?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. for((j=0;j<2;j++));do
    7. echo "########################## ${host_address[j]}配置Lnet网卡的IP地址 ##########################"
    8. read -p "请根据以上输出显示中,输入你要配置的LnNet网卡名称:" network_card
    9. if [ -z "`ssh root@${host_address[j]} "ip addr | grep -o ${network_card}"`" ];then
    10. echo "网卡不存在,请重试"
    11. exit
    12. fi
    13. ssh root@${host_address[j]} "nmcli connection delete ${network_card} &> /dev/null"
    14. ssh root@${host_address[j]} "nmcli connection add type ethernet con-name ${network_card} ifname ${network_card} ipv4.method manual ipv4.addresses '${lnet_address[j]}/24' autoconnect yes"
    15. ssh root@${host_address[j]} "nmcli connection up ${network_card}"
    16. echo "############# ${host_address[j]}配置完毕 #############"
    17. echo "########################## ${host_address[j]}配置Lnet网络 ##########################"
    18. ssh root@${host_address[j]} "echo options lnet networks='tcp(ens33),tcp2(${network_card})' > /etc/modprobe.d/lustre.conf"
    19. ssh root@${host_address[j]} "lustre_rmmod && modprobe -v lustre"
    20. echo "############# ${host_address[j]}配置完毕 #############"
    21. echo "############# ${host_address[j]}查看Lnet网络 #############"
    22. ssh root@${host_address[j]} "lctl list_nids"
    23. echo "##########################################################"
    24. done
    25. break
    26. elif [ "${flag}" == "n" ];then
    27. echo "############# 已跳过步骤-配置Lnet网卡的IP地址 #############"
    28. break
    29. elif [ ${i} -eq 99 ];then
    30. echo "############# 已退出 #############"
    31. exit
    32. else continue;fi
    33. done

    14.格式化lustre:

    1. # 格式化lustre
    2. for ((i=0;i<100;i++));do
    3. read -p "是否继续格式化lustre?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. echo "########################## 开始格式化lustre ##########################"
    7. ssh root@${host_address[0]} "lsblk"
    8. ssh root@${host_address[0]} "mkfs.lustre --mgs --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sdb"
    9. ssh root@${host_address[0]} "mkfs.lustre --fsname global --mdt --index=0 --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --mgsnode=${lnet_address[0]}@tcp2 --mgsnode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sdc"
    10. ssh root@${host_address[0]} "mkfs.lustre --fsname global --mdt --index=1 --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --mgsnode=${lnet_address[0]}@tcp2 --mgsnode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sdd"
    11. ssh root@${host_address[0]} "mkfs.lustre --fsname global --ost --index=0 --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --mgsnode=${lnet_address[0]}@tcp2 --mgsnode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sde"
    12. ssh root@${host_address[0]} "mkfs.lustre --fsname global --ost --index=1 --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --mgsnode=${lnet_address[0]}@tcp2 --mgsnode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sdf"
    13. echo "############# 格式化完毕 #############"
    14. echo "########################## 创建挂载点目录 ##########################"
    15. for((j=0;j<2;j++));do
    16. ssh root@${host_address[j]} "mkdir /mnt/mgs &> /dev/null;mkdir /mnt/mdt1 &> /dev/null;mkdir /mnt/mdt2 &> /dev/null;mkdir /mnt/ost1 &> /dev/null;mkdir /mnt/ost2 &> /dev/null"
    17. done
    18. echo "############# 创建完毕 #############"
    19. echo "############# ${host_address[1]}查看格式化 #############"
    20. ssh root@${host_address[1]} "blkid /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf"
    21. echo "##########################################################"
    22. break
    23. elif [ "${flag}" == "n" ];then
    24. echo "############# 已跳过步骤-格式化lustre #############"
    25. break
    26. elif [ ${i} -eq 99 ];then
    27. echo "############# 已退出 #############"
    28. exit
    29. else continue;fi
    30. done

    15.测试lustre挂载:

    1. # 测试挂载
    2. for ((i=0;i<100;i++));do
    3. read -p "是否继续测试lustre的挂载?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. for((j=0;j<2;j++));do
    7. echo "########################## ${host_address[j]}测试挂载 ##########################"
    8. echo "########################## mgs测试挂载 ##########################"
    9. ssh root@${host_address[j]} "umount /mnt/mgs &> /dev/null"
    10. ssh root@${host_address[j]} "mount -t lustre /dev/sdb /mnt/mgs"
    11. if [ `echo $?` != 0 ];then
    12. echo "############# mgs测试有误,请手动查看! #############"
    13. exit
    14. fi
    15. echo "########################## mdt1测试挂载 ##########################"
    16. ssh root@${host_address[j]} "umount /mnt/mdt1 &> /dev/null"
    17. ssh root@${host_address[j]} "mount -t lustre /dev/sdc /mnt/mdt1"
    18. if [ `echo $?` != 0 ];then
    19. echo "############# mdt1测试有误,请手动查看! #############"
    20. ssh root@${host_address[j]} "umount /mnt/mgs"
    21. exit
    22. fi
    23. echo "########################## mdt2测试挂载 ##########################"
    24. ssh root@${host_address[j]} "umount /mnt/mdt2 &> /dev/null"
    25. ssh root@${host_address[j]} "mount -t lustre /dev/sdd /mnt/mdt2"
    26. if [ `echo $?` != 0 ];then
    27. echo "############# mdt2测试有误,请手动查看! #############"
    28. ssh root@${host_address[j]} "umount /mnt/mdt1;umount /mnt/mgs"
    29. exit
    30. fi
    31. echo "########################## ost1测试挂载 ##########################"
    32. ssh root@${host_address[j]} "umount /mnt/ost1 &> /dev/null"
    33. ssh root@${host_address[j]} "mount -t lustre /dev/sde /mnt/ost1"
    34. if [ `echo $?` != 0 ];then
    35. echo "############# ost1测试有误,请手动查看! #############"
    36. ssh root@${host_address[j]} "umount /mnt/mdt2;umount /mnt/mdt1;umount /mnt/mgs"
    37. exit
    38. fi
    39. echo "########################## ost2测试挂载 ##########################"
    40. ssh root@${host_address[j]} "umount /mnt/ost2 &> /dev/null"
    41. ssh root@${host_address[j]} "mount -t lustre /dev/sdf /mnt/ost2"
    42. if [ `echo $?` != 0 ];then
    43. echo "############# ost2测试有误,请手动查看! #############"
    44. ssh root@${host_address[j]} "umount /mnt/ost1;umount /mnt/mdt2;umount /mnt/mdt1;umount /mnt/mgs"
    45. exit
    46. fi
    47. ssh root@${host_address[j]} "umount /mnt/ost2;umount /mnt/ost1;umount /mnt/mdt2;umount /mnt/mdt1;umount /mnt/mgs"
    48. done
    49. echo "############# 测试完毕 #############"
    50. break
    51. elif [ "${flag}" == "n" ];then
    52. echo "############# 已跳过步骤-测试挂载 #############"
    53. break
    54. elif [ ${i} -eq 99 ];then
    55. echo "############# 已退出 #############"
    56. exit
    57. else continue;fi
    58. done

    16.安装高可用和创建集群:

    1. # 安装packemaker和corosync软件和创建集群
    2. for ((i=0;i<100;i++));do
    3. read -p "是否继续安装packemaker和corosync软件和创建集群?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. if [ -z "`ssh root@${host_address[0]} 'pcs status' | grep mycluster`" ];then
    7. for((j=0;j<2;j++));do
    8. echo "########################## ${host_address[j]}开始安装 ##########################"
    9. ssh root@${host_address[j]} "yum install pacemaker pcs policycoreutils-python -y"
    10. echo "############# ${host_address[j]}安装完毕 #############"
    11. echo "########################## ${host_address[j]}开始配置 ##########################"
    12. ssh root@${host_address[j]} "systemctl enable pcsd;systemctl restart pcsd"
    13. ssh root@${host_address[j]} "echo '${host_passwd}' |passwd --stdin hacluster"
    14. echo "############# ${host_address[j]}配置完毕 #############"
    15. done
    16. /usr/bin/expect << eof
    17. # 设置捕获字符串后,期待回复的超时时间
    18. set timeout 10
    19. spawn ssh root@${host_address[0]} "pcs cluster auth ${host_hostname[*]}"
    20. ## 开始进连续捕获
    21. expect {
    22.        "Username:"         { send "hacluster\n"; exp_continue }
    23.        "Password:"         { send "${host_passwd}\n"; exp_continue }
    24. }
    25. eof
    26. echo "########################## 开始创建集群 ##########################"
    27. ssh root@${host_address[0]} "pcs cluster setup --name mylustre ${host_hostname[*]}"
    28. echo "############# 创建完毕 #############"
    29. fi
    30. echo "########################## 启动集群 ##########################"
    31. ssh root@${host_address[0]} "pcs cluster start --all"
    32. echo "############# 启动完毕 #############"
    33. break
    34. elif [ "${flag}" == "n" ];then
    35. echo "############# 已跳过步骤-安装packemaker和corosync软件和配置 #############"
    36. break
    37. elif [ ${i} -eq 99 ];then
    38. echo "############# 已退出 #############"
    39. exit
    40. else continue;fi
    41. done

    17.配置资源防护:

    1. # 配置资源防护
    2. for ((i=0;i<100;i++));do
    3. read -p "是否继续配置资源防护?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. echo "########################## 开始配置资源防护 ##########################"
    7. for((j=0;j<2;j++));do
    8. ssh root@${host_address[j]} "yum install -y fence-agents-all"
    9. done
    10. ssh root@${host_address[0]} "pcs property set stonith-enabled=true"
    11. if [ `ssh root@${host_address[j]} "pcs status" | grep "stonith:fence_heuristics_ping" | grep -c "Started"` -eq 2 ];then
    12. echo "############# stonith已创建,并且正常运行,跳过配置stonith #############"
    13. break
    14. fi
    15. if [ `ssh root@${host_address[j]} "pcs status" | grep "stonith:fence_heuristics_ping"` -eq 2 ];then
    16. ssh root@${host_address[0]} "pcs stonith delete stonith-ping-${host_hostname[0]}"
    17. ssh root@${host_address[0]} "pcs stonith delete stonith-ping-${host_hostname[0]}"
    18. fi
    19. ssh root@${host_address[0]} "pcs stonith create stonith-ping-${host_hostname[0]} fence_heuristics_ping ping_targets=${host_address[0]}"
    20. ssh root@${host_address[0]} "pcs stonith create stonith-ping-${host_hostname[1]} fence_heuristics_ping ping_targets=${host_address[1]}"
    21. ssh root@${host_address[0]} "pcs status"
    22. echo "############# 配置完毕 #############"
    23. break
    24. elif [ "${flag}" == "n" ];then
    25. echo "############# 已跳过步骤-配置资源防护 #############"
    26. break
    27. elif [ ${i} -eq 99 ];then
    28. echo "############# 已退出 #############"
    29. exit
    30. else continue;fi
    31. done

    18.创建lustre资源:

    1. # 创建lustre资源
    2. for ((i=0;i<100;i++));do
    3. read -p "是否继续创建lustre资源(可删除后再创建)?(Y/n): " flag
    4. if [ "${flag}" == "Y" ];then
    5. sleep 3
    6. for((j=0;j<2;j++));do
    7. if [ -z `ssh root@${host_address[j]} 'rpm -qa | grep lustre-resource-agents-2.12.1-1'` ];then
    8. echo "########################## ${host_address[j]}安装ocf:lustre:Lustre包 ##########################"
    9. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/lustre-resource-agents-2.12.1-1.el7.x86_64.rpm"
    10. ssh root@${host_address[j]} "rpm -ivh lustre-resource-agents-2.12.1-1.el7.x86_64.rpm"
    11. echo "############# 安装完毕 #############"
    12. fi
    13. done
    14. echo "########################## 开始删除lustre资源 ##########################"
    15. ssh root@${host_address[0]} "pcs resource delete global-ost2 &> /dev/null;pcs resource delete global-ost1 &> /dev/null"
    16. ssh root@${host_address[0]} "pcs resource delete global-mdt1 &> /dev/null;pcs resource delete global-mdt2 &> /dev/null"
    17. ssh root@${host_address[0]} "pcs resource delete global-mgs &> /dev/null"
    18. echo "############# 删除完毕 #############"
    19. echo "########################## 开始创建lustre资源 ##########################"
    20. ssh root@${host_address[0]} "pcs resource create global-mgs ocf:lustre:Lustre target=/dev/sdb mountpoint=/mnt/mgs"
    21. if [ `echo $?` != 0 ];then
    22. echo "############# mgs资源创建有误,请手动查看! #############"
    23. exit
    24. fi
    25. ssh root@${host_address[0]} "pcs resource create global-mdt1 ocf:lustre:Lustre target=/dev/sdc mountpoint=/mnt/mdt1"
    26. if [ `echo $?` != 0 ];then
    27. echo "############# mdt1资源创建有误,请手动查看! #############"
    28. exit
    29. fi
    30. ssh root@${host_address[0]} "pcs resource create global-mdt2 ocf:lustre:Lustre target=/dev/sdd mountpoint=/mnt/mdt2"
    31. if [ `echo $?` != 0 ];then
    32. echo "############# mdt2资源创建有误,请手动查看! #############"
    33. exit
    34. fi
    35. ssh root@${host_address[0]} "pcs resource create global-ost1 ocf:lustre:Lustre target=/dev/sde mountpoint=/mnt/ost1"
    36. if [ `echo $?` != 0 ];then
    37. echo "############# ost1资源创建有误,请手动查看! #############"
    38. exit
    39. fi
    40. ssh root@${host_address[0]} "pcs resource create global-ost2 ocf:lustre:Lustre target=/dev/sdf mountpoint=/mnt/ost2"
    41. if [ `echo $?` != 0 ];then
    42. echo "############# ost2资源创建有误,请手动查看! #############"
    43. exit
    44. fi
    45. ssh root@${host_address[0]} "pcs constraint location add global-constraint-mgs global-mgs ${host_hostname[0]} 10"
    46. ssh root@${host_address[0]} "pcs constraint location add global-constraint-mdt1 global-mdt1 ${host_hostname[0]} 10"
    47. ssh root@${host_address[0]} "pcs constraint location add global-constraint-mdt2 global-mdt2 ${host_hostname[1]} 10"
    48. ssh root@${host_address[0]} "pcs constraint location add global-constraint-ost1 global-ost1 ${host_hostname[0]} 10"
    49. ssh root@${host_address[0]} "pcs constraint location add global-constraint-ost2 global-ost2 ${host_hostname[1]} 10"
    50. ssh root@${host_address[0]} "pcs constraint order start global-mgs then start global-mdt1"
    51. ssh root@${host_address[0]} "pcs constraint order start global-mgs then start global-mdt2"
    52. ssh root@${host_address[0]} "pcs constraint order start global-mgs then start global-ost1"
    53. ssh root@${host_address[0]} "pcs constraint order start global-mgs then start global-ost2"
    54. if [ `echo $?` != 0 ];then
    55. echo "############# 资源约束创建有误,请手动查看! #############"
    56. exit
    57. fi
    58. lnet_name1=`ssh root@${host_address[0]} "awk '{print $3}' /etc/modprobe.d/lustre.conf" | awk -F ',' '{print $2}' | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
    59. lnet_name2=`ssh root@${host_address[1]} "awk '{print $3}' /etc/modprobe.d/lustre.conf" | awk -F ',' '{print $2}' | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
    60. ssh root@${host_address[0]} "pcs resource delete ping-lnet &>/dev/null;pcs resource delete global-healthLUSTRE &> /dev/null"
    61. ssh root@${host_address[0]} "pcs resource create ping-lnet ocf:lustre:healthLNET lctl=true multiplier=1001 device=${lnet_name1} host_list='${lnet_address[0]}@tcp2 ${lnet_address[1]}@tcp2' --clone"
    62. ssh root@${host_address[0]} "pcs resource create global-healthLUSTRE ocf:lustre:healthLUSTRE --clone"
    63. if [ `echo $?` != 0 ];then
    64. echo "############# 资源监听创建有误,请手动查看! #############"
    65. exit
    66. fi
    67. echo "############# 创建完毕 #############"
    68. echo "##########################   查看集群状态   ##########################"
    69. echo "########################################################################"
    70. echo "########################################################################"
    71. ssh root@${host_address[0]} "pcs status"
    72. echo "########################################################################"
    73. echo "########################################################################"
    74. echo "################################################## 脚本到此全部执行完毕 ##################################################"
    75. break
    76. elif [ "${flag}" == "n" ];then
    77. echo "############# 已跳过步骤-创建lustre资源 #############"
    78. break
    79. elif [ ${i} -eq 99 ];then
    80. echo "############# 已退出 #############"
    81. exit
    82. else continue;fi
    83. done

    19.完整脚本:

    1. # 高可用双机lustre集群的的自动化部署脚本,(部署条件:配置了共享磁盘(/dev/sdb,/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf)。并且有两个网卡,其中一个网卡已经配置了IP地址)
    2. host_address=(192.168.10.25 192.168.10.26)
    3. lnet_address=(192.168.209.25 192.168.209.26)
    4. host_hostname=(mds005 mds006)
    5. host_passwd=110119
    6. # 安装expect命令
    7. expect -v &> /dev/null
    8. if [ `echo $?` -ne 0 ];then
    9. echo "没有expect,安装expect命令"
    10. yum install -y expect
    11. fi
    12. # 配置免密登录
    13. echo "########################## 本地开始配置ssh ##########################"
    14. if [ `test -a ~/.ssh/id_rsa.pub;echo $?` == 0 ];then
    15. echo "ssh公钥已创建"
    16. else
    17. echo "ssh公钥未创建,开始创建"
    18. /usr/bin/expect << eof
    19. # 设置捕获字符串后,期待回复的超时时间
    20. set timeout 10
    21. spawn ssh-keygen -t rsa -b 1024
    22. ## 开始进连续捕获
    23. expect {
    24.        "connecting (yes/no)?" { send "yes\n"; exp_continue }
    25.        "s password:"         { send "${host_passwd}\n"; exp_continue }
    26.        ".ssh/id_rsa)"         { send "\n"; exp_continue }
    27.        "Overwrite (y/n)?"     { send "y\n"; exp_continue }
    28.        "no passphrase):"     { send "\n"; exp_continue }
    29.        "passphrase again:"   { send "\n"; exp_continue }
    30. }
    31. eof
    32. fi
    33. # 本地的密钥开始加入被控制主机
    34. for ((j=0;j<2;j++));do
    35. echo "########################## ${host_address[j]}正在被添加公钥 ##########################"
    36. /usr/bin/expect << eof
    37. # 设置捕获字符串后,期待回复的超时时间
    38. set timeout 10
    39. spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@${host_address[j]}
    40. ## 开始进连续捕获
    41. expect {
    42.        "connecting (yes/no)?" { send "yes\n"; exp_continue }
    43.        "s password:"         { send "${host_passwd}\n"; exp_continue }
    44. }
    45. eof
    46. echo "############# ${host_address[j]}配置完毕 #############"
    47. done
    48. # 被控制主机开始创建密钥
    49. for ((j=0;j<2;j++));do
    50. echo "########################## ${host_address[j]}开始创建密钥 ##########################"
    51. if [ `ssh root@${host_address[j]} 'test -a ~/.ssh/id_rsa.pub;echo $?'` == 0 ];then
    52. echo "ssh公钥已创建"
    53. else
    54. echo "ssh公钥未创建,开始创建"
    55. /usr/bin/expect << eof
    56. # 设置捕获字符串后,期待回复的超时时间
    57. set timeout 10
    58. spawn ssh root@${host_address[j]} "ssh-keygen -t rsa -b 1024"
    59. ## 开始进连续捕获
    60. expect {
    61.        "connecting (yes/no)?" { send "yes\n"; exp_continue }
    62.        "s password:"         { send "${host_passwd}\n"; exp_continue }
    63.        ".ssh/id_rsa)"         { send "\n"; exp_continue }
    64.        "Overwrite (y/n)?"     { send "y\n"; exp_continue }
    65.        "no passphrase):"     { send "\n"; exp_continue }
    66.        "passphrase again:"   { send "\n"; exp_continue }
    67. }
    68. eof
    69. fi
    70. echo "############# ${host_address[j]}配置完毕 #############"
    71. done
    72. # 被控制主机开始分配密钥
    73. for ((j=0;j<2;j++));do
    74. echo "########################## ${host_address[j]}开始分配公钥 ##########################"
    75. for ((k=0;k<2;k++));do
    76. /usr/bin/expect << eof
    77. # 设置捕获字符串后,期待回复的超时时间
    78. set timeout 10
    79. spawn ssh root@${host_address[j]} "ssh-copy-id -i /root/.ssh/id_rsa.pub root@${host_address[k]}"
    80. ## 开始进连续捕获
    81. expect {
    82.        "connecting (yes/no)?" { send "yes\n"; exp_continue }
    83.        "s password:"         { send "${host_passwd}\n"; exp_continue }
    84. }
    85. eof
    86. done
    87. echo "############# ${host_address[j]}分配完毕 #############"
    88. done
    89. # 修改主机名和配置域名映射
    90. for ((i=0;i<100;i++));do
    91. read -p "修改主机名和配置域名映射?(Y/n): " flag
    92. if [ "${flag}" == "Y" ];then
    93. sleep 3
    94. echo "########################## 开始配置主机名和域名映射 ##########################"
    95. for ((j=0;j<2;j++));do
    96. if [ `ssh root@${host_address[j]} "hostname"` != "${host_hostname[j]}" ];then
    97. ssh root@${host_address[j]} "hostnamectl set-hostname ${host_hostname[j]}"
    98. fi
    99. ssh root@${host_address[j]} "cat << eof > /etc/hosts
    100. 127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
    101. ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
    102. eof"
    103. for ((k=0;k<2;k++));do
    104. ssh root@${host_address[j]} "echo '${host_address[k]} ${host_hostname[k]}' >> /etc/hosts"
    105. done
    106. done
    107. echo "############# 配置完毕 #############"
    108. break
    109. elif [ "${flag}" == "n" ];then
    110. echo "############# 已跳过步骤-修改主机名和配置域名映射 #############"
    111. break
    112. elif [ ${i} -eq 99 ];then
    113. echo "############# 已退出 #############"
    114. exit
    115. else continue;fi
    116. done
    117. # 配置防火墙和selinux
    118. for ((i=0;i<100;i++));do
    119. read -p "配置防火墙和selinux?(Y/n): " flag
    120. if [ "${flag}" == "Y" ];then
    121. sleep 3
    122. echo "########################## 开始配置防火墙和selinux ##########################"
    123. for ((j=0;j<2;j++));do
    124. ssh root@${host_address[j]} "systemctl stop firewalld;systemctl disable firewalld"
    125. ssh root@${host_address[j]} "sed -i 's/SELINUX=.*/SELINUX=disabled/' /etc/selinux/config"
    126. done
    127. echo "############# 配置完毕 #############"
    128. break
    129. elif [ "${flag}" == "n" ];then
    130. echo "############# 已跳过步骤-配置防火墙和selinux #############"
    131. break
    132. elif [ ${i} -eq 99 ];then
    133. echo "############# 已退出 #############"
    134. exit
    135. else continue;fi
    136. done
    137. # 配置yum源
    138. for ((i=0;i<100;i++));do
    139. read -p "配置yum源?(Y/n): " flag
    140. if [ "${flag}" == "Y" ];then
    141. sleep 3
    142. echo "########################## 开始配置ssh ##########################"
    143. for ((j=0;j<2;j++));do
    144. echo "########################## 配置${host_address[j]}的本地yum源 ##########################"
    145. ssh root@${host_address[j]} "mkdir /mnt/cdrom &> /dev/null;mount /dev/cdrom /mnt/cdrom"
    146. if [ -z "`ssh root@${host_address[j]} "grep '^\/dev\/cdrom' /etc/fstab"`" ];then
    147. ssh root@${host_address[j]} "cat << eof >> /etc/fstab
    148. /dev/cdrom /mnt/cdrom iso9660 defaults  0  0
    149. eof"
    150. fi
    151. ssh root@${host_address[j]} "cat << eof > /etc/yum.repos.d/centos-local.repo
    152. [centos7.9]
    153. name=centos7.9
    154. baseurl=file:///mnt/cdrom
    155. enabled=1
    156. gpgcheck=0
    157. eof"
    158. echo "############# ${host_address[j]}配置完毕 #############"
    159. echo "########################## 配置${host_address[j]}的扩展源 ##########################"
    160. ssh root@${host_address[j]} "yum install epel-release -y"
    161. echo "############# ${host_address[j]}配置完毕 #############"
    162. echo "########################## 配置${host_address[j]}的阿里yum源 ##########################"
    163. ssh root@${host_address[j]} "yum install -y wget"
    164. if [ `ssh root@${host_address[j]} 'test -a /etc/yum.repos.d/CentOS-Base.repo;echo $?'` == 0 ];then
    165. ssh root@${host_address[j]} "wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo"
    166. fi
    167. ssh root@${host_address[j]} "yum clean all && yum repolist"
    168. echo "############# ${host_address[j]}配置完毕 #############"
    169. if [ `ssh root@${host_address[j]} "echo $?"` != 0 ];then
    170. echo "yum源配置有误,退出执行脚本"
    171. exit
    172. fi
    173. done
    174. break
    175. elif [ "${flag}" == "n" ];then
    176. echo "############# 已跳过步骤-配置yum源 #############"
    177. break
    178. elif [ ${i} -eq 99 ];then
    179. echo "############# 已退出 #############"
    180. exit
    181. else continue;fi
    182. done
    183. # 配置chrony时间服务器
    184. for ((i=0;i<100;i++));do
    185. read -p "配置chrony时间服务器?(Y/n): " flag
    186. if [ "${flag}" == "Y" ];then
    187. sleep 3
    188. echo "########################## 开始配置chrony ##########################"
    189. for ((j=0;j<2;j++));do
    190. if [ `ssh root@${host_address[j]} "systemctl restart chronyd;echo $?"` != 0 ];then
    191. echo "${host_address[j]} 安装chrony"
    192. ssh root@${host_address[j]} "yum install -y chrony && systemctl restart chronyd"
    193. if [ `echo $?` != 0 ];then
    194. echo "安装失败,请排错!"
    195. exit
    196. fi
    197. fi
    198. echo "${host_address[j]}配置chrony"
    199.   ssh root@${host_address[j]} "sed -i '/^server/d' /etc/chrony.conf"
    200. if [ ${host_address[j]} == ${host_address[0]} ];then
    201. ssh root@${host_address[j]} "sed -i '2a\server '"${host_address[0]}"' iburst\' /etc/chrony.conf"
    202. ssh root@${host_address[j]} "sed -i 's/#allow 192.168.0.0\/16/allow 192.168.10.0\/16/' /etc/chrony.conf"
    203. ssh root@${host_address[j]} "sed -i 's/#local stratum 10/local stratum 10/' /etc/chrony.conf"
    204. sleep 2
    205. else
    206. ssh root@${host_address[j]} "sed -i '2a\server '"${host_address[0]}"' iburst\' /etc/chrony.conf"
    207. fi
    208. ssh root@${host_address[j]} "systemctl restart chronyd && systemctl enable chronyd &> /dev/null"
    209. sleep 5
    210. ssh root@${host_address[j]} "timedatectl set-ntp true && chronyc sources -v | sed -n '/^\^\*/p'"
    211. if [ -z "`ssh root@${host_address[j]} "chronyc sources -v | sed -n '/^\^\*/p'"`" ];then
    212. echo -e "\e[31m此节点${host_address[j]}的chrony配置有误,请手动调试\e[0m"
    213. exit
    214. fi
    215. echo "############# ${host_address[j]}配置完毕 #############"
    216. done
    217. break
    218. elif [ "${flag}" == "n" ];then
    219. echo "############# 已跳过步骤-配置chrony时间服务器 #############"
    220. break
    221. elif [ ${i} -eq 99 ];then
    222. echo "############# 已退出 #############"
    223. exit
    224. else continue;fi
    225. done
    226. # 安装e2fsprogs
    227. for ((i=0;i<100;i++));do
    228. read -p "安装e2fsprogs?(Y/n): " flag
    229. if [ "${flag}" == "Y" ];then
    230. sleep 3
    231. echo "########################## 开始安装e2fsprogs ##########################"
    232. for ((j=0;j<2;j++));do
    233. echo "########################## ${host_address[j]}开始安装e2fsprogs ##########################"
    234. ssh root@${host_address[j]} "rm -rf ~/e2fsprogs1.44.5 && mkdir ~/e2fsprogs1.44.5"
    235. ssh root@${host_address[j]} "wget -c -r -nd https://downloads.whamcloud.com/public/e2fsprogs/1.44.5.wc1/el7/RPMS/x86_64/ -P ~/e2fsprogs1.44.5"
    236. ssh root@${host_address[j]} "rm -rf ~/e2fsprogs1.44.5/index.html* ~/e2fsprogs1.44.5/unknown.gif ~/e2fsprogs1.44.5/*.gif ~/e2fsprogs1.44.5/sha256sum"
    237. ssh root@${host_address[j]} "rpm -Uvh ~/e2fsprogs1.44.5/* --force"
    238. ssh root@${host_address[j]} "rpm -qa | grep e2fsprogs"
    239. if [ `echo $?` != 0 ];then
    240. echo "安装失败,请排错!"
    241. exit
    242. fi
    243. echo "############# ${host_address[j]}配置完毕 #############"
    244. done
    245. break
    246. elif [ "${flag}" == "n" ];then
    247. echo "############# 已跳过步骤-安装e2fsprogs #############"
    248. break
    249. elif [ ${i} -eq 99 ];then
    250. echo "############# 已退出 #############"
    251. exit
    252. else continue;fi
    253. done
    254. # 安装lustre
    255. for ((i=0;i<100;i++));do
    256. read -p "安装Lustre软件?(Y/n): " flag
    257. if [ "${flag}" == "Y" ];then
    258. sleep 3
    259. echo "########################## 开始安装lustre ##########################"
    260. for ((j=0;j<2;j++));do
    261. echo "########################## ${host_address[j]}开始安装lustre ##########################"
    262. ssh root@${host_address[j]} "yum install -y linux-firmware dracut selinux-policy-targeted kexec-tools libyaml perl"
    263. ssh root@${host_address[j]} "rm -rf ~/lustre2.12.1 && mkdir ~/lustre2.12.1"
    264. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/kernel-3.10.0-957.10.1.el7_lustre.x86_64.rpm -P ~/lustre2.12.1"
    265. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/kmod-lustre-2.12.1-1.el7.x86_64.rpm -P ~/lustre2.12.1"
    266. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/kmod-lustre-osd-ldiskfs-2.12.1-1.el7.x86_64.rpm -P ~/lustre2.12.1"
    267. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/lustre-2.12.1-1.el7.x86_64.rpm -P ~/lustre2.12.1"
    268. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/lustre-osd-ldiskfs-mount-2.12.1-1.el7.x86_64.rpm -P ~/lustre2.12.1"
    269. ssh root@${host_address[j]} "rpm -Uvh ~/lustre2.12.1/*.rpm --force"
    270. ssh root@${host_address[j]} "rpm -qa | grep lustre"
    271. if [ `echo $?` != 0 ];then
    272. echo "安装失败,请排错!"
    273. exit
    274. fi
    275. echo "############# ${host_address[j]}配置完毕 #############"
    276. done
    277. break
    278. elif [ "${flag}" == "n" ];then
    279. echo "############# 已跳过步骤-安装lustre #############"
    280. break
    281. elif [ ${i} -eq 99 ];then
    282. echo "############# 已退出 #############"
    283. exit
    284. else continue;fi
    285. done
    286. #echo "########################## 请手动重启 ##########################"
    287. for ((i=0;i<100;i++));do
    288. read -p "是否重启集群主机(只有重启kernel内核才能更换生效)?(Y/n): " flag
    289. if [ "${flag}" == "Y" ];then
    290. sleep 3
    291. for((j=0;j<2;j++));do
    292. ssh root@${host_address[j]} "reboot"
    293. continue
    294. done
    295. for ((k=0;k<100;k++));do
    296. if [ ${k} -eq 99 ];then
    297. echo "############# 设备连接超时.... #############"
    298. exit
    299. fi
    300. if [ `ssh root@${host_address[0]} -o ConnectTimeout=5 "exit";echo $?` == 0 -a `ssh root@${host_address[1]} -o ConnectTimeout=5 "exit";echo $?` == 0 ];then
    301. echo "############# 设备已重启 #############"
    302. break
    303. else
    304. echo "############# 设备正在重启 #############"
    305. fi
    306. done
    307. break
    308. elif [ "${flag}" == "n" ];then
    309. echo "############# 已跳过步骤-重启 #############"
    310. break
    311. elif [ ${i} -eq 99 ];then
    312. echo "############# 已退出 #############"
    313. exit
    314. else continue;fi
    315. done
    316. sleep 5
    317. # 检查lustre
    318. for((i=0;i<2;i++));do
    319. echo "########################## ${host_address[i]}加载Lustre模块,查看Lustre版本 ##########################"
    320. ssh root@${host_address[i]} "modprobe lustre && lsmod | grep lustre"
    321. ssh root@${host_address[i]} "modinfo lustre"
    322. echo "############# ${host_address[i]}配置完毕 #############"
    323. done
    324. # 配置配置Lnet网卡的IP地址和Lustre网络
    325. for ((i=0;i<100;i++));do
    326. read -p "是否继续配置Lnet网卡的IP地址?(Y/n): " flag
    327. if [ "${flag}" == "Y" ];then
    328. sleep 3
    329. for((j=0;j<2;j++));do
    330. echo "########################## ${host_address[j]}配置Lnet网卡的IP地址 ##########################"
    331. read -p "请根据以上输出显示中,输入你要配置的LnNet网卡名称:" network_card
    332. if [ -z "`ssh root@${host_address[j]} "ip addr | grep -o ${network_card}"`" ];then
    333. echo "网卡不存在,请重试"
    334. exit
    335. fi
    336. ssh root@${host_address[j]} "nmcli connection delete ${network_card} &> /dev/null"
    337. ssh root@${host_address[j]} "nmcli connection add type ethernet con-name ${network_card} ifname ${network_card} ipv4.method manual ipv4.addresses '${lnet_address[j]}/24' autoconnect yes"
    338. ssh root@${host_address[j]} "nmcli connection up ${network_card}"
    339. echo "############# ${host_address[j]}配置完毕 #############"
    340. echo "########################## ${host_address[j]}配置Lnet网络 ##########################"
    341. ssh root@${host_address[j]} "echo options lnet networks='tcp(ens33),tcp2(${network_card})' > /etc/modprobe.d/lustre.conf"
    342. ssh root@${host_address[j]} "lustre_rmmod && modprobe -v lustre"
    343. echo "############# ${host_address[j]}配置完毕 #############"
    344. echo "############# ${host_address[j]}查看Lnet网络 #############"
    345. ssh root@${host_address[j]} "lctl list_nids"
    346. echo "##########################################################"
    347. done
    348. break
    349. elif [ "${flag}" == "n" ];then
    350. echo "############# 已跳过步骤-配置Lnet网卡的IP地址 #############"
    351. break
    352. elif [ ${i} -eq 99 ];then
    353. echo "############# 已退出 #############"
    354. exit
    355. else continue;fi
    356. done
    357. # 格式化lustre
    358. for ((i=0;i<100;i++));do
    359. read -p "是否继续格式化lustre?(Y/n): " flag
    360. if [ "${flag}" == "Y" ];then
    361. sleep 3
    362. echo "########################## 开始格式化lustre ##########################"
    363. ssh root@${host_address[0]} "lsblk"
    364. ssh root@${host_address[0]} "mkfs.lustre --mgs --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sdb"
    365. ssh root@${host_address[0]} "mkfs.lustre --fsname global --mdt --index=0 --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --mgsnode=${lnet_address[0]}@tcp2 --mgsnode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sdc"
    366. ssh root@${host_address[0]} "mkfs.lustre --fsname global --mdt --index=1 --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --mgsnode=${lnet_address[0]}@tcp2 --mgsnode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sdd"
    367. ssh root@${host_address[0]} "mkfs.lustre --fsname global --ost --index=0 --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --mgsnode=${lnet_address[0]}@tcp2 --mgsnode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sde"
    368. ssh root@${host_address[0]} "mkfs.lustre --fsname global --ost --index=1 --servicenode=${lnet_address[0]}@tcp2 --servicenode=${lnet_address[1]}@tcp2 --mgsnode=${lnet_address[0]}@tcp2 --mgsnode=${lnet_address[1]}@tcp2 --backfstype=ldiskfs --reformat /dev/sdf"
    369. echo "############# 格式化完毕 #############"
    370. echo "########################## 创建挂载点目录 ##########################"
    371. for((j=0;j<2;j++));do
    372. ssh root@${host_address[j]} "mkdir /mnt/mgs &> /dev/null;mkdir /mnt/mdt1 &> /dev/null;mkdir /mnt/mdt2 &> /dev/null;mkdir /mnt/ost1 &> /dev/null;mkdir /mnt/ost2 &> /dev/null"
    373. done
    374. echo "############# 创建完毕 #############"
    375. echo "############# ${host_address[1]}查看格式化 #############"
    376. ssh root@${host_address[1]} "blkid /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf"
    377. echo "##########################################################"
    378. break
    379. elif [ "${flag}" == "n" ];then
    380. echo "############# 已跳过步骤-格式化lustre #############"
    381. break
    382. elif [ ${i} -eq 99 ];then
    383. echo "############# 已退出 #############"
    384. exit
    385. else continue;fi
    386. done
    387. # 测试挂载
    388. for ((i=0;i<100;i++));do
    389. read -p "是否继续测试lustre的挂载?(Y/n): " flag
    390. if [ "${flag}" == "Y" ];then
    391. sleep 3
    392. for((j=0;j<2;j++));do
    393. echo "########################## ${host_address[j]}测试挂载 ##########################"
    394. echo "########################## mgs测试挂载 ##########################"
    395. ssh root@${host_address[j]} "umount /mnt/mgs &> /dev/null"
    396. ssh root@${host_address[j]} "mount -t lustre /dev/sdb /mnt/mgs"
    397. if [ `echo $?` != 0 ];then
    398. echo "############# mgs测试有误,请手动查看! #############"
    399. exit
    400. fi
    401. echo "########################## mdt1测试挂载 ##########################"
    402. ssh root@${host_address[j]} "umount /mnt/mdt1 &> /dev/null"
    403. ssh root@${host_address[j]} "mount -t lustre /dev/sdc /mnt/mdt1"
    404. if [ `echo $?` != 0 ];then
    405. echo "############# mdt1测试有误,请手动查看! #############"
    406. ssh root@${host_address[j]} "umount /mnt/mgs"
    407. exit
    408. fi
    409. echo "########################## mdt2测试挂载 ##########################"
    410. ssh root@${host_address[j]} "umount /mnt/mdt2 &> /dev/null"
    411. ssh root@${host_address[j]} "mount -t lustre /dev/sdd /mnt/mdt2"
    412. if [ `echo $?` != 0 ];then
    413. echo "############# mdt2测试有误,请手动查看! #############"
    414. ssh root@${host_address[j]} "umount /mnt/mdt1;umount /mnt/mgs"
    415. exit
    416. fi
    417. echo "########################## ost1测试挂载 ##########################"
    418. ssh root@${host_address[j]} "umount /mnt/ost1 &> /dev/null"
    419. ssh root@${host_address[j]} "mount -t lustre /dev/sde /mnt/ost1"
    420. if [ `echo $?` != 0 ];then
    421. echo "############# ost1测试有误,请手动查看! #############"
    422. ssh root@${host_address[j]} "umount /mnt/mdt2;umount /mnt/mdt1;umount /mnt/mgs"
    423. exit
    424. fi
    425. echo "########################## ost2测试挂载 ##########################"
    426. ssh root@${host_address[j]} "umount /mnt/ost2 &> /dev/null"
    427. ssh root@${host_address[j]} "mount -t lustre /dev/sdf /mnt/ost2"
    428. if [ `echo $?` != 0 ];then
    429. echo "############# ost2测试有误,请手动查看! #############"
    430. ssh root@${host_address[j]} "umount /mnt/ost1;umount /mnt/mdt2;umount /mnt/mdt1;umount /mnt/mgs"
    431. exit
    432. fi
    433. ssh root@${host_address[j]} "umount /mnt/ost2;umount /mnt/ost1;umount /mnt/mdt2;umount /mnt/mdt1;umount /mnt/mgs"
    434. done
    435. echo "############# 测试完毕 #############"
    436. break
    437. elif [ "${flag}" == "n" ];then
    438. echo "############# 已跳过步骤-测试挂载 #############"
    439. break
    440. elif [ ${i} -eq 99 ];then
    441. echo "############# 已退出 #############"
    442. exit
    443. else continue;fi
    444. done
    445. # 安装packemaker和corosync软件和创建集群
    446. for ((i=0;i<100;i++));do
    447. read -p "是否继续安装packemaker和corosync软件和创建集群?(Y/n): " flag
    448. if [ "${flag}" == "Y" ];then
    449. sleep 3
    450. if [ -z "`ssh root@${host_address[0]} 'pcs status' | grep mycluster`" ];then
    451. for((j=0;j<2;j++));do
    452. echo "########################## ${host_address[j]}开始安装 ##########################"
    453. ssh root@${host_address[j]} "yum install pacemaker pcs policycoreutils-python -y"
    454. echo "############# ${host_address[j]}安装完毕 #############"
    455. echo "########################## ${host_address[j]}开始配置 ##########################"
    456. ssh root@${host_address[j]} "systemctl enable pcsd;systemctl restart pcsd"
    457. ssh root@${host_address[j]} "echo '${host_passwd}' |passwd --stdin hacluster"
    458. echo "############# ${host_address[j]}配置完毕 #############"
    459. done
    460. /usr/bin/expect << eof
    461. # 设置捕获字符串后,期待回复的超时时间
    462. set timeout 10
    463. spawn ssh root@${host_address[0]} "pcs cluster auth ${host_hostname[*]}"
    464. ## 开始进连续捕获
    465. expect {
    466.        "Username:"         { send "hacluster\n"; exp_continue }
    467.        "Password:"         { send "${host_passwd}\n"; exp_continue }
    468. }
    469. eof
    470. echo "########################## 开始创建集群 ##########################"
    471. ssh root@${host_address[0]} "pcs cluster setup --name mylustre ${host_hostname[*]}"
    472. echo "############# 创建完毕 #############"
    473. fi
    474. echo "########################## 启动集群 ##########################"
    475. ssh root@${host_address[0]} "pcs cluster start --all"
    476. echo "############# 启动完毕 #############"
    477. break
    478. elif [ "${flag}" == "n" ];then
    479. echo "############# 已跳过步骤-安装packemaker和corosync软件和配置 #############"
    480. break
    481. elif [ ${i} -eq 99 ];then
    482. echo "############# 已退出 #############"
    483. exit
    484. else continue;fi
    485. done
    486. # 配置资源防护
    487. for ((i=0;i<100;i++));do
    488. read -p "是否继续配置资源防护?(Y/n): " flag
    489. if [ "${flag}" == "Y" ];then
    490. sleep 3
    491. echo "########################## 开始配置资源防护 ##########################"
    492. for((j=0;j<2;j++));do
    493. ssh root@${host_address[j]} "yum install -y fence-agents-all"
    494. done
    495. ssh root@${host_address[0]} "pcs property set stonith-enabled=true"
    496. if [ `ssh root@${host_address[j]} "pcs status" | grep "stonith:fence_heuristics_ping" | grep -c "Started"` -eq 2 ];then
    497. echo "############# stonith已创建,并且正常运行,跳过配置stonith #############"
    498. break
    499. fi
    500. if [ `ssh root@${host_address[j]} "pcs status" | grep "stonith:fence_heuristics_ping"` -eq 2 ];then
    501. ssh root@${host_address[0]} "pcs stonith delete stonith-ping-${host_hostname[0]}"
    502. ssh root@${host_address[0]} "pcs stonith delete stonith-ping-${host_hostname[0]}"
    503. fi
    504. ssh root@${host_address[0]} "pcs stonith create stonith-ping-${host_hostname[0]} fence_heuristics_ping ping_targets=${host_address[0]}"
    505. ssh root@${host_address[0]} "pcs stonith create stonith-ping-${host_hostname[1]} fence_heuristics_ping ping_targets=${host_address[1]}"
    506. ssh root@${host_address[0]} "pcs status"
    507. echo "############# 配置完毕 #############"
    508. break
    509. elif [ "${flag}" == "n" ];then
    510. echo "############# 已跳过步骤-配置资源防护 #############"
    511. break
    512. elif [ ${i} -eq 99 ];then
    513. echo "############# 已退出 #############"
    514. exit
    515. else continue;fi
    516. done
    517. # 创建lustre资源
    518. for ((i=0;i<100;i++));do
    519. read -p "是否继续创建lustre资源(可删除后再创建)?(Y/n): " flag
    520. if [ "${flag}" == "Y" ];then
    521. sleep 3
    522. for((j=0;j<2;j++));do
    523. if [ -z `ssh root@${host_address[j]} 'rpm -qa | grep lustre-resource-agents-2.12.1-1'` ];then
    524. echo "########################## ${host_address[j]}安装ocf:lustre:Lustre包 ##########################"
    525. ssh root@${host_address[j]} "wget https://downloads.whamcloud.com/public/lustre/lustre-2.12.1/el7/server/RPMS/x86_64/lustre-resource-agents-2.12.1-1.el7.x86_64.rpm"
    526. ssh root@${host_address[j]} "rpm -ivh lustre-resource-agents-2.12.1-1.el7.x86_64.rpm"
    527. echo "############# 安装完毕 #############"
    528. fi
    529. done
    530. echo "########################## 开始删除lustre资源 ##########################"
    531. ssh root@${host_address[0]} "pcs resource delete global-ost2 &> /dev/null;pcs resource delete global-ost1 &> /dev/null"
    532. ssh root@${host_address[0]} "pcs resource delete global-mdt1 &> /dev/null;pcs resource delete global-mdt2 &> /dev/null"
    533. ssh root@${host_address[0]} "pcs resource delete global-mgs &> /dev/null"
    534. echo "############# 删除完毕 #############"
    535. echo "########################## 开始创建lustre资源 ##########################"
    536. ssh root@${host_address[0]} "pcs resource create global-mgs ocf:lustre:Lustre target=/dev/sdb mountpoint=/mnt/mgs"
    537. if [ `echo $?` != 0 ];then
    538. echo "############# mgs资源创建有误,请手动查看! #############"
    539. exit
    540. fi
    541. ssh root@${host_address[0]} "pcs resource create global-mdt1 ocf:lustre:Lustre target=/dev/sdc mountpoint=/mnt/mdt1"
    542. if [ `echo $?` != 0 ];then
    543. echo "############# mdt1资源创建有误,请手动查看! #############"
    544. exit
    545. fi
    546. ssh root@${host_address[0]} "pcs resource create global-mdt2 ocf:lustre:Lustre target=/dev/sdd mountpoint=/mnt/mdt2"
    547. if [ `echo $?` != 0 ];then
    548. echo "############# mdt2资源创建有误,请手动查看! #############"
    549. exit
    550. fi
    551. ssh root@${host_address[0]} "pcs resource create global-ost1 ocf:lustre:Lustre target=/dev/sde mountpoint=/mnt/ost1"
    552. if [ `echo $?` != 0 ];then
    553. echo "############# ost1资源创建有误,请手动查看! #############"
    554. exit
    555. fi
    556. ssh root@${host_address[0]} "pcs resource create global-ost2 ocf:lustre:Lustre target=/dev/sdf mountpoint=/mnt/ost2"
    557. if [ `echo $?` != 0 ];then
    558. echo "############# ost2资源创建有误,请手动查看! #############"
    559. exit
    560. fi
    561. ssh root@${host_address[0]} "pcs constraint location add global-constraint-mgs global-mgs ${host_hostname[0]} 10"
    562. ssh root@${host_address[0]} "pcs constraint location add global-constraint-mdt1 global-mdt1 ${host_hostname[0]} 10"
    563. ssh root@${host_address[0]} "pcs constraint location add global-constraint-mdt2 global-mdt2 ${host_hostname[1]} 10"
    564. ssh root@${host_address[0]} "pcs constraint location add global-constraint-ost1 global-ost1 ${host_hostname[0]} 10"
    565. ssh root@${host_address[0]} "pcs constraint location add global-constraint-ost2 global-ost2 ${host_hostname[1]} 10"
    566. ssh root@${host_address[0]} "pcs constraint order start global-mgs then start global-mdt1"
    567. ssh root@${host_address[0]} "pcs constraint order start global-mgs then start global-mdt2"
    568. ssh root@${host_address[0]} "pcs constraint order start global-mgs then start global-ost1"
    569. ssh root@${host_address[0]} "pcs constraint order start global-mgs then start global-ost2"
    570. if [ `echo $?` != 0 ];then
    571. echo "############# 资源约束创建有误,请手动查看! #############"
    572. exit
    573. fi
    574. lnet_name1=`ssh root@${host_address[0]} "awk '{print $3}' /etc/modprobe.d/lustre.conf" | awk -F ',' '{print $2}' | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
    575. lnet_name2=`ssh root@${host_address[1]} "awk '{print $3}' /etc/modprobe.d/lustre.conf" | awk -F ',' '{print $2}' | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
    576. ssh root@${host_address[0]} "pcs resource delete ping-lnet &>/dev/null;pcs resource delete global-healthLUSTRE &> /dev/null"
    577. ssh root@${host_address[0]} "pcs resource create ping-lnet ocf:lustre:healthLNET lctl=true multiplier=1001 device=${lnet_name1} host_list='${lnet_address[0]}@tcp2 ${lnet_address[1]}@tcp2' --clone"
    578. ssh root@${host_address[0]} "pcs resource create global-healthLUSTRE ocf:lustre:healthLUSTRE --clone"
    579. if [ `echo $?` != 0 ];then
    580. echo "############# 资源监听创建有误,请手动查看! #############"
    581. exit
    582. fi
    583. echo "############# 创建完毕 #############"
    584. echo "##########################   查看集群状态   ##########################"
    585. echo "########################################################################"
    586. echo "########################################################################"
    587. ssh root@${host_address[0]} "pcs status"
    588. echo "########################################################################"
    589. echo "########################################################################"
    590. echo "################################################## 脚本到此全部执行完毕 ##################################################"
    591. break
    592. elif [ "${flag}" == "n" ];then
    593. echo "############# 已跳过步骤-创建lustre资源 #############"
    594. break
    595. elif [ ${i} -eq 99 ];then
    596. echo "############# 已退出 #############"
    597. exit
    598. else continue;fi
    599. done

  • 相关阅读:
    jmeter 用户自定义变量
    Linux 防火墙 firewalld 常用命令
    [补题记录] Atcoder Beginner Contest 300(E)
    【Flask】官方教程(Tutorial)-part3:blog蓝图、项目可安装化
    国外访问学者申请政策解析
    CSS_文本属性
    golang常用包
    KNN学习代码理解尝试
    【OpenSSL】Ubuntu 下编译OpenSSL
    Qt5开发及实例V2.0-第十五章-Qt单元测试框架
  • 原文地址:https://blog.csdn.net/qq_56776641/article/details/133300332