强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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

扩展阅读