批量配置IP
侧边栏壁纸
  • 累计撰写 217 篇文章
  • 累计收到 124 条评论

批量配置IP

cby
cby
2026-09-29 / 0 评论 / 11 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2026年09月29日,已超过10天没有更新,若内容或图片失效,请留言反馈。

批量配置IP

#!/bin/bash

# ============================================================
# Linux 批量虚拟机网络初始化脚本
#
# 功能:
#
# 1. 安装 sshpass
# 2. 创建 SSH Key
# 3. 批量分发 SSH Key
# 4. 重新生成 machine-id
# 5. 检查 ens32 网卡
# 6. 重建 ens32 NetworkManager 连接
# 7. 配置静态 IPv4
# 8. 配置内网 IPv6
# 9. 自动检测新 IPv4 是否可以 SSH
# 10. 输出最终配置结果
#
# ============================================================
#
# 使用:
#
# chmod +x network_init.sh
# ./network_init.sh
#
# ============================================================


# ============================================================
# 基础配置
# ============================================================

# 网络接口名称
NET_IF="ens160"

# IPv4 网段
IP_PREFIX="192.168.1"

# 新 IPv4 起始地址
START_IP=21

# IPv4 子网掩码
IP_MASK=24

# IPv4 网关
GATEWAY="192.168.1.1"

# IPv4 DNS
DNS="223.5.5.5"

# ============================================================
# IPv6 配置
# ============================================================

# IPv6 前缀
IPV6_PREFIX="fc00::"

# IPv6 前缀长度
IPV6_MASK=8

# 是否配置 IPv6 网关
#
# 0 = 只配置 IPv6 地址
# 1 = 配置 IPv6 地址 + 网关 + DNS
#
CONFIG_IPV6_GATEWAY=0

# IPv6 网关
IPV6_GATEWAY="fc00::1"

# IPv6 DNS
IPV6_DNS="2400:3200::1"


# ============================================================
# SSH 配置
# ============================================================

SSH_USER="root"

SSH_TIMEOUT=5

SSH_KEY="/root/.ssh/id_rsa"


# ============================================================
# 颜色
# ============================================================

GREEN="\033[32m"
YELLOW="\033[33m"
RED="\033[31m"
BLUE="\033[36m"
RESET="\033[0m"


# ============================================================
# 日志目录
# ============================================================

LOG_DIR="/tmp/network-init"

mkdir -p "$LOG_DIR"


# ============================================================
# 输出函数
# ============================================================

info()
{
    echo -e "${BLUE}[INFO]${RESET} $1"
}


success()
{
    echo -e "${GREEN}[ OK ]${RESET} $1"
}


warning()
{
    echo -e "${YELLOW}[WARN]${RESET} $1"
}


error()
{
    echo -e "${RED}[ERROR]${RESET} $1"
}


# ============================================================
# 检查 root
# ============================================================

if [ "$EUID" -ne 0 ]; then

    error "请使用 root 用户运行脚本。"

    exit 1

fi


# ============================================================
# 开始
# ============================================================

clear

echo
echo "============================================================"
echo "       Linux 批量虚拟机网络初始化工具"
echo "============================================================"
echo


# ============================================================
# 输入服务器 IP
# ============================================================

read -rp "请输入服务器旧 IP(空格分隔): " IP_LIST


if [ -z "$IP_LIST" ]; then

    error "没有输入服务器 IP。"

    exit 1

fi


# ============================================================
# 输入 SSH 密码
# ============================================================

echo

read -rsp "请输入 root SSH 密码: " SSHPASS

echo

if [ -z "$SSHPASS" ]; then

    error "SSH 密码不能为空。"

    exit 1

fi

export SSHPASS


# ============================================================
# 统计服务器数量
# ============================================================

COUNT=0

for HOST in $IP_LIST
do
    COUNT=$((COUNT + 1))
done


# ============================================================
# 显示配置计划
# ============================================================

echo
echo "============================================================"
echo "配置计划"
echo "============================================================"

echo
echo "网卡              : $NET_IF"
echo "IPv4 网段         : $IP_PREFIX"
echo "IPv4 起始地址     : $START_IP"
echo "IPv4 网关         : $GATEWAY"
echo "IPv4 DNS          : $DNS"
echo "IPv6 前缀         : $IPV6_PREFIX"
echo "IPv6 前缀长度     : /$IPV6_MASK"
echo "服务器数量        : $COUNT"

echo

INDEX=0

for HOST in $IP_LIST
do

    NEW_IP=$((START_IP + INDEX))

    echo "$HOST  ->  ${IP_PREFIX}.${NEW_IP}  ->  ${IPV6_PREFIX}${NEW_IP}"

    INDEX=$((INDEX + 1))

done


# ============================================================
# 确认
# ============================================================

echo

warning "即将修改远程服务器网络配置。"

echo

read -rp "确认继续执行?[y/N]: " CONFIRM


if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then

    warning "用户取消操作。"

    exit 0

fi


# ============================================================
# 1. 安装 sshpass
# ============================================================

echo
echo "============================================================"
echo "1. 检查 sshpass"
echo "============================================================"


if command -v sshpass >/dev/null 2>&1; then

    success "sshpass 已安装"

else

    info "正在安装 sshpass..."

    yum install -y sshpass

    if [ $? -ne 0 ]; then

        error "sshpass 安装失败。"

        exit 1

    fi

    success "sshpass 安装成功"

fi


# ============================================================
# 2. 创建 SSH Key
# ============================================================

echo
echo "============================================================"
echo "2. 创建 SSH Key"
echo "============================================================"


mkdir -p /root/.ssh

chmod 700 /root/.ssh


if [ -f "$SSH_KEY" ]; then

    warning "$SSH_KEY 已存在,不重新生成。"

else

    ssh-keygen -t rsa -b 4096 -f "$SSH_KEY" -N ""

    if [ $? -ne 0 ]; then

        error "SSH Key 创建失败。"

        exit 1

    fi

    success "SSH Key 创建成功"

fi


# ============================================================
# 3. 测试 SSH
# ============================================================

echo
echo "============================================================"
echo "3. 测试服务器 SSH"
echo "============================================================"


SUCCESS_HOSTS=""

FAILED_HOSTS=""


for HOST in $IP_LIST
do

    echo

    info "测试 $HOST ..."

    timeout "$SSH_TIMEOUT" sshpass -e ssh \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout="$SSH_TIMEOUT" \
        "$SSH_USER@$HOST" \
        "echo SSH_OK" >/dev/null 2>&1


    if [ $? -eq 0 ]; then

        success "$HOST SSH 正常"

        SUCCESS_HOSTS="$SUCCESS_HOSTS $HOST"

    else

        error "$HOST SSH 连接失败"

        FAILED_HOSTS="$FAILED_HOSTS $HOST"

    fi

done


# ============================================================
# SSH 失败检查
# ============================================================

if [ -n "$FAILED_HOSTS" ]; then

    echo

    warning "以下服务器 SSH 连接失败:"

    for HOST in $FAILED_HOSTS
    do
        echo "  $HOST"
    done

    echo

    read -rp "是否跳过这些服务器继续?[Y/n]: " SKIP_FAILED

    if [[ "$SKIP_FAILED" =~ ^[Nn]$ ]]; then

        error "脚本终止。"

        exit 1

    fi

fi


# ============================================================
# 4. SSH Key 分发
# ============================================================

echo
echo "============================================================"
echo "4. 分发 SSH Key"
echo "============================================================"


for HOST in $SUCCESS_HOSTS
do

    echo

    info "$HOST -> ssh-copy-id"


    timeout 15 sshpass -e ssh-copy-id \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout="$SSH_TIMEOUT" \
        "$SSH_USER@$HOST" >/dev/null 2>&1


    if [ $? -eq 0 ]; then

        success "$HOST SSH Key 分发成功"

    else

        warning "$HOST SSH Key 分发失败"

    fi

done


# ============================================================
# 5. 重新生成 machine-id
# ============================================================

echo
echo "============================================================"
echo "5. 重新生成 Machine-ID"
echo "============================================================"


for HOST in $SUCCESS_HOSTS
do

    info "$HOST -> machine-id"


    timeout 10 ssh \
        -o BatchMode=yes \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout="$SSH_TIMEOUT" \
        "$SSH_USER@$HOST" \
        "rm -f /etc/machine-id;
         systemd-machine-id-setup"


    if [ $? -eq 0 ]; then

        success "$HOST machine-id 已重新生成"

    else

        warning "$HOST machine-id 修改失败"

    fi

done


# ============================================================
# 6. 检查 ens32
# ============================================================

echo
echo "============================================================"
echo "6. 检查远程 ens32 网卡"
echo "============================================================"


for HOST in $SUCCESS_HOSTS
do

    info "$HOST -> 检查 $NET_IF"


    RESULT=$(timeout 10 ssh \
        -o BatchMode=yes \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout="$SSH_TIMEOUT" \
        "$SSH_USER@$HOST" \
        "ip link show '$NET_IF' >/dev/null 2>&1;
         echo \$?" 2>/dev/null)


    if [ "$RESULT" = "0" ]; then

        success "$HOST 存在 $NET_IF"

    else

        error "$HOST 不存在 $NET_IF"

    fi

done


# ============================================================
# 7. 重建 ens32
#
# 注意:
#
# 不在这里强制 nmcli con up。
#
# 因为 SSH 正在通过当前网络连接。
#
# 先创建新的连接。
# 后面修改 IPv4 时再统一切换。
# ============================================================

echo
echo "============================================================"
echo "7. 重建 NetworkManager ens32"
echo "============================================================"


for HOST in $SUCCESS_HOSTS
do

    info "$HOST -> 重建 $NET_IF"


    timeout 15 ssh \
        -o BatchMode=yes \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout="$SSH_TIMEOUT" \
        "$SSH_USER@$HOST" "

        nmcli con delete '$NET_IF' >/dev/null 2>&1 || true

        nmcli con add \
            type ethernet \
            ifname '$NET_IF' \
            con-name '$NET_IF'

    "


    if [ $? -eq 0 ]; then

        success "$HOST ens32 创建成功"

    else

        error "$HOST ens32 创建失败"

    fi

done


# ============================================================
# 8. 配置 IPv4
#
# 这里是整个脚本最重要的部分。
#
# 不直接:
#
# ssh host "nmcli con up ens32"
#
# 而是:
#
# 1. 远程后台启动
# 2. sleep 2
# 3. 修改 IPv4
# 4. nmcli con up
# 5. SSH 立即退出
# 6. 等待网络恢复
# 7. 使用新 IP 检查
#
# 防止脚本卡住。
# ============================================================

echo
echo "============================================================"
echo "8. 配置静态 IPv4"
echo "============================================================"


INDEX=0


for HOST in $SUCCESS_HOSTS
do

    NEW_IP="${IP_PREFIX}.$((START_IP + INDEX))"

    echo
    echo "------------------------------------------------------------"
    info "$HOST"
    echo "      新 IPv4 : ${NEW_IP}/${IP_MASK}"
    echo "      Gateway : ${GATEWAY}"
    echo "      DNS     : ${DNS}"
    echo "------------------------------------------------------------"


    # --------------------------------------------------------
    # 新 IP 是否就是当前 IP
    # --------------------------------------------------------

    if [ "$HOST" = "$NEW_IP" ]; then

        warning "$HOST 和新 IP 相同,跳过网络切换。"

        INDEX=$((INDEX + 1))

        continue

    fi


    # --------------------------------------------------------
    # 远程后台修改网络
    # --------------------------------------------------------

    timeout 10 ssh \
        -o BatchMode=yes \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout="$SSH_TIMEOUT" \
        "$SSH_USER@$HOST" "

        nohup bash -c '
            sleep 2

            echo \"===== Network Change Start =====\" >> /tmp/network-init.log

            nmcli con mod '$NET_IF' \
                ipv4.addresses '${NEW_IP}/${IP_MASK}' \
                ipv4.gateway '$GATEWAY' \
                ipv4.method manual \
                ipv4.dns '$DNS'

            echo \"nmcli con mod result=\$?\" >> /tmp/network-init.log

            nmcli con up '$NET_IF'

            echo \"nmcli con up result=\$?\" >> /tmp/network-init.log

            echo \"===== Network Change End =====\" >> /tmp/network-init.log

        ' >/tmp/network-init-run.log 2>&1 &

        echo NETWORK_CHANGE_STARTED

    " >/dev/null 2>&1


    SSH_RESULT=$?


    if [ "$SSH_RESULT" -ne 0 ]; then

        error "$HOST 网络修改任务启动失败"

        INDEX=$((INDEX + 1))

        continue

    fi


    success "$HOST 网络修改任务已启动"


    # --------------------------------------------------------
    # 等待网络切换
    # --------------------------------------------------------

    echo "      等待网络切换..."

    sleep 5


    # --------------------------------------------------------
    # 使用新 IP 检查
    # --------------------------------------------------------

    CHECK_OK=0


    for RETRY in 1 2 3 4 5 6 7 8
    do

        echo -n "      检查 ${NEW_IP} SSH ($RETRY/8)... "


        timeout 5 ssh \
            -o BatchMode=yes \
            -o StrictHostKeyChecking=no \
            -o UserKnownHostsFile=/dev/null \
            -o ConnectTimeout=3 \
            "$SSH_USER@$NEW_IP" \
            "echo OK" >/dev/null 2>&1


        if [ $? -eq 0 ]; then

            echo -e "${GREEN}成功${RESET}"

            CHECK_OK=1

            break

        else

            echo -e "${YELLOW}等待${RESET}"

        fi


        sleep 2

    done


    if [ "$CHECK_OK" -eq 1 ]; then

        success "$HOST -> $NEW_IP IPv4 配置成功"

    else

        error "$HOST -> $NEW_IP IPv4 检查失败"

        warning "请检查:"

        echo "      IP 是否冲突"
        echo "      Gateway 是否正确"
        echo "      ens32 是否存在"
        echo "      NetworkManager 是否正常"
        echo "      /tmp/network-init.log"


        # ----------------------------------------------------
        # 记录失败
        # ----------------------------------------------------

        echo "$HOST -> $NEW_IP" >> "$LOG_DIR/ipv4_failed.log"

    fi


    INDEX=$((INDEX + 1))

done


# ============================================================
# 9. 配置 IPv6
#
# IPv6 使用:
#
# fc00::21/8
# fc00::22/8
# fc00::23/8
#
# 默认:
#
# 只配置 IPv6 地址
# 不配置 IPv6 Gateway
# 不配置 IPv6 DNS
#
# ============================================================

echo
echo "============================================================"
echo "9. 配置内网 IPv6"
echo "============================================================"


INDEX=0


for HOST in $SUCCESS_HOSTS
do

    NEW_IP="${IP_PREFIX}.$((START_IP + INDEX))"

    NEW_IPV6="${IPV6_PREFIX}$((START_IP + INDEX))"


    echo
    info "$HOST"
    echo "      IPv4 : $NEW_IP"
    echo "      IPv6 : ${NEW_IPV6}/${IPV6_MASK}"


    # --------------------------------------------------------
    # 使用新 IPv4 连接
    # --------------------------------------------------------

    timeout 5 ssh \
        -o BatchMode=yes \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout=3 \
        "$SSH_USER@$NEW_IP" \
        "echo IPv4_OK" >/dev/null 2>&1


    if [ $? -ne 0 ]; then

        error "$HOST -> $NEW_IP SSH 不可用,跳过 IPv6"

        INDEX=$((INDEX + 1))

        continue

    fi


    # --------------------------------------------------------
    # 配置 IPv6
    # --------------------------------------------------------

    if [ "$CONFIG_IPV6_GATEWAY" -eq 1 ]; then


        timeout 10 ssh \
            -o BatchMode=yes \
            -o StrictHostKeyChecking=no \
            -o UserKnownHostsFile=/dev/null \
            -o ConnectTimeout="$SSH_TIMEOUT" \
            "$SSH_USER@$NEW_IP" "

            nmcli con mod '$NET_IF' \
                ipv6.addresses '${NEW_IPV6}/${IPV6_MASK}' \
                ipv6.gateway '$IPV6_GATEWAY' \
                ipv6.method manual \
                ipv6.dns '$IPV6_DNS'

            nmcli con up '$NET_IF'

        "


    else


        timeout 10 ssh \
            -o BatchMode=yes \
            -o StrictHostKeyChecking=no \
            -o UserKnownHostsFile=/dev/null \
            -o ConnectTimeout="$SSH_TIMEOUT" \
            "$SSH_USER@$NEW_IP" "

            nmcli con mod '$NET_IF' \
                ipv6.addresses '${NEW_IPV6}/${IPV6_MASK}'

            nmcli con up '$NET_IF'

        "


    fi


    if [ $? -eq 0 ]; then

        success "$HOST IPv6 配置成功"

    else

        warning "$HOST IPv6 配置可能失败"

    fi


    INDEX=$((INDEX + 1))

done


# ============================================================
# 10. 最终检查
# ============================================================

echo
echo "============================================================"
echo "10. 最终网络配置检查"
echo "============================================================"


INDEX=0


for HOST in $SUCCESS_HOSTS
do

    NEW_IP="${IP_PREFIX}.$((START_IP + INDEX))"

    NEW_IPV6="${IPV6_PREFIX}$((START_IP + INDEX))"


    echo
    echo "============================================================"
    echo "服务器"
    echo "旧 IP : $HOST"
    echo "新 IP : $NEW_IP"
    echo "IPv6  : ${NEW_IPV6}/${IPV6_MASK}"
    echo "============================================================"


    timeout 10 ssh \
        -o BatchMode=yes \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout="$SSH_TIMEOUT" \
        "$SSH_USER@$NEW_IP" "

        echo
        echo '--- Machine ID ---'
        cat /etc/machine-id

        echo
        echo '--- Connection ---'
        nmcli con show '$NET_IF'

        echo
        echo '--- IPv4 ---'
        nmcli -g ipv4.addresses,ipv4.gateway,ipv4.dns \
            con show '$NET_IF'

        echo
        echo '--- IPv6 ---'
        nmcli -g ipv6.addresses,ipv6.gateway,ipv6.method \
            con show '$NET_IF'

        echo
        echo '--- Device ---'
        nmcli device show '$NET_IF' |
            grep -E 'GENERAL.STATE|GENERAL.CONNECTION|IP4.ADDRESS|IP4.GATEWAY|IP6.ADDRESS'

    "


    if [ $? -ne 0 ]; then

        warning "$NEW_IP 最终检查失败"

    fi


    INDEX=$((INDEX + 1))

done


# ============================================================
# 11. 输出 IP 对应关系
# ============================================================

echo
echo "============================================================"
echo "最终 IP 对应关系"
echo "============================================================"

printf "%-20s %-20s %-25s %-10s\n" \
    "旧 IP" \
    "新 IPv4" \
    "新 IPv6" \
    "状态"

echo "--------------------------------------------------------------------------"


INDEX=0


for HOST in $SUCCESS_HOSTS
do

    NEW_IP="${IP_PREFIX}.$((START_IP + INDEX))"

    NEW_IPV6="${IPV6_PREFIX}$((START_IP + INDEX))"


    if timeout 5 ssh \
        -o BatchMode=yes \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout=3 \
        "$SSH_USER@$NEW_IP" \
        "echo OK" >/dev/null 2>&1
    then

        STATUS="OK"

    else

        STATUS="FAILED"

    fi


    printf "%-20s %-20s %-25s %-10s\n" \
        "$HOST" \
        "$NEW_IP" \
        "${NEW_IPV6}/${IPV6_MASK}" \
        "$STATUS"


    INDEX=$((INDEX + 1))

done


# ============================================================
# 12. 失败日志
# ============================================================

echo

if [ -f "$LOG_DIR/ipv4_failed.log" ]; then

    echo "============================================================"
    echo "IPv4 配置失败列表"
    echo "============================================================"

    cat "$LOG_DIR/ipv4_failed.log"

    echo
    echo "详细日志:"
    echo "$LOG_DIR/"

else

    success "所有 IPv4 配置检查正常。"

fi


# ============================================================
# 完成
# ============================================================

echo
echo "============================================================"
echo -e "${GREEN}批量网络初始化完成${RESET}"
echo "============================================================"

echo
echo "SSH 登录示例:"

echo

INDEX=0

for HOST in $SUCCESS_HOSTS
do

    NEW_IP="${IP_PREFIX}.$((START_IP + INDEX))"

    echo "ssh root@$NEW_IP"

    INDEX=$((INDEX + 1))

done

echo
echo "日志目录:$LOG_DIR"
echo
0

评论 (0)

取消