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