NetworkManager 运维教程 / 第 10 章:高级技巧与脚本化
第 10 章:高级技巧与脚本化
10.1 JSON 输出
NM 1.32+ 支持 JSON 格式输出,极大简化了脚本解析。
# JSON 格式输出设备状态
nmcli -t -f DEVICE,TYPE,STATE -o json device status
# [
# {"device":"eth0","type":"ethernet","state":"connected"},
# {"device":"wlan0","type":"wifi","state":"disconnected"},
# {"device":"lo","type":"loopback","state":"unmanaged"}
# ]
# 使用 jq 解析
nmcli -t -f DEVICE,TYPE,STATE -o json device status | jq '.[] | select(.state == "connected")'
# 获取连接信息的 JSON
nmcli -o json connection show "Wired connection 1"
# 获取所有连接的 JSON
nmcli -o json connection show
# 从 JSON 提取特定字段
nmcli -o json connection show "Wired connection 1" | jq '.[0].ipv4.addresses'
# 获取设备信息 JSON
nmcli -o json device show eth0
# 提取 IP 地址
nmcli -o json device show eth0 | jq -r '.[0]["ip4-address"][0].address'
10.2 批量配置脚本
批量创建连接
#!/bin/bash
# batch-create-connections.sh
# 批量创建以太网连接配置
CONFIGS=(
"server-1|eth0|192.168.1.11/24|192.168.1.1|8.8.8.8"
"server-2|eth1|192.168.1.12/24|192.168.1.1|8.8.8.8"
"server-3|eth0|10.0.0.11/24|10.0.0.1|1.1.1.1"
)
for config in "${CONFIGS[@]}"; do
IFS='|' read -r name iface ip gw dns <<< "$config"
# 检查连接是否已存在
if nmcli connection show "$name" &>/dev/null; then
echo "连接 '$name' 已存在,跳过..."
continue
fi
nmcli connection add \
type ethernet \
con-name "$name" \
ifname "$iface" \
ipv4.method manual \
ipv4.addresses "$ip" \
ipv4.gateway "$gw" \
ipv4.dns "$dns" \
connection.autoconnect yes \
ipv6.method disabled
echo "已创建连接: $name ($iface -> $ip)"
done
echo "批量创建完成"
批量修改连接
#!/bin/bash
# batch-modify-connections.sh
# 批量修改所有 DHCP 连接为自定义 DNS
TARGET_DNS="8.8.8.8,1.1.1.1"
nmcli -t -f NAME,TYPE connection show | while IFS=: read -r name type; do
if [ "$type" = "802-3-ethernet" ]; then
current_method=$(nmcli -t -f ipv4.method connection show "$name" | cut -d: -f2)
if [ "$current_method" = "auto" ]; then
echo "修改 '$name' 的 DNS 为 $TARGET_DNS"
nmcli connection modify "$name" ipv4.dns "$TARGET_DNS"
nmcli connection modify "$name" ipv4.ignore-auto-dns yes
fi
fi
done
批量 VLAN 配置
#!/bin/bash
# batch-vlan-setup.sh
# 批量创建 VLAN 接口
PARENT_IF="eth0"
VLANS=(
"100|10.100.0.1/24|10.100.0.254"
"200|10.200.0.1/24|10.200.0.254"
"300|10.300.0.1/24|10.300.0.254"
)
for vlan_config in "${VLANS[@]}"; do
IFS='|' read -r vid ip gw <<< "$vlan_config"
vlan_name="vlan${vid}"
vlan_iface="${PARENT_IF}.${vid}"
if nmcli connection show "$vlan_name" &>/dev/null; then
echo "VLAN $vid 已存在,跳过"
continue
fi
nmcli connection add \
type vlan \
con-name "$vlan_name" \
ifname "$vlan_iface" \
vlan.parent "$PARENT_IF" \
vlan.id "$vid" \
ipv4.method manual \
ipv4.addresses "$ip" \
ipv4.gateway "$gw" \
ipv6.method disabled
echo "已创建 VLAN $vid: $vlan_iface -> $ip"
done
10.3 配置文件直接编辑
虽然推荐使用 nmcli,但有时直接编辑配置文件更高效。
keyfile 格式详解
# /etc/NetworkManager/system-connections/my-server.nmconnection
[connection]
id=my-server # 连接名称
uuid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # UUID
type=ethernet # 连接类型
interface-name=eth0 # 绑定接口
autoconnect=true # 自动连接
autoconnect-priority=10 # 自动连接优先级
timestamp=1685000000 # 最后使用时间戳
[ethernet]
mac-address=AA:BB:CC:DD:EE:FF # 绑定 MAC 地址
mtu=1500 # MTU
auto-negotiate=true # 自动协商
[ipv4]
method=manual # manual / auto / disabled / link-local
address1=192.168.1.100/24,192.168.1.1 # IP/掩码,网关
address2=10.0.0.100/24 # 多个 IP
dns=8.8.8.8;8.8.4.4; # DNS(分号分隔)
dns-search=example.com; # 搜索域
dns-priority=100 # DNS 优先级
dns-options=rotate # DNS 选项
route1=10.0.0.0/8,192.168.1.254 # 静态路由
route2=172.16.0.0/12,192.168.1.254,100 # 路由(带度量值)
never-default=false # 是否为默认路由
ignore-auto-dns=true # 忽略 DHCP DNS
ignore-auto-routes=true # 忽略 DHCP 路由
[ipv6]
method=auto # auto / dhcp / manual / link-local / disabled / ignore
addr-gen-mode=stable-privacy # 地址生成模式
dns=2001:4860:4860::8888; # IPv6 DNS
[proxy]
method=none # none / auto
批量编辑配置文件
#!/bin/bash
# bulk-edit-configs.sh
# 直接编辑 NM 配置文件批量修改
CONFIG_DIR="/etc/NetworkManager/system-connections"
for config_file in "$CONFIG_DIR"/*.nmconnection; do
if [ ! -f "$config_file" ]; then
continue
fi
# 检查是否为以太网连接
if ! grep -q "type=ethernet" "$config_file"; then
continue
fi
# 备份
cp "$config_file" "${config_file}.bak"
# 使用 sed 修改配置
# 例如:为所有以太网连接添加 MTU 9000
if grep -q "\[ethernet\]" "$config_file"; then
if ! grep -q "mtu=" "$config_file"; then
sed -i '/\[ethernet\]/a mtu=9000' "$config_file"
echo "已修改: $(basename "$config_file")"
fi
fi
done
# 重载 NM 配置
nmcli connection reload
echo "配置已重载"
从 ifcfg 格式迁移
#!/bin/bash
# migrate-ifcfg.sh
# 将 ifcfg 格式迁移为 NM keyfile
IFCFG_DIR="/etc/sysconfig/network-scripts"
NM_DIR="/etc/NetworkManager/system-connections"
for ifcfg_file in "$IFCFG_DIR"/ifcfg-*; do
[ -f "$ifcfg_file" ] || continue
iface=$(basename "$ifcfg_file" | sed 's/ifcfg-//')
# 跳过 loopback
[ "$iface" = "lo" ] && continue
echo "迁移: $iface"
# 使用 nmcli 导入
nmcli connection load "$ifcfg_file"
if [ $? -eq 0 ]; then
echo " 成功: $iface"
else
echo " 失败: $iface"
fi
done
10.4 高级 nmcli 技巧
条件查询与过滤
# 获取所有连接的 IP 地址
nmcli -t -f NAME,IP4.ADDRESS connection show
# 获取特定连接的网关
nmcli -t -f IP4.GATEWAY connection show "Wired connection 1" | cut -d: -f2
# 获取所有 WiFi 连接的 SSID
nmcli -t -f NAME,802-11-wireless.ssid connection show | grep -v "^$"
# 获取连接状态变化监控
nmcli monitor | while read -r line; do
echo "$(date): $line"
# 触发自定义操作
done
# 获取连接设备的 MAC 地址
nmcli -t -f GENERAL.HWADDR device show eth0 | cut -d: -f2-
动态配置生成
#!/bin/bash
# auto-configure-vlans.sh
# 根据交换机 VLAN 信息自动配置
# 从外部 API 或配置获取 VLAN 列表
VLANS=$(curl -s https://api.example.com/vlans)
echo "$VLANS" | jq -c '.[]' | while read -r vlan; do
vid=$(echo "$vlan" | jq -r '.id')
subnet=$(echo "$vlan" | jq -r '.subnet')
gw=$(echo "$vlan" | jq -r '.gateway')
name=$(echo "$vlan" | jq -r '.name')
con_name="vlan-${name}"
if nmcli connection show "$con_name" &>/dev/null; then
echo "更新: $con_name"
nmcli connection modify "$con_name" \
ipv4.addresses "${subnet}" \
ipv4.gateway "${gw}"
else
echo "创建: $con_name"
nmcli connection add \
type vlan \
con-name "$con_name" \
ifname "eth0.${vid}" \
vlan.parent eth0 \
vlan.id "$vid" \
ipv4.method manual \
ipv4.addresses "${subnet}" \
ipv4.gateway "${gw}" \
ipv6.method disabled
fi
done
nmcli connection reload
10.5 网络配置备份与恢复
#!/bin/bash
# backup-nm-config.sh
# 备份所有 NM 连接配置
BACKUP_DIR="/backup/networkmanager/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# 备份连接配置文件
cp -a /etc/NetworkManager/system-connections/ "$BACKUP_DIR/"
# 备份主配置
cp -a /etc/NetworkManager/NetworkManager.conf "$BACKUP_DIR/" 2>/dev/null
cp -a /etc/NetworkManager/conf.d/ "$BACKUP_DIR/conf.d/" 2>/dev/null
# 备份 dispatcher 脚本
cp -a /etc/NetworkManager/dispatcher.d/ "$BACKUP_DIR/dispatcher.d/" 2>/dev/null
# 导出所有连接的 nmcli 格式
for conn in $(nmcli -t -f NAME connection show); do
nmcli connection export "$conn" > "$BACKUP_DIR/${conn}.nmconnection" 2>/dev/null
done
echo "备份完成: $BACKUP_DIR"
#!/bin/bash
# restore-nm-config.sh
# 恢复 NM 连接配置
BACKUP_DIR="$1"
if [ -z "$BACKUP_DIR" ] || [ ! -d "$BACKUP_DIR" ]; then
echo "用法: $0 <备份目录>"
exit 1
fi
# 恢复连接配置文件
for config_file in "$BACKUP_DIR"/*.nmconnection; do
[ -f "$config_file" ] || continue
filename=$(basename "$config_file")
echo "导入: $filename"
nmcli connection load "$config_file"
done
# 恢复连接
nmcli connection reload
echo "恢复完成"
echo "请手动激活需要的连接:nmcli connection up <name>"
10.6 监控与告警脚本
#!/bin/bash
# nm-monitor.sh
# 实时监控网络状态变化
LOG_FILE="/var/log/nm-monitor.log"
nmcli monitor | while read -r line; do
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "$timestamp | $line" >> "$LOG_FILE"
# 检测连接丢失
if echo "$line" | grep -q "disconnected"; then
echo "$timestamp | ALERT: Network disconnected!" >> "$LOG_FILE"
# 发送告警
curl -s -X POST "https://hooks.slack.com/..." \
-d "{\"text\": \"⚠️ 网络断开: $line\"}" &
fi
# 检测连接恢复
if echo "$line" | grep -q "connected"; then
echo "$timestamp | INFO: Network connected" >> "$LOG_FILE"
fi
done
10.7 配置模板系统
#!/bin/bash
# apply-network-template.sh
# 应用预定义的网络配置模板
TEMPLATE="$1"
shift
PARAMS=("$@")
case "$TEMPLATE" in
"static-server")
# 用法: apply-network-template.sh static-server eth0 192.168.1.100 192.168.1.1
IFACE=${PARAMS[0]}
IP=${PARAMS[1]}
GW=${PARAMS[2]}
nmcli connection add \
type ethernet \
con-name "server-${IFACE}" \
ifname "$IFACE" \
ipv4.method manual \
ipv4.addresses "${IP}/24" \
ipv4.gateway "$GW" \
ipv4.dns "8.8.8.8,8.8.4.4" \
ipv6.method disabled \
connection.autoconnect yes
;;
"dhcp-client")
IFACE=${PARAMS[0]}
nmcli connection add \
type ethernet \
con-name "dhcp-${IFACE}" \
ifname "$IFACE" \
ipv4.method auto \
ipv6.method auto
;;
"bond-lacp")
IFACES=${PARAMS[0]} # 逗号分隔的接口列表
IP=${PARAMS[1]}
GW=${PARAMS[2]}
nmcli connection add \
type bond \
con-name "bond0" \
ifname bond0 \
bond.options "mode=802.3ad,miimon=100,xmit_hash_policy=layer3+4" \
ipv4.method manual \
ipv4.addresses "${IP}" \
ipv4.gateway "$GW"
IFS=',' read -ra IFACE_ARRAY <<< "$IFACES"
for iface in "${IFACE_ARRAY[@]}"; do
nmcli connection add \
type ethernet \
con-name "bond0-${iface}" \
ifname "$iface" \
master bond0 \
slave-type bond
done
;;
*)
echo "未知模板: $TEMPLATE"
echo "可用模板: static-server, dhcp-client, bond-lacp"
exit 1
;;
esac
echo "模板 '$TEMPLATE' 已应用"
10.8 性能调优
# 1. 禁用不需要的 IPv6
nmcli connection modify "my-conn" ipv6.method disabled
# 2. 设置 MTU
nmcli connection modify "my-conn" ethernet.mtu 9000
# 3. 禁用 DNS 搜索域(减少 DNS 查询延迟)
nmcli connection modify "my-conn" ipv4.dns-search ""
# 4. 禁用连通性检查(服务器场景)
sudo tee /etc/NetworkManager/conf.d/no-connectivity.conf << 'EOF'
[connectivity]
uri=
interval=0
EOF
# 5. 优化 DHCP 超时
nmcli connection modify "my-conn" \
ipv4.dhcp-timeout 30 \
ipv4.dhcp-send-hostname no
# 6. 禁用 DNS 代理
sudo tee /etc/NetworkManager/conf.d/no-dns-proxy.conf << 'EOF'
[main]
dns=none
EOF
10.9 本章小结
| 要点 | 说明 |
|---|---|
| JSON 输出 | nmcli -o json,配合 jq 解析 |
| 批量配置 | 使用 for 循环 + nmcli 命令 |
| 配置文件 | /etc/NetworkManager/system-connections/*.nmconnection |
| 备份恢复 | 导出 nmcli connection export,导入 nmcli connection load |
| 监控 | nmcli monitor 实时监控状态变化 |
| 模板化 | 封装常用配置为脚本模板 |
| 性能 | 禁用 IPv6、连通性检查、优化 DHCP |