如何通过cloud-init覆盖云服务商提供的DNS配置(Ubuntu Netplan环境)
最近我在Hetzner的纯IPv6 Ubuntu实例上遇到了个麻烦:想替换云服务商默认的DNS服务器,但直接修改/etc/netplan/50-cloud-init.yaml里的nameservers字段后,重启机器配置就被cloud-init还原了——毕竟这个文件是cloud-init自动生成的,手动修改根本不持久。
先说说我的环境情况:这台机器是纯IPv6配置,服务商通过cloud-init把所有IPv6网络参数都写死在了Netplan文件里,DHCP4只是开着做本地IPv4 fallback用,没有公网IPv4。
初次尝试:直接在user-data中配置Netplan(失败)
我本来以为,cloud-init会自动合并服务商的vendor-data.yml和我提供的user-data.yml里的网络配置,于是写了这样的user-data.yml:
#cloud-config network: version: 2 ethernets: eth0: nameservers: addresses: - 2a00:1098:2c::1 - 2a00:1098:2b::1 - 2a01:4f9:c010:3f02::1
结果新机器启动后,DNS还是服务商默认的,我的自定义配置完全没生效。看来cloud-init的网络配置合并逻辑不是我想的那样——对于服务商已经明确设置的字段,用户配置不会自动覆盖。
临时解决方案:添加高优先级Netplan文件
没办法,我先搞了个“笨办法”:利用Netplan支持读取/etc/netplan目录下所有yaml文件并合并的特性,通过cloud-init写入一个优先级比50-cloud-init.yaml更高的配置文件(文件名数字更小,比如40-nat64-dns.yaml),然后重启机器让配置生效:
#cloud-config write_files: - path: /etc/netplan/40-nat64-dns.yaml content: | network: version: 2 ethernets: eth0: nameservers: addresses: - 2a00:1098:2c::1 - 2a00:1098:2b::1 - 2a01:4f9:c010:3f02::1 permissions: '0644' runcmd: - reboot
这个方法确实能用,但额外的重启总感觉有点冗余,而且像个hack。
更优方案:直接配置systemd-resolved
后来我找到了更优雅的方式——不用碰Netplan,直接给systemd-resolved添加自定义配置文件,只需要重启resolved服务就行,不用重启整个机器:
#cloud-config write_files: - path: /etc/systemd/resolved.conf.d/dns_servers.conf content: | [Resolve] DNS=2a00:1098:2b::1 2a01:4f9:c010:3f02::1 2a01:4f8:c2c:123f::1 Domains=~. permissions: '0644' runcmd: - systemctl restart systemd-resolved
这里的Domains=~.表示所有域名都用我们指定的DNS解析,确保全局生效。
小结
如果直接在user-data的Netplan配置里覆盖DNS没生效,大概率是cloud-init的合并逻辑不会替换服务商已经明确设置的字段。这时候要么用高优先级Netplan文件(需要重启),要么直接给systemd-resolved加配置(更轻量),后者是更推荐的方案。
备注:内容来源于stack exchange,提问作者Eric Anderson

