SSH高效實(shí)現(xiàn)批量連接服務(wù)器的最佳實(shí)踐與避坑指南
引言:當(dāng)自動(dòng)化遇到現(xiàn)實(shí)挑戰(zhàn)
在運(yùn)維工作中,我們常常需要管理大量服務(wù)器。最近我遇到一個(gè)實(shí)際需求:需要快速測(cè)試一批服務(wù)器的SSH連接性,所有服務(wù)器都使用相同的root賬號(hào)和密碼。這聽(tīng)起來(lái)應(yīng)該很簡(jiǎn)單,但實(shí)施過(guò)程中卻遇到了各種意想不到的問(wèn)題。本文將詳細(xì)記錄這次實(shí)戰(zhàn)經(jīng)歷,分享完整的解決方案,并總結(jié)出可靠的最佳實(shí)踐。
需求分析:明確目標(biāo)與約束條件
原始需求
- 測(cè)試一批IP地址的22端口SSH連接
- 使用固定賬號(hào):root
- 使用固定密碼:mn150@2099A
- 需要超時(shí)控制(2-3秒)
- 結(jié)果分類(lèi):成功與失敗的IP分別保存
- 支持批量處理(IP數(shù)量可能達(dá)到數(shù)百個(gè))
技術(shù)約束
- 運(yùn)行環(huán)境:CentOS 7
- 網(wǎng)絡(luò)條件:可能存在網(wǎng)絡(luò)延遲和防火墻限制
- 安全考慮:雖然使用密碼認(rèn)證,但這是測(cè)試環(huán)境
技術(shù)選型:多種方案的對(duì)比
方案一:sshpass + 循環(huán)(最直接)
sshpass -p '密碼' ssh -o ConnectTimeout=5 root@IP "命令"
優(yōu)點(diǎn):簡(jiǎn)單直接,無(wú)需額外依賴(lài)
缺點(diǎn):缺乏并行處理,速度慢
方案二:expect腳本(更健壯)
expect << EOF
spawn ssh root@IP
expect "password:" { send "密碼\r" }
expect eof
EOF
優(yōu)點(diǎn):處理交互更穩(wěn)定
缺點(diǎn):語(yǔ)法復(fù)雜,調(diào)試?yán)щy
方案三:Python + Paramiko(功能強(qiáng)大)
import paramiko ssh = paramiko.SSHClient() ssh.connect(hostname=ip, username='root', password='密碼', timeout=3)
優(yōu)點(diǎn):功能全面,錯(cuò)誤處理完善
缺點(diǎn):需要Python環(huán)境
方案四:并行處理(效率最高)
cat iplist.txt | parallel -j 20 "測(cè)試命令"
優(yōu)點(diǎn):大幅提升測(cè)試速度
缺點(diǎn):并發(fā)控制復(fù)雜,容易出錯(cuò)
實(shí)戰(zhàn)過(guò)程:從簡(jiǎn)單到復(fù)雜的演進(jìn)
第一階段:基礎(chǔ)腳本開(kāi)發(fā)
最初的腳本很簡(jiǎn)單:
#!/bin/bash
PASSWORD='mn150@2099A'
while read ip; do
sshpass -p "$PASSWORD" ssh -o ConnectTimeout=3 root@$ip "echo ok"
if [ $? -eq 0 ]; then
echo "$ip" >> success.txt
else
echo "$ip" >> fail.txt
fi
done < iplist.txt
這個(gè)腳本能工作,但存在幾個(gè)問(wèn)題:
- 沒(méi)有進(jìn)度顯示
- 錯(cuò)誤處理不完善
- 串行執(zhí)行,速度慢
第二階段:添加功能增強(qiáng)
改進(jìn)版本增加了以下功能:
#!/bin/bash
# 配置參數(shù)
PASSWORD='mn150@2099A'
TIMEOUT=3
IP_FILE="iplist.txt"
SUCCESS_FILE="success_ips.txt"
FAILED_FILE="failed_ips.txt"
# 統(tǒng)計(jì)和進(jìn)度
TOTAL=$(wc -l < "$IP_FILE")
COUNT=0
echo "開(kāi)始測(cè)試 $TOTAL 個(gè)IP..."
while read ip; do
COUNT=$((COUNT + 1))
echo -n "[$COUNT/$TOTAL] 測(cè)試 $ip ... "
timeout $TIMEOUT sshpass -p "$PASSWORD" ssh \
-o ConnectTimeout=$TIMEOUT \
-o StrictHostKeyChecking=no \
root@"$ip" "exit" 2>/dev/null
if [ $? -eq 0 ]; then
echo "成功"
echo "$ip" >> "$SUCCESS_FILE"
else
echo "失敗"
echo "$ip" >> "$FAILED_FILE"
fi
done < "$IP_FILE"
這個(gè)版本已經(jīng)有了基本框架,但仍然不夠理想。
第三階段:遇到的坑與解決方案
坑1:腳本編碼問(wèn)題
現(xiàn)象:腳本只運(yùn)行了4個(gè)IP就停止
錯(cuò)誤信息:./ssh_batch_test.sh: line 1: i#!/bin/bash: No such file or directory
根本原因:腳本文件編碼問(wèn)題,可能是從Windows復(fù)制過(guò)來(lái),或者編輯器保存時(shí)加入了BOM頭。
解決方案:
# 檢查文件編碼 file ssh_batch_test.sh # 修復(fù)編碼問(wèn)題 sed -i '1s/^.*#!/#!/' ssh_batch_test.sh # 或者重新創(chuàng)建腳本 cat > new_script.sh << 'EOF' #!/bin/bash # 正確內(nèi)容 EOF
坑2:IP文件格式問(wèn)題
現(xiàn)象:雖然IP文件有200多個(gè)IP,但腳本只處理了4個(gè)
可能原因:
- Windows換行符(CRLF)
- 文件中有特殊字符
- 讀取邏輯有缺陷
解決方案:
# 檢查文件格式 cat -A iplist.txt | head -10 # 修復(fù)換行符 dos2unix iplist.txt # 或者使用sed sed -i 's/\r$//' iplist.txt # 清理文件 grep -v '^[[:space:]]*$' iplist.txt | grep -v '^#' > iplist_clean.txt
坑3:SSH選項(xiàng)沖突
現(xiàn)象:手動(dòng)測(cè)試成功,但腳本測(cè)試失敗
關(guān)鍵發(fā)現(xiàn):-o BatchMode=yes選項(xiàng)會(huì)禁用密碼認(rèn)證!
錯(cuò)誤配置:
# 這個(gè)會(huì)失敗 sshpass -p '密碼' ssh -o BatchMode=yes root@IP "命令" # 正確的應(yīng)該是 sshpass -p '密碼' ssh -o BatchMode=no root@IP "命令" # 或者干脆不要BatchMode選項(xiàng)
坑4:并發(fā)寫(xiě)入沖突
現(xiàn)象:并行腳本運(yùn)行異常,結(jié)果不完整
原因:多個(gè)進(jìn)程同時(shí)寫(xiě)入同一個(gè)文件
解決方案:
# 每個(gè)進(jìn)程寫(xiě)入獨(dú)立文件,最后合并 TEMP_DIR=$(mktemp -d) # 每個(gè)進(jìn)程寫(xiě)入 $TEMP_DIR/result_$PID # 最后 cat $TEMP_DIR/* > final_result.txt
第四階段:最終穩(wěn)定版本
經(jīng)過(guò)多次調(diào)試,最終得到了穩(wěn)定可靠的版本:
#!/bin/bash
# SSH批量測(cè)試腳本 - 最終穩(wěn)定版
set -u # 使用未定義變量時(shí)報(bào)錯(cuò)
set -e # 遇到錯(cuò)誤時(shí)退出
# 配置參數(shù)
PASSWORD='mn150@2099A'
CONNECT_TIMEOUT=3
IP_FILE="${1:-iplist.txt}"
# 生成帶時(shí)間戳的結(jié)果文件
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
SUCCESS_FILE="success_ips_${TIMESTAMP}.txt"
FAILED_FILE="failed_ips_${TIMESTAMP}.txt"
# 驗(yàn)證環(huán)境
validate_environment() {
# 檢查sshpass
if ! command -v sshpass &> /dev/null; then
echo "錯(cuò)誤: sshpass 未安裝"
echo "請(qǐng)執(zhí)行: sudo yum install -y epel-release sshpass"
exit 1
fi
# 檢查IP文件
if [ ! -f "$IP_FILE" ]; then
echo "錯(cuò)誤: IP文件 $IP_FILE 不存在"
exit 1
fi
# 檢查文件是否可讀
if [ ! -r "$IP_FILE" ]; then
echo "錯(cuò)誤: 無(wú)法讀取IP文件 $IP_FILE"
exit 1
fi
}
# 清理和驗(yàn)證IP列表
prepare_ip_list() {
local input_file="$1"
local output_file="$2"
# 清空輸出文件
> "$output_file"
# 處理IP文件:移除注釋、空行、換行符
while IFS= read -r line || [ -n "$line" ]; do
# 移除換行符和回車(chē)符
line=$(echo "$line" | tr -d '\r')
# 跳過(guò)空行和注釋
if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then
continue
fi
# 提取IP(移除可能的后綴和注釋?zhuān)?
ip=$(echo "$line" | sed 's/[[:space:]].*$//' | sed 's/#.*$//')
# 驗(yàn)證IP格式
if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "$ip" >> "$output_file"
else
echo "警告: 跳過(guò)無(wú)效IP格式: $ip" >&2
fi
done < "$input_file"
}
# 測(cè)試單個(gè)IP
test_single_ip() {
local ip="$1"
# 使用timeout防止命令掛起
timeout ${CONNECT_TIMEOUT} sshpass -p "$PASSWORD" ssh \
-o ConnectTimeout=${CONNECT_TIMEOUT} \
-o StrictHostKeyChecking=no \
-o PasswordAuthentication=yes \
-o BatchMode=no \
-o LogLevel=ERROR \
root@"$ip" "exit 0" 2>/dev/null
return $?
}
# 主函數(shù)
main() {
echo "========================================"
echo "SSH批量連接測(cè)試工具"
echo "========================================"
echo "賬號(hào): root"
echo "超時(shí)時(shí)間: ${CONNECT_TIMEOUT}秒"
echo "IP文件: $IP_FILE"
echo "結(jié)果文件:"
echo " 成功IP: $SUCCESS_FILE"
echo " 失敗IP: $FAILED_FILE"
echo "========================================"
# 驗(yàn)證環(huán)境
validate_environment
# 準(zhǔn)備IP列表
local clean_ip_file="/tmp/clean_ips_$$.txt"
prepare_ip_list "$IP_FILE" "$clean_ip_file"
local total_ips=$(wc -l < "$clean_ip_file" 2>/dev/null || echo 0)
if [ $total_ips -eq 0 ]; then
echo "錯(cuò)誤: 沒(méi)有有效的IP地址可以測(cè)試"
exit 1
fi
echo "找到 $total_ips 個(gè)有效IP地址"
echo ""
# 清空結(jié)果文件
> "$SUCCESS_FILE"
> "$FAILED_FILE"
# 開(kāi)始測(cè)試
echo "開(kāi)始測(cè)試..."
echo "----------------------------------------"
local tested=0
local success=0
local failed=0
while read ip; do
tested=$((tested + 1))
# 顯示進(jìn)度(每10個(gè)IP顯示一次)
if [ $((tested % 10)) -eq 0 ] || [ $tested -eq $total_ips ]; then
printf "\r進(jìn)度: %d/%d (%.1f%%)" $tested $total_ips $(echo "scale=1; $tested*100/$total_ips" | bc)
fi
# 測(cè)試連接
if test_single_ip "$ip"; then
echo "$ip" >> "$SUCCESS_FILE"
success=$((success + 1))
else
echo "$ip" >> "$FAILED_FILE"
failed=$((failed + 1))
fi
done < "$clean_ip_file"
echo ""
echo "----------------------------------------"
echo ""
# 生成報(bào)告
echo "測(cè)試完成!"
echo "========================================"
echo "統(tǒng)計(jì)結(jié)果:"
echo " 總IP數(shù): $total_ips"
echo " 成功數(shù): $success"
echo " 失敗數(shù): $failed"
if [ $total_ips -gt 0 ]; then
local success_rate=$(echo "scale=2; $success * 100 / $total_ips" | bc)
echo " 成功率: ${success_rate}%"
fi
echo ""
echo "結(jié)果文件已生成:"
echo " $SUCCESS_FILE"
echo " $FAILED_FILE"
# 顯示示例結(jié)果
if [ -s "$SUCCESS_FILE" ]; then
echo ""
echo "成功IP示例(前5個(gè)):"
head -5 "$SUCCESS_FILE" | cat -n
fi
# 清理臨時(shí)文件
rm -f "$clean_ip_file"
}
# 執(zhí)行主函數(shù)
main
關(guān)鍵技術(shù)與最佳實(shí)踐
1. 健壯的IP文件處理
# 正確處理各種格式的IP文件
while IFS= read -r line || [ -n "$line" ]; do
# 處理?yè)Q行符
line=$(echo "$line" | tr -d '\r')
# 驗(yàn)證IP格式
if [[ "$line" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
# 有效IP
fi
done < ipfile.txt
2. 適當(dāng)?shù)腟SH選項(xiàng)配置
# 推薦的SSH選項(xiàng)組合
sshpass -p "$PASSWORD" ssh \
-o ConnectTimeout=$TIMEOUT \ # 連接超時(shí)
-o StrictHostKeyChecking=no \ # 不檢查主機(jī)密鑰(測(cè)試環(huán)境)
-o PasswordAuthentication=yes \ # 允許密碼認(rèn)證
-o BatchMode=no \ # 不禁用密碼認(rèn)證!
-o LogLevel=ERROR \ # 只顯示錯(cuò)誤日志
root@"$IP" "命令"
3. 合理的錯(cuò)誤處理
# 使用timeout防止命令掛起
timeout $TIMEOUT 命令
# 檢查退出碼
if [ $? -eq 0 ]; then
# 成功
elif [ $? -eq 124 ]; then
# 超時(shí)
elif [ $? -eq 5 ]; then
# 認(rèn)證失敗
else
# 其他錯(cuò)誤
fi
4. 進(jìn)度反饋機(jī)制
# 顯示進(jìn)度百分比
printf "\r進(jìn)度: %d/%d (%.1f%%)" $current $total $(echo "scale=1; $current*100/$total" | bc)
# 或使用進(jìn)度條
draw_progress_bar() {
local current=$1
local total=$2
local width=50
local percent=$((current * 100 / total))
local completed=$((current * width / total))
printf "\r["
printf "%${completed}s" | tr ' ' '='
printf "%$((width - completed))s" | tr ' ' '.'
printf "] %3d%%" $percent
}
性能優(yōu)化技巧
并行處理方案
對(duì)于大量IP的測(cè)試,順序執(zhí)行太慢。以下是安全的并行方案:
#!/bin/bash
# 安全的并行測(cè)試
PASSWORD='mn150@2099A'
TIMEOUT=3
MAX_JOBS=10
# 創(chuàng)建臨時(shí)目錄存放結(jié)果
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
# 測(cè)試函數(shù)
test_ip() {
local ip=$1
local pid=$$
timeout $TIMEOUT sshpass -p "$PASSWORD" ssh \
-o ConnectTimeout=$TIMEOUT \
-o StrictHostKeyChecking=no \
root@"$ip" "exit" 2>/dev/null
if [ $? -eq 0 ]; then
echo "$ip" >> "$TEMP_DIR/success_$pid"
else
echo "$ip" >> "$TEMP_DIR/failed_$pid"
fi
}
# 導(dǎo)出函數(shù)
export -f test_ip
export PASSWORD TIMEOUT TEMP_DIR
# 并行執(zhí)行
cat iplist.txt | xargs -P $MAX_JOBS -I {} bash -c 'test_ip "$@"' _ {}
# 合并結(jié)果
cat "$TEMP_DIR"/success_* 2>/dev/null | sort -u > success.txt
cat "$TEMP_DIR"/failed_* 2>/dev/null | sort -u > failed.txt
資源限制
# 避免過(guò)多的并發(fā)連接 ulimit -n 4096 # 增加文件描述符限制 # 限制每個(gè)進(jìn)程的資源使用 prlimit --nproc=100 --nofile=100 --cpu=10 sshpass ...
安全注意事項(xiàng)
盡管這是測(cè)試腳本,但仍需注意安全:
- 密碼處理:不要在腳本中硬編碼密碼,考慮使用環(huán)境變量或配置文件
- 結(jié)果保護(hù):測(cè)試結(jié)果可能包含敏感信息,妥善保管
- 權(quán)限控制:腳本應(yīng)運(yùn)行在受控環(huán)境中
- 日志清理:定期清理日志文件
# 更安全的方式:從環(huán)境變量讀取密碼
PASSWORD=${SSH_PASSWORD:-'default_password'}
# 或者從加密文件讀取
if [ -f ~/.ssh_test_pass ]; then
PASSWORD=$(openssl enc -d -aes-256-cbc -in ~/.ssh_test_pass)
fi
擴(kuò)展功能
1. 添加重試機(jī)制
test_with_retry() {
local ip=$1
local max_retries=2
for ((i=1; i<=max_retries; i++)); do
if test_single_ip "$ip"; then
return 0
fi
sleep 1
done
return 1
}
2. 收集系統(tǒng)信息
# 測(cè)試時(shí)順便收集信息
collect_info() {
local ip=$1
sshpass -p "$PASSWORD" ssh root@"$ip" "
echo '=== 系統(tǒng)信息 ==='
uname -a
echo ''
echo '=== 內(nèi)存信息 ==='
free -h
echo ''
echo '=== 磁盤(pán)信息 ==='
df -h
" > "info_${ip}.txt" 2>/dev/null
}
3. 生成HTML報(bào)告
generate_html_report() {
cat > report.html << EOF
<html>
<head>
<title>SSH測(cè)試報(bào)告</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; }
th { background-color: #f2f2f2; }
.success { background-color: #dff0d8; }
.failed { background-color: #f2dede; }
</style>
</head>
<body>
<h1>SSH連接測(cè)試報(bào)告</h1>
<p>生成時(shí)間: $(date)</p>
<p>總計(jì): $total_ips 個(gè)IP</p>
<p>成功: $success 個(gè)</p>
<p>失敗: $failed 個(gè)</p>
<h2>成功IP列表</h2>
<table>
<tr><th>序號(hào)</th><th>IP地址</th></tr>
$(cat success.txt | awk '{print "<tr class=\"success\"><td>"NR"</td><td>"$1"</td></tr>"}')
</table>
</body>
</html>
EOF
}
總結(jié)與建議
經(jīng)過(guò)這次實(shí)戰(zhàn),我總結(jié)了以下經(jīng)驗(yàn):
核心原則
- 簡(jiǎn)單可靠勝過(guò)復(fù)雜先進(jìn):先確?;A(chǔ)功能穩(wěn)定
- 逐步增強(qiáng):從簡(jiǎn)單版本開(kāi)始,逐步添加功能
- 充分測(cè)試:小范圍測(cè)試驗(yàn)證后再批量運(yùn)行
- 錯(cuò)誤處理:預(yù)見(jiàn)到可能的問(wèn)題并提前處理
技術(shù)建議
- 使用
set -euo pipefail讓腳本更健壯 - 總是驗(yàn)證輸入數(shù)據(jù)的有效性
- 為長(zhǎng)時(shí)間運(yùn)行的任務(wù)添加進(jìn)度反饋
- 使用臨時(shí)文件處理并發(fā)寫(xiě)入問(wèn)題
- 清理臨時(shí)文件和資源
運(yùn)維建議
- 保持腳本的日志記錄能力
- 考慮添加監(jiān)控和告警機(jī)制
- 定期回顧和優(yōu)化腳本
- 文檔化腳本的使用和維護(hù)方法
批量測(cè)試SSH連接雖然看起來(lái)簡(jiǎn)單,但實(shí)際實(shí)施時(shí)會(huì)遇到各種預(yù)料之外的問(wèn)題。通過(guò)系統(tǒng)的分析和逐步的改進(jìn),我們最終得到了一個(gè)穩(wěn)定、高效、功能完善的解決方案。這個(gè)過(guò)程不僅解決了眼前的問(wèn)題,也為類(lèi)似任務(wù)積累了寶貴經(jīng)驗(yàn)。
記住:好的自動(dòng)化腳本不是一蹴而就的,而是在不斷遇到問(wèn)題、解決問(wèn)題的過(guò)程中逐漸完善起來(lái)的。每個(gè)錯(cuò)誤和調(diào)試過(guò)程都是學(xué)習(xí)和提高的機(jī)會(huì)。
到此這篇關(guān)于SSH高效實(shí)現(xiàn)批量連接服務(wù)器的最佳實(shí)踐與避坑指南的文章就介紹到這了,更多相關(guān)SSH批量連接服務(wù)器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用GIT進(jìn)行源碼管理——GUI客戶(hù)端小結(jié)
很多人對(duì)GIT GUI客戶(hù)端是非常不屑一顧的,但我非常喜歡GUI的方便快捷,也不用記憶冗雜的命令,本文簡(jiǎn)單的介紹了幾種免費(fèi)的Windows下的GIT客戶(hù)端,方便大家使用2014-09-09
WordPress網(wǎng)站遷移到新主機(jī)防火墻配置
這篇文章主要為大家介紹了WordPress網(wǎng)站遷移到新主機(jī)防火墻配置,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
git分支管理_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了git分支管理的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
git修改和刪除功能_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了git修改和刪除功能,需要的朋友可以參考下2017-08-08
自己搭建CDN服務(wù)器的工具GoEdge安裝和配置教程
GoEdge是一款基于Go語(yǔ)言開(kāi)發(fā)的管理分布式CDN邊緣節(jié)點(diǎn)的開(kāi)源工具軟件,支持多種協(xié)議和功能,包括?HTTP、HTTPS、WebSocket、TCP、TLS、UDP?等,并集成了?WAF(Web?應(yīng)用防火墻)能力,可以讓用戶(hù)輕松地、低成本地創(chuàng)建CDN/WAF等應(yīng)用2025-08-08
Apache?Hudi靈活的Payload機(jī)制硬核解析
這篇文章主要為大家介紹了Apache?Hudi靈活的Payload機(jī)制超硬核解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03

