解决 PVE 虚拟机复制时 DHCP 地址重复的问题
前几天重新制作了 Debian 12 的基线模版,意外发现 clone 多个虚拟机时,它们的 IP 地址都是相同的,导致无法正常 ssh 登录。
检查 PVE 宿主机的 cloudinit 配置,其 MAC 地址各不相同,IP 都是 DHCP 模式,为什么呢?
systemd-networkd是 Debian 12 的默认网络管理软件,根据 systemd.network官方文档,DHCP 配置有个ClientIdentifier选项,取值范围是[‘mac’, ‘duid’, ‘duid-only’]。
The DHCPv4 client identifier to use. Takes one of “mac”, “duid” or “duid-only”. If set to “mac”, the MAC address of the link is used. If set to “duid”, an RFC4361-compliant Client ID, which is the combination of IAID and DUID (see below), is used. If set to “duid-only”, only DUID is used, this may not be RFC compliant, but some setups may require to use this. Defaults to “duid”.
也就是说,如果设置了 ClientIdentifier=duid ,那么当 DHCPv4 客户端在获取动态 IPv4 地址时, 会向 DHCPv4 服务器发送 DUID(DHCP Unique Identifier) 以及网络接口的 IAID(Identity Association Identifier)。 DHCP 服务器可根据 DUID 与 IAID 来唯一定位主机及其网络接口。
实际上,这次出问题的虚拟机模版克隆了多个 VM 之后,在向 DHCP 服务器申请 IP 地址时,其上报的 Client ID 根本不是 VM 的虚拟化 Mac 地址,而是基于 machine-id 生成的、完全相同的 DUID,因此 DHCP 服务器每次都分配完全相同的 IP 地址,这就问题的根本原因。
一、systemd 的 machine id
machine id 是 Systemd 项目引入的系统标识机制,最早出现在 systemd v197 版本(2012 年),是一个 32 位的 16 进制字符串,用于操作系统安装或启动时的唯一标识。参见 Debian 关于 machine-id 的技术说明。
由于历史原因,machine-id 在 Linux 内部有两种实现,分别为 systemd 的 /etc/machine-id 和 dbus 的 /var/lib/dbus/machine-id,系统命令 systemd-machine-id-setup 和 dbus-uuidgen 各自尝试复制对方的机器 ID,以避免两个文件顺序相反的进程对 machine id 的判断不一致。
1 | root@Copy-of-VM-Debian13-Cloud-Base:~# cat /etc/machine-id |
注意!将 /etc/machine-id 设置为 0 字节的空文件,被认为是清除该文件的规范方法,而不是直接删除,因为如果 systemd 运行在完全只读的根文件系统上,它有代码可以在 tmpfs 上创建机器 ID,并将其绑定挂载到空文件之上。
二、DHCP 的 DUID 和 IAID
关于 DUID、IAID 的定义,在 networkd.conf 官方文档中做了介绍,完全符合 RFC 4361 : Node-specific Client Identifiers for DHCPv4 的技术规范。
传统 DHCPv4 用 Client-ID 标识客户身份,格式是 Tpye(0x00)+ 6字节的 MAC 地址;但多网卡、双栈(IPv4+IPv6)、PXE 多阶段启动时,同一设备会出现多个不同 Client-ID,导致 DHCP 服务器分配不同 IP / 租约,造成身份割裂。
RFC 4361 的核心目标是让 DHCPv4 客户端使用 DHCPv6 定义的 DUID 作为唯一标识,为此新定义了 DUID-based Client-ID 替代 Client-ID ,用于标识设备 + 网卡,全局唯一且跨协议一致。
1 | Client-ID (Option 61) = Type (1字节) + IAID (4字节) + DUID (变长) |
Type:固定为 0xFFIAID(Interface Association ID):,4 字节,用于区分同一设备的不同网卡(同一设备多网卡用相同 DUID、不同 IAID)。DUID(DHCP Unique ID):≥ 6 字节,基于RFC 3315定义的 DHCPv6 全局唯一标识符,设备生成后永久保存。

对客户端行为规范的核心要求是:
- DHCPv4 客户端:
必须生成 / 使用 DUID,并持久化存储(重启、重装系统不变)。
同一设备的所有网卡共用同一个 DUID,但每个网卡用不同 IAID。
发送 DHCPv4 报文时,Option 61 必须用 255 + IAID + DUID 格式。
双栈设备(同时跑 DHCPv4/6)必须共用同一个 DUID,无需人工配置。 - DHCPv6 客户端:
应提供查看 / 配置 DUID 的接口。
与 DHCPv4 共用 DUID,保证身份统一。 - 阶段网络启动(PXE)
第一阶段(如 PXE ROM)与后续阶段(如 OS 内 DHCP 客户端)应使用相同 DUID,避免身份切换导致租约冲突。 - DHCP 服务器:
支持识别 Type=255 的 Client-ID,按 DUID+IAID 做身份绑定,而非仅 MAC。
对同一 DUID、不同 IAID 的请求,视为同一设备的不同网卡,分配不同 IP 但保持配置一致性。
兼容传统 Type=0(MAC)的 Client-ID,保证向后兼容。
1 | root@Copy-of-VM-Debian12-cloudinit-v2:/var/lib/cloud/instances# networkctl status eth0 |
一句话总结:RFC 4361 是 DHCPv4 的身份标准化补丁:用 DHCPv6 的 DUID 做全局唯一 ID,搭配 IAID 区分网卡,让设备在 IPv4/IPv6、多网卡、多启动阶段都保持同一个网络身份,解决租约与配置的一致性难题。
三、解决方案
解决办法是修改 Debian 12 的基线模版,在最后清理阶段增加如下步骤:
- 清空 machine-id 配置:后续克隆后的 VM 启动时,系统将自动生成全新的、不一样的 machine-id,DHCP 服务器就可以区分不同 VM 了
- 清空 cloud-init 日志:这是保险起见,避免遗留的日志信息干扰 VM 启动
- 基线模版直接关机!!!并转为模版避免修改
1 | rm -f /etc/machine-id /var/lib/dbus/machine-id |
附录:DHCP 服务响应信息查询
nmap(Network Mapper)是 Debian 官方 main 仓库中的网络扫描与审计工具包,核心功能是:
- 端口扫描(TCP/UDP)、服务版本探测、操作系统指纹识别;
- 网络拓扑发现、存活主机检测、漏洞扫描(需配合 NSE 脚本);
- 支持 IPv4/IPv6,兼容各类网络协议(DHCP、DNS、HTTP 等)。
检查 DHCP 服务器相应信息,可以使用命令:nmap --script broadcast-dhcp-discover
这个会直接列出所有给你分配地址的 DHCP 服务器 IP,如果出现多个 server-identifier 就是多 DHCP 冲突。
1 | root@Copy-of-VM-Debian12-cloudinit-v2:/var/lib/systemd# nmap --script broadcast-dhcp-discover |