为什么要放弃 ifupdown 改用 systemd-networkd
很多从 CentOS 时代过来的站长,习惯了一辈子只用 ifcfg-eth0 和 /etc/network/interfaces。这套配置方式确实简单直观,但它有两个绕不开的硬伤:一是"配了不一定生效",改了文件之后必须手动 ifdown/ifup,中间那几秒服务器会短暂断网,如果是远程 SSH 操作,手一抖就再也连不上了;二是它和现代网络管理栈(NetworkManager、netplan、systemd-networkd)互相打架,谁最后启动谁说了算,导致"我明明改了配置,重启后又变回去了"这种经典疑难杂症。
systemd-networkd 是 systemd 家族里负责网络配置的守护进程。它不是简单的"配置文件换个位置",而是彻底改变了网络配置的模型:它是声明式的,你只描述"我要什么状态",由它负责让系统收敛到这个状态;它是事件驱动的,网卡插拔、DHCP 租约到期、路由变化都会实时响应;它是可组合的,把"配置网络设备"和"配置网络连接"拆成两个独立对象,复用一个连接配置可以套到多个网卡上。
对个人站长来说,systemd-networkd 有三个实际好处:第一,它是随系统启动的,配置改完 networkctl reload 就能平滑生效,不需要 ifdown 那一刀;第二,它天生支持多网卡、网桥、VLAN、Bond,一台 VPS 上做复杂网络拓扑不用装额外工具;第三,它的日志走 journald,排查时 journalctl -u systemd-networkd 一条命令就能把网卡为什么没起来看得清清楚楚。
核心概念:.network、.netdev、.link 三类配置文件
systemd-networkd 的配置全在 /etc/systemd/network/ 目录下,按扩展名分成三类,理解这三类就理解了一切:
- .link 文件——作用于"物理设备本身",按 MAC 地址或网卡名匹配。它的典型用途是给网卡改名(比如把
enp3s0固定改名成wan0)、调整 MTU、开关某个特性。它在网卡一出现时就生效,早于 .network。 - .netdev 文件——用来"创建虚拟网络设备",比如网桥(Bridge)、聚合口(Bond)、VLAN、VXLAN、隧道。只有需要创建虚拟设备时才写,普通物理网卡直接用 .network 即可。
- .network 文件——作用于"网络连接",按网卡名匹配,负责配 IP、网关、DNS、路由。这是最常用的类型。
排序规则很关键:.link 先于 .netdev,.netdev 先于 .network。命名上建议用数字前缀控制优先级,例如 10-wan0.link、20-br0.netdev、30-br0.network。匹配用的是通配符,Match 段里可以写 Name=en*、MACAddress=xx:xx:xx:xx:xx:xx,多个条件之间是"与"关系,满足全部条件才匹配。
实战第一步:用 .network 配置静态 IP
假设我们在 Debian 12 上有一块网卡 ens3,要配成静态 IP。先确认网卡名字和当前状态:
ip -br link
networkctl status ens3然后创建配置文件 /etc/systemd/network/20-ens3.network:
[Match]
Name=ens3
[Network]
Address=203.0.113.10/24
Gateway=203.0.113.1
DNS=1.1.1.1
DNS=8.8.8.8
IPv6AcceptRA=no这里有两个易错点。第一,Address= 后面的掩码是CIDR 前缀(/24),不是传统的 255.255.255.0,写成点分十进制会直接报错。第二,Gateway= 只应在一个 .network 文件里出现,如果在多块网卡上都写了默认网关,systemd-networkd 会按文件名顺序取第一个,另一个静默失效——这是"双网卡服务器默认路由乱跳"的常见根因。
要配 DHCP 则更简单:
[Match]
Name=ens3
[Network]
DHCP=yesIPv4 和 IPv6 可以分开控制,DHCP=ipv4 或 DHCP=ipv6。如果想保留 DHCP 分配的地址同时再叠加几个静态地址,用 DHCP=yes 配合额外的 Address= 行即可,两者是叠加而非互斥关系。
让配置生效:平滑 reload 而不是 ifdown
这是 systemd-networkd 相比 ifupdown 最爽的地方。改完配置后执行:
systemctl restart systemd-networkd注意这里是 restart 而不是 reload。很多人以为网络配置应该用 reload,但 systemd-networkd 的 reload 只是重新读取磁盘上的配置文件、不重新应用到已配置的接口,所以"我 reload 了怎么没变化"就成了高频疑问。真正要让新配置作用于接口,要么 restart 服务(它会重新处理所有接口),要么对单个接口执行:
networkctl reconfigure ens3用 reconfigure 单接口是远程操作时最安全的方式——它只重配指定网卡,其他网卡和现有连接不受影响。即便如此,改 IP 类操作仍建议提前准备一条带外通道(VNC 控制台或云厂商的救援模式),因为一旦新 IP 网段填错,SSH 会话会在几秒后断开且无法恢复。
验证是否生效,用 networkctl 而不是传统 ip addr 就能看到更多信息:
networkctl status ens3
networkctl listnetworkctl list 会列出所有接口及其状态(configured 表示已按 .network 文件配置成功,unmanaged 表示没有被 systemd-networkd 接管)。如果某个网卡显示 unmanaged,说明没有匹配的 .network 文件,这是个非常好用的自检信号。
进阶一:用 .netdev 创建网桥 br0
把多块网卡桥接成一个二层交换域,是自建 KVM 虚拟机、跑 LXC 容器时最常见的需求。systemd-networkd 里分两步走。
第一步创建虚拟网桥设备 /etc/systemd/network/20-br0.netdev:
[NetDev]
Name=br0
Kind=bridge
[Bridge]
STP=no个人服务器上通常关掉 STP(生成树协议),因为只有一个网桥不存在环路,开着 STP 反而会让首次转发延迟几十秒。
第二步创建网桥的连接配置 /etc/systemd/network/30-br0.network,把 IP 配在网桥上而不是物理口上:
[Match]
Name=br0
[Network]
Address=192.168.10.1/24
DNS=192.168.10.1第三步创建物理口的"从属"配置 /etc/systemd/network/30-ens4.network,把它挂到网桥上,注意不要给物理口配 IP:
[Match]
Name=ens4
[Network]
Bridge=br0这里最容易踩的坑是:给物理口和网桥都配了 IP,结果物理口上的 IP 直接可达、网桥上的 IP 又不可达,虚拟机的流量全乱。记住一条铁律:被桥接的物理口只做"线",不配 IP;IP 只配在网桥这个"逻辑交换机"上。
进阶二:netplan——Ubuntu 的抽象层
Ubuntu 从 17.10 开始引入了 netplan,它本身不是网络守护进程,而是一个YAML 格式的配置抽象层,负责把你的意图翻译成 systemd-networkd 或 NetworkManager 能懂的配置。也就是说,Ubuntu 上你写的是 /etc/netplan/xx.yaml,真正干活的还是 systemd-networkd。
一个静态 IP 的 netplan 配置长这样(/etc/netplan/01-netcfg.yaml):
network:
version: 2
renderer: networkd
ethernets:
ens3:
addresses:
- 203.0.113.10/24
routes:
- to: default
via: 203.0.113.1
nameservers:
addresses: [1.1.1.1, 8.8.8.8]写完执行校验和套用:
netplan trynetplan try 会在应用前给你一个 120 秒的回滚窗口——如果改变了 IP 导致 SSH 断开,时间一到它会自动回滚到旧配置。这是 netplan 相比裸改 systemd-networkd 最大的人性化设计,远程改网络一定要先用 netplan try 而不是 netplan apply。
netplan 的 YAML 有两个必须注意的细节:一是缩进必须用空格、绝对不能有 Tab,一个 Tab 就能让整个文件解析失败;二是 routes 段里新版语法用 to: default(旧版是 gateway4),写成旧语法虽有兼容但会打印废弃警告。改完 netplan 后 systemd-networkd 会被自动触发重载,不需要你手动 restart。
真实场景:一次"配置正确却连不上"的排查
我在一台 Debian 12 的 VPS 上做过一次网络迁移:把原来 ifupdown 管的 eth0 换成 systemd-networkd 管的 ens3。配置写好后 networkctl list 显示 configured,ip addr 也能看到正确的 IP,但就是 ping 不通网关。
排查过程是这样的:先看服务日志,journalctl -u systemd-networkd -n 50 显示接口配置成功,没有报错;再看路由,ip route 发现默认路由是有的;接着查 ARP,ip neigh 显示网关的 MAC 地址状态一直是 FAILED。这说明二层就不通,问题不在 IP 层。
最后定位到根因:原来的 ifupdown 配置里有个 mtu 1400 的设定(某些云厂商的 overlay 网络要求),而新的 .network 文件漏了这个参数,接口用了默认 MTU 1500。云厂商的封装网络对超长帧做了丢弃,导致网关 ARP 请求被吞。在 [Link] 段加上 MTUBytes=1400 后立刻恢复。
这个案例的教训是:迁移网络配置时,必须逐条清点旧配置里的每一个非默认参数(MTU、MAC、bond 参数、VLAN ID 等),不能只搬 IP 和网关。systemd-networkd 的默认值很可能和旧配置的默认值不一样,而这些差异平时不显山露水,只在特定网络环境下才暴露。
五个必踩的坑
- 混淆 reload 与 restart:改配置后
reload不生效,要用restart或networkctl reconfigure。这是最高频的疑问。 - 多网卡都写默认网关:只有一个 .network 文件的 Gateway 会生效,其余静默失效。多网卡要用静态路由策略而不是多个默认网关。
- 网桥物理口也配 IP:物理口只做线不配 IP,IP 只配网桥。否则虚拟机和宿主机流量走向混乱。
- netplan 用了 Tab 缩进:YAML 只认空格,一个 Tab 就解析失败,而且报错信息很含糊,肉眼看不出哪里错了。
- 改远程网络不准备带外通道:无论是 networkd 还是 netplan,改 IP 前先确认云控制台 / VNC 能进,且优先用
netplan try或networkctl reconfigure这种带保护的操作。
总结:什么时候该用 systemd-networkd
systemd-networkd 不是"更好的 ifupdown",而是为现代场景设计的网络管理栈。它的价值在复杂拓扑下才充分体现:多网卡、网桥、Bond、VLAN、隧道这些需求,用 systemd-networkd 只需要几个声明式文件,而用 ifupdown 要写一堆 pre-up 脚本还不一定稳妥。
对个人站长而言,一个务实的迁移路径是:新装机器(Debian 12 / Ubuntu 22.04 之后)直接用 systemd-networkd 或 netplan;老机器如果只有一张网卡、一个静态 IP,继续用现有方式也没问题,不必为了迁移而迁移。真正需要迁移的信号是:你开始需要网桥、Bond,或者厌倦了改配置必须 ifdown 掉线的提心吊胆。那时候,systemd-networkd 会给你一个更稳、更可观测、更容易回滚的网络层。