Ubuntu 服务器运维:实现自动配置网络
Ubuntu 系统中,默认通过 Netplan 工具管理网络接口。通常我们只需要手动写好 /etc/netplan/ 目录下的 YAML 文件,一劳永逸地让系统在每次启动时应用相同配置。然而,有些场景下可能需要一个脚本能:
- 
自动检测当前服务器是否已经是一个可用的静态 IP;如果网络畅通,就无需改动;  - 
如果网络不可用,则尝试对服务器所有网卡进行DHCP 获取 IP,并检测能否访问外网(例如 Ping 目标 IP 或域名);  - 
一旦找到能连通外网的网卡,就固定(静态)它当前的 IP/Gateway/DNS;  - 
如果所有网卡都无法获取可用网络,退出并提示失败。  
类似逻辑多用于初次部署或自动化安装场景。有以下风险需了解:
- 
如果只有单块网卡且依赖 SSH 远程,IP 变更后 SSH 会话极有可能断开;  - 
脚本会移除所有原先的 Netplan 文件后再写新的配置,过程里没有“netplan try”的回滚;  - 
如果脚本未能找出可用网卡并成功配置,系统最终会没有 Netplan 配置文件,需要人工从备份里恢复。  
二、脚本逻辑概述
最终版本脚本(详见后文)包含以下步骤:
- 
STEP A:判断是否已有可用静态网络 - 
如果系统内没有任何网卡显示为“dynamic”,一般表示所有网卡要么是静态,要么未配置 IP。  - 
脚本尝试 ping -c 3 baidu.com(或其他可达地址);若能联通,则说明已有可用的静态 IP 配置,脚本直接退出,不做改动。 - 
如果 Ping 不通,则继续执行后续步骤。  
 - 
 - 
STEP 0:备份并清空 Netplan 文件 - 
将 /etc/netplan/*.yaml文件备份到/var/backups/netplan-时间戳; - 
然后 rm -f /etc/netplan/*.yaml,保证后续生成的配置不与旧文件冲突。 
 - 
 - 
STEP 1 ~ STEP 2:枚举所有网卡,检测/强制设置 DHCP - 
脚本列出除 lo以外的所有网卡(ip link show),依次检查: - 
若成功,可马上把它转成静态配置;若失败(未分配到 IP,或无法通外网),就删掉临时文件,继续下一个网卡。  
- 
如果网卡已经是 “dynamic”(DHCP 模式),那么直接获取其当前 IP / 网关 / DNS,然后转为静态;  - 
否则,脚本临时写入一个只含 dhcp4: yes的 netplan 文件,netplan apply后等待几秒,看能否成功分配 IP 并Ping 通目标。 
 - 
 - 
STEP 3:将获取到的 IP 配置写回静态 - 
用 ip -4 addr show dev NIC拿到 IP(带 CIDR),ip route拿到网关,/etc/resolv.conf里拿到 DNS; - 
写进 /etc/netplan/99-auto-network.yaml,其中将gateway4替换为用routes:设置默认路由,并将dhcp4改为no; - 
nameservers部分默认会追加8.8.8.8和114.114.114.114,如果只想用这两个,可把脚本里追加的逻辑改为覆盖; - 
netplan apply使之生效,然后做一次 Ping 测试,观察连通性。 
 - 
 - 
若所有网卡都尝试完仍失败 - 
脚本会退出,并不会留下任何新的 netplan 文件;此时需要从前面备份的目录手动复制恢复。  
 - 
 
三、常见注意事项
- 
SSH 连接风险 
如果脚本在改动当前使用的网卡 IP 段,SSH 会立刻断开,需要物理/带外或多网卡支持才能继续操作。 - 
netplan apply 无回滚 
脚本使用netplan apply而非netplan try,一旦出现配置不通,将无法自动回退。
所以要么有冗余的访问方式,要么在测试环境充分验证。 - 
DNS 逻辑 - 
脚本默认将 DHCP 下发的 DNS 与 8.8.8.8、114.114.114.114一起写入静态配置; - 
如果你只想保留那两个公共 DNS,请把相关代码改成直接覆盖。  
 - 
 - 
DNS 或外网 - 
脚本主要通过 ping baidu.com判断外网连通。如果你所在网络访问百度不通,请改成ping 114.114.114.114或其他更适合的目标。 
 - 
 - 
一次性或开机自启 - 
大多数情况下,这种自动脚本用于一次性的网络初始化,不必在每次开机都重复执行;  - 
如果确实要开机时自动跑,可做一个 systemd service 或放进 cron @reboot(但要注意网络依赖顺序)。  
 - 
 
四、脚本示例
下文为最终整合的脚本示例:auto_dhcp_then_static.sh。其中包含:
- 
前置检查:如果没有网卡处于 DHCP(dynamic),且 ping测试外网成功,说明已有可用静态网络,脚本立即退出。 - 
否则,清空 netplan 文件并循环尝试各网卡: - 
若网卡已是 DHCP,则转为静态;  - 
否则临时改为 DHCP,看能否上网;  - 
一旦成功,就“锁定”成静态 IP 并停止脚本。  - 
如果所有网卡都失败,则提示错误并退出。  
 - 
 
请将其以 UTF-8 编码保存为 auto_dhcp_then_static.sh 并给予可执行权限:
#!/usr/bin/env bash
# -*- coding: utf-8 -*-
#
# auto_dhcp_then_static.sh
#
# 功能:
#   1) 如果当前已有可用静态网络(无dynamic网卡,且能ping外网),则直接退出
#   2) 否则备份并删除 /etc/netplan/*.yaml
#   3) 枚举所有网卡(excl. lo),若网卡是dynamic则直接转静态,否则强制DHCP测试
#   4) 找到能ping通外网的网卡后,将其固定IP
#   5) 若所有网卡都失败,退出不做最终保存
#
# 注意:
#   - netplan apply 无回滚,可能导致SSH断开
#   - 默认会追加 8.8.8.8、114.114.114.114 到 DNS
#   - ping目标默认为 baidu.com,请根据需要修改
set -e  # 一旦出错立即退出
CONFIG_FILE="/etc/netplan/99-auto-network.yaml"
PING_TARGET="baidu.com"  # 或换成 114.114.114.114
###############################################################################
# STEP A:判断是否已有可用静态网络
###############################################################################
echo ">>> 检查是否已有可用的静态IP和网络连通..."
HAS_DYNAMIC=false
ALL_NICS=$(ip -o link show | awk -F': ' '{print $2}' | grep -v '^lo$')
for nic in $ALL_NICS; do
  if ip -4 addr show dev "$nic" | grep -q "dynamic"; then
    HAS_DYNAMIC=true
    break
  fi
done
# 无dynamic网卡 -> 可能全是静态或没IP -> 测试能否ping通
if [ "$HAS_DYNAMIC" = false ]; then
  echo "系统内未检测到dynamic网卡,可能已有静态IP或无IP。测试ping: $PING_TARGET"
  if ping -c 3 "$PING_TARGET" &> /dev/null; then
    echo ">>> 当前网络已可达,视为静态网络可用,无需修改。退出。"
    exit 0
  else
    echo ">>> 无法ping通,继续脚本流程..."
  fi
else
  echo ">>> 存在dynamic网卡,可能需要转换,继续执行..."
fi
###############################################################################
# STEP 0:备份并删除旧的 netplan 配置
###############################################################################
timestamp=$(date +%Y%m%d-%H%M%S)
backup_path="/var/backups/netplan-${timestamp}"
echo ">>> 备份 /etc/netplan 到: $backup_path"
mkdir -p "$backup_path"
cp -r /etc/netplan/* "$backup_path"/ 2>/dev/null || true
echo ">>> 备份完成。"
echo ">>> 删除现有 /etc/netplan/*.yaml"
rm -f /etc/netplan/*.yaml
echo ">>> 已删除旧的 netplan 文件。"
echo
###############################################################################
# 列出非 lo 的网卡
###############################################################################
NICS=$(ip -o link show | awk -F': ' '{print $2}' | grep -v '^lo$')
if [[ -z "$NICS" ]]; then
  echo "没有可用网卡(除lo外)!退出。"
  exit 0
fi
###############################################################################
# 函数:尝试给网卡启用 DHCP 并测试连通
###############################################################################
try_dhcp_and_test() {
  local nic="$1"
  echo ">>> 对网卡 $nic 写入临时 DHCP 配置..."
  cat <<EOF > "$CONFIG_FILE"
network:
  version: 2
  renderer: networkd
  ethernets:
    $nic:
      dhcp4: yes
EOF
  chmod 600 "$CONFIG_FILE"
  echo ">>> netplan apply (DHCP) on $nic..."
  netplan apply
  echo ">>> 等待5秒,让DHCP有机会拿到IP..."
  sleep 5
  local ip_cidr
  ip_cidr=$(ip -4 addr show dev "$nic" | grep -oP '(?<=inet\s)\S+')
  if [[ -z "$ip_cidr" ]]; then
    echo ">>> DHCP模式下,$nic 未获取到IP。"
    return 1
  fi
  echo ">>> 测试ping: $PING_TARGET"
  if ping -c 3 "$PING_TARGET" &> /dev/null; then
    echo ">>> 网卡 $nic 已可连通 $PING_TARGET。"
    return 0
  else
    echo ">>> 虽拿到IP,但无法ping通 $PING_TARGET。"
    return 2
  fi
}
###############################################################################
# 函数:将当前 DHCP 下发的IP/Gateway/DNS 转为静态
###############################################################################
make_static_config() {
  local nic="$1"
  echo ">>> 读取 $nic 的IP/Gateway/DNS,准备写入静态配置..."
  local ip_cidr
  ip_cidr=$(ip -4 addr show dev "$nic" | grep -oP '(?<=inet\s)\S+')
  local gateway
  gateway=$(ip route | grep -E "^default .* $nic" | awk '{print $3}')
  local dns_list
  dns_list=$(grep -E '^nameserver' /etc/resolv.conf | awk '{print $2}' | xargs echo)
  dns_list="$dns_list 8.8.8.8 114.114.114.114"
  # 拼成 YAML 列表
  local dns_yaml="["
  local first=1
  for dns in $dns_list; do
    if [[ $first -eq 1 ]]; then
      dns_yaml="$dns_yaml$dns"
      first=0
    else
      dns_yaml="$dns_yaml, $dns"
    fi
  done
  dns_yaml="$dns_yaml]"
  echo ">>> 生成最终静态配置 $CONFIG_FILE"
  cat <<EOF > "$CONFIG_FILE"
network:
  version: 2
  renderer: networkd
  ethernets:
    $nic:
      addresses:
        - $ip_cidr
EOF
  if [[ -n "$gateway" ]]; then
    cat <<EOF >> "$CONFIG_FILE"
      routes:
        - to: default
          via: $gateway
EOF
  fi
  cat <<EOF >> "$CONFIG_FILE"
      nameservers:
        addresses: $dns_yaml
      dhcp4: no
EOF
  chmod 600 "$CONFIG_FILE"
  echo ">>> netplan apply (static) on $nic..."
  netplan apply
  echo ">>> 再次测试ping: $PING_TARGET"
  if ping -c 3 "$PING_TARGET" &> /dev/null; then
    echo ">>> $nic (静态) 可成功访问 $PING_TARGET。"
  else
    echo ">>> 警告:$nic (静态) 依然无法ping通 $PING_TARGET。"
  fi
}
###############################################################################
# 主循环:对每个网卡检查
###############################################################################
found_working_nic=false
for nic in $NICS; do
  echo "=== 检查网卡: $nic ==="
  # 如果网卡是 DHCP (dynamic),直接转静态
  if ip -4 addr show dev "$nic" | grep -q "dynamic"; then
    echo ">>> 网卡 $nic 已是 DHCP 模式,直接转换为静态..."
    make_static_config "$nic"
    found_working_nic=true
    break
  else
    # 否则尝试强制 DHCP
    if try_dhcp_and_test "$nic"; then
      echo ">>> 成功拿到IP并连通外网,现在转为静态..."
      make_static_config "$nic"
      found_working_nic=true
      break
    else
      echo ">>> $nic DHCP 尝试失败或无法连接外网,继续下一个网卡..."
      rm -f "$CONFIG_FILE"
    fi
  fi
done
if [ "$found_working_nic" = false ]; then
  echo
  echo "没有任何网卡成功配置DHCP并连通外网,退出,不做最终保存。"
fi
echo
echo "===== [脚本结束] ====="
五、总结
该脚本是一个“先判断是否已有可用静态网络,若否再进行DHCP→静态”的综合示例。主要功能与注意事项包括:
- 
如果脚本检测到已有静态网络并可访问外网,则不修改任何配置;  - 
备份后清空 /etc/netplan/,避免旧文件干扰; - 
逐一尝试网卡: - 
若已是 DHCP,直接将其当前分配信息转静态;  - 
否则强制启用 DHCP,能连通就转静态,不能就尝试下一个网卡;  
 - 
 - 
最终只固定找到的首个可用网卡。若无可用网卡,全局退出、不写任何新的 netplan 文件;  - 
DNS 默认追加 8.8.8.8与114.114.114.114,可按需改成只使用这两个; - 
警惕 SSH 断开、无 netplan try 回滚等风险,确保有可回退或带外访问方式。  
在实际生产环境,通常只需在安装或首配时运行此脚本一次,一旦拿到稳定 IP 并静态化,后续就可无需重复执行。
来源:DevOpsAI
        THE END
    
        
        






