麒麟 arm64 架构虚拟机无法连接网络这个问题我在过去两年里被问过不下二十次场景几乎一模一样宿主机是 Apple Silicon 或者一台 arm64 服务器用 QEMU、libvirt 或者某个图形化虚拟机管理器起了一台麒麟系统的 arm64 虚拟机系统装完了、桌面也进来了结果浏览器打不开、终端里 ping 不通、软件源更新直接超时。很多人第一反应是虚拟机没网然后就开始改 DNS、关防火墙、重装系统折腾一圈发现问题还在。实际上这类故障九成以上不在网络配置写错了这一层而是卡在更下面的地方网卡设备压根没被客户机识别、驱动模块没打进 initramfs、虚拟网络后端的转发规则没生效或者用户模式网络的地址段和你想的完全不是一回事。这篇内容面向的是需要在 arm64 平台上跑麒麟虚拟机、并且必须让它联上外网或内网的人包括做国产化适配的开发者、做离线环境验证的运维、以及在自己笔记本上搭测试环境的个人。我会按先定性、再分层、最后给可复制的配置这个顺序讲从虚拟化层的网络模型差异一直讲到客户机里的 NetworkManager 和 netplan中间穿插地址规划的计算过程、可以直接抄的命令和 XML 片段还有几个我踩过之后再也不愿意踩第二遍的坑。读完你应该能做到拿到一台不通网的麒麟 arm64 虚拟机十分钟内判断出问题出在哪一层并且有对应的修复手段。1. 先把问题定性网络不通到底断在哪一层1.1 三类典型现象与它们各自指向的故障域我习惯先让提问的人描述现象因为现象本身就带着答案。第一类是客户机里根本看不到网卡ip a列出来只有 lo或者只有 lo 加一个 down 状态的接口。这类问题几乎百分之百在虚拟化层的设备模型上要么创建虚拟机时选的网卡型号客户机内核没有对应驱动要么内核镜像里没编进 virtio 相关模块要么 UEFI 固件没把 PCIe 设备枚举出来。这时候你去客户机里改任何网络配置文件都是白费力气因为配置文件绑定的设备名压根不存在。第二类是网卡在、但没有 IP 地址。ip a能看到 enp0s1 或者 eth0状态 UP但没有 inet 那一行。这个故障域在地址获取环节DHCP 请求发出去了没人应答或者 NetworkManager 的连接配置是ipv4.method manual但没填地址或者 netplan 的 renderer 写成了 systemd-networkd 而系统实际只跑着 NetworkManager两套服务互相看不见对方的配置。还有一种隐蔽情况是 DHCP 服务正常但客户机发出的 discover 报文被宿主机的防火墙规则丢掉了表现和没有 DHCP 服务器完全一样。第三类是有 IP、能 ping 通网关、但上不了外网。这类最容易误判。IP 有了网关 ping 得通说明二层和三层前半段都是好的问题在转发或者名字解析。要么宿主机没开net.ipv4.ip_forward要么 NAT 的 masquerade 规则没匹配上要么客户机的/etc/resolv.conf指向了一个不可达的 DNS。我见过最离谱的一次是客户机同时跑了 systemd-resolvedresolv.conf是个指向127.0.0.53的软链接而 resolved 服务因为缺配置文件起不来于是所有域名解析全部超时但 IP 直连完全正常——用户一直以为是网络时断时续。注意判断有没有网不要只用 ping。用户模式网络slirp在某些版本上对 ICMP 的支持是不完整的会出现 ping 不通但 TCP 完全正常的情况。用curl -I或者nc -zv测一个 TCP 端口结论比 ping 可靠得多。1.2 一条从宿主到客户机的分段验证主线我给自己的排查流程定了一条固定主线从上往下走每走一步都要拿到明确结论才往下。第一步在宿主机上确认虚拟网络后端是活的virsh net-list --all看 default 网络是不是 activeip a show virbr0看网桥有没有起来、有没有 192.168.122.1 这个地址。如果 virbr0 都不存在后面全是空谈。第二步确认后端到外网的转发能力sysctl net.ipv4.ip_forward必须是 1nft list ruleset | grep masq或者iptables -t nat -L POSTROUTING -n要能看到针对 192.168.122.0/24 的 masquerade。这一步不过虚拟机就算拿到地址也出不去。第三步进客户机确认设备层ls /sys/class/net看接口列表dmesg | grep -i -E virtio|eth|enp看内核有没有认到设备、有没有报错ethtool -i enp0s1看驱动名。这一步能过滤掉一大半问题。第四步看地址和路由ip -4 a、ip route、ip neigh。ARP 表里网关那条如果是FAILED或者一直INCOMPLETE说明二层不通回去查网桥和 MAC 过滤别在路由上浪费时间。第五步才是 DNS 和应用层cat /etc/resolv.conf、resolvectl status如果装了 resolved、curl -v http://某个内网地址。这条主线走完问题基本就被压缩到一个很窄的范围里了。2. 虚拟化层arm64 上的网络模型和 x86 到底差在哪2.1 NAT、桥接、仅主机三种模式在 arm64 下的实际表现从概念上说arm64 和 x86 的三种网络模式没有区别NAT 是虚拟机走宿主机的地址出去桥接是虚拟机直接挂在宿主机所在的物理网段上仅主机是只能和宿主机通信。但实际用起来差异不小。NAT 模式在 arm64 上是最稳的因为它不依赖物理网卡的任何特性libvirt 起一个 virbr0dnsmasq 发地址内核做 SNAT整条链路都在软件层完成跟 CPU 架构没关系。桥接模式在 arm64 上翻车的概率明显更高尤其是在笔记本上。原因很现实桥接要求物理网卡进入混杂模式而很多无线网卡驱动根本不支持把无线接口加进网桥强行加进去的结果是网桥起来了、虚拟机也连上了但所有报文都出不去。苹果 Silicon 的机器上这个问题尤其突出因为 macOS 侧的桥接实现vmnet有自己的一套逻辑和 Linux 上的 br0 完全不是一回事。我在 arm64 服务器上做桥接从来没出过问题在笔记本上基本放弃桥接一律走 NAT 加端口转发。仅主机模式libvirt 里的 isolated 网络用得少但很有用典型场景是做纯离线验证或者要模拟一个和外界隔离的内网环境。它的坑在于很多人忘了它本来就不该通外网然后花半天时间排查为什么上不了网。模式依赖条件arm64 上的稳定性典型适用场景NATvirbr0宿主机 ip_forward masquerade高与架构无关日常开发、装软件、拉依赖桥接br0物理网卡支持混杂模式有线高无线低虚拟机需被同网段其他机器访问仅主机无高离线验证、隔离测试用户模式slirp无QEMU 内置高但功能受限快速起一台临时机器、无 root 权限2.2 网卡型号选择virtio-net 与 e1000 的取舍这是 arm64 上最容易出错的一个点。x86 上大家习惯性选 e1000因为几乎所有系统都内置了这个驱动兼容性最好。但在 arm64 的麒麟系统上e1000 的驱动模块不一定编进内核尤其是一些精简过的镜像。结果就是设备在 PCIe 总线上但内核找不到驱动接口不出现你在客户机里怎么查都查不到。virtio-net 是半虚拟化设备性能比模拟的 e1000 好一个数量级而且麒麟的内核通常会带 virtio 系列模块。所以我的默认选择永远是 virtio除非有明确理由不用。但 virtio 也有前提内核得能加载virtio_pci、virtio_net、virtio_ring这几个模块。如果系统是从一个通用镜像装出来的initramfs 里可能只打了存储相关的 virtio 模块为了挂根分区网络模块被漏掉了表现就是根分区挂载正常、系统能起来、但网卡没有。libvirt 的 XML 里对应这么一段model typevirtio就是关键interface typenetwork mac address52:54:00:3a:7c:1e/ source networkdefault/ model typevirtio/ /interface如果换了 virtio 还是不行可以退回model typee1000e试一次但要注意 e1000e 在 arm64 客户机上的表现不如 x86 稳定有时候会出现链路状态反复 up/down。真要用模拟网卡rtl8139在 arm64 上基本不用考虑驱动缺失的概率更高。MAC 地址那一行不是必须的但建议显式写上。QEMU 默认生成的 MAC 前缀是52:54:00第一字节 0x52 的第二个比特是 1表示这是一个本地管理地址不会和真实厂商的地址冲突。自己指定的时候保持这个前缀后三字节随便填但要保证同一宿主机上不重复否则两个虚拟机会抢同一个 IP表现是网络时通时断非常难查。2.3 UEFI 固件与 initramfs 里的驱动缺口arm64 虚拟机没有传统 BIOS启动走的是 UEFIQEMU 上一般用 EDK2 的QNAA64.fd或者edk2-aarch64-code.fd作为固件。这个固件本身只负责枚举 PCIe 设备、找到可引导设备、把控制权交给内核它不负责给内核提供网卡驱动。所以固件里能看到网卡和系统里能用网卡是两件独立的事很多人看到 UEFI 启动界面里列出了 virtio-net 设备就以为万事大吉结果进系统后照样没接口。真正的缺口在 initramfs。麒麟系统的 initramfs 是在安装时根据当时的硬件环境生成的如果安装过程中用的是某种网卡模型生成出来的 initramfs 就只带那种模型的驱动。后续你把虚拟机配置改成另一种网卡内核在挂载根分区之前不会去加载新驱动网络自然起不来。修复方式是在客户机里重新生成 initramfs把 virtio_net 显式加进去。以 dracut 为例编辑/etc/dracut.conf.d/virtio.confadd_drivers virtio_pci virtio_net virtio_ring virtio_blk 然后dracut -f --regenerate-all重建。这一步做完重启接口才会稳定出现。这个操作我在至少五台环境上验证过属于必须知道但文档里很少写的那一类。还有一种更彻底的办法在 QEMU 启动参数里直接用-kernel指定内核和 initrd绕过固件的引导流程。这种做法调试方便但要自己写全root和console参数写错一个就是内核 panic反而增加排查成本。我一般只在做内核级调试时这么干。3. 客户机侧网卡、地址、路由、解析逐层修复3.1 网卡没出现的排查顺序进到客户机第一件事是ls /sys/class/net。如果只有 lo先别急着改配置去看dmesg | grep -i -E virtio|pci|net。常见输出有三种一种是完全没有相关日志说明设备压根没被枚举问题在虚拟化层一种是有virtio-pci 0000:00:01.0: enabling device但没有后续的virtio_net注册说明 PCIe 设备在、驱动没绑上还有一种是明确报Failed to load module virtio_net或者Unknown symbol那是模块依赖问题。第二种情况多半是模块没装。用modprobe virtio_net手动加载一次如果成功且接口立刻出现说明只是没自动加载把这个模块写进/etc/modules-load.d/就能持久化。如果modprobe报Module not found那就是内核里没有这个模块得从内核包或者kernel-modules-extra之类的附加包里找。arm64 的麒麟系统有些版本把驱动拆得比较细主内核包只带最基础的virtio 网络模块在额外的包里。第三种情况是模块加载失败常见原因是内核版本和模块版本不匹配比如系统升级了内核但没重建 initramfs或者装了两个内核版本uname -r和/lib/modules/下的目录对不上。这种情况depmod -a加重建 initramfs 基本能解决。提示lspci在 arm64 客户机里通常能看到 virtio 设备但如果系统里没装 pciutils用ls /sys/bus/pci/devices/加cat /sys/bus/pci/devices/*/vendor也能达到同样目的。vendor 是0x1af4就是 virtio 设备0x8086是 Intel 模拟网卡。3.2 地址获取失败NetworkManager、netplan 与 ifcfg 的三方混战麒麟系统在不同版本上用了不同的网络管理栈有的用 NetworkManager有的用 netplan 前端加 systemd-networkd 后端有的还保留着传统的 ifcfg 脚本。最麻烦的情况是几套同时存在/etc/netplan/*.yaml里写了一份配置/etc/NetworkManager/system-connections/里有一份连接/etc/sysconfig/network-scripts/ifcfg-*里还有一份三份互相打架最终生效的往往是最后启动的那个服务。判断当前谁在管事用nmcli general status看 NetworkManager 状态用systemctl status systemd-networkd看另一套。如果两个都 active先停掉不用的那个systemctl disable --now systemd-networkd。这一步不做后面所有配置都可能被覆盖。确认管理器之后配置 DHCP 就很简单了。NetworkManager 下nmcli con add type ethernet ifname enp0s1 con-name kylin-dhcp ipv4.method auto nmcli con up kylin-dhcp nmcli con show kylin-dhcp | grep ipv4netplan 下/etc/netplan/01-netcfg.yaml里写network: version: 2 renderer: NetworkManager ethernets: enp0s1: dhcp4: true dhcp4-overrides: use-dns: true注意renderer那一行写NetworkManager就交给 NM 渲染写networkd就交给 systemd-networkd写错了就是配置不生效的经典原因。改完执行netplan apply如果报 yaml 语法错误用netplan generate单独校验。传统 ifcfg 方式下/etc/sysconfig/network-scripts/ifcfg-enp0s1至少要有这几行TYPEEthernet BOOTPROTOdhcp NAMEenp0s1 DEVICEenp0s1 ONBOOTyesONBOOTno是高频坑点系统装完之后网卡默认不激活手动ifup enp0s1能起来重启又没了。另一个坑是DEVICE和实际接口名不一致arm64 机器上的接口名不一定是 eth0virtio 设备通常命名成enp0s1、enp0s2这种基于 PCIe 槽位的名字配置文件里写死 eth0 就绑不上。3.3 路由与 DNS 的收尾配置地址拿到之后ip route应该至少有一条default via 192.168.122.1 dev enp0s1。如果没有默认路由说明 DHCP 服务器没下发 option 3或者 NetworkManager 忽略了它。手动补一条ip route add default via 192.168.122.1可以临时验证但要持久化还得回到配置文件里加gateway4netplan或者ipv4.gatewaynmcli。DNS 是另一个高频故障点。/etc/resolv.conf在现代麒麟系统上通常是个软链接指向../run/systemd/resolve/stub-resolv.conf或者/run/NetworkManager/resolv.conf。如果 systemd-resolved 服务没起来这个软链接就成了死链所有解析超时。判断方法很简单ls -l /etc/resolv.conf看指向哪儿systemctl status systemd-resolved看服务状态resolvectl status看有没有上游服务器。如果确认是 resolved 的问题两条路一是把服务修好systemctl enable --now systemd-resolved然后resolvectl dns enp0s1 192.168.122.1二是干脆绕开删掉软链接写一份静态的/etc/resolv.conf内容就一行nameserver 192.168.122.1。第二种做法简单粗暴但有效缺点是 NetworkManager 重启时可能把文件覆盖回去需要在连接配置里关掉ipv4.dns-priority相关的自动管理或者直接chattr i /etc/resolv.conf锁住文件——锁之前想清楚忘了这事儿以后改 DNS 会怀疑人生。还有一个跟架构相关但容易被忽略的点IPv6。arm64 系统默认开启 IPv6客户机会自动生成一个 link-local 地址并且尝试走 IPv6 出去。如果宿主机侧没有 IPv6 通路某些应用尤其是走 getaddrinfo 的会先等 IPv6 超时再回退到 IPv4表现出来就是网络很慢但能通。症状是curl前几秒卡住之后突然就快了。解决办法是在客户机里关掉 IPv6 或者调整优先级sysctl -w net.ipv6.conf.all.disable_ipv61写进/etc/sysctl.d/持久化。3.4 安全策略与防火墙的拦截麒麟系统带安全增强模块默认策略下可能会拦截一些网络行为。最典型的是 DHCP 客户端发出的 raw socket 报文被策略挡住表现是dhclient一直显示DHCPDISCOVER on enp0s1但收不到 OFFER。用ausearch -m avc -ts recent或者journalctl -t audit能看到对应的拒绝记录。临时验证可以setenforce 0之后重试如果立刻拿到地址就基本确认是策略问题然后针对 dhclient 加一条允许规则而不是长期关掉整个模块。防火墙这一层要分两侧看。客户机侧firewalld 如果启用接口默认可能落在publiczone 里而publiczone 默认只放行 ssh 和 dhcpv6-client出方向的流量不受影响所以一般不会导致完全上不了网但如果有人在 zone 里配了 rich rule 或者做了接口绑定错误就可能出问题。检查方式是firewall-cmd --get-active-zones确认 enp0s1 在哪个 zonefirewall-cmd --zonezone --list-all看规则。宿主机侧的防火墙才是更常见的拦截点。libvirt 在创建 default 网络时会自动插入 masquerade 规则和 forward 规则但如果宿主机上跑着 firewalld 或者 ufw这些规则可能被清掉或者在重启后丢失。表现是虚拟机刚起来时能上网宿主机重启一次之后就再也不通了。检查方法nft list ruleset | grep -A5 masq看有没有针对 virbr0 网段的 SNATiptables -t nat -L -n看 POSTROUTING 链。缺了就手动补或者让 libvirt 重新加载网络定义virsh net-destroy default virsh net-start default。4. 实操从零配一台能上网的 arm64 麒麟虚拟机4.1 网段与地址规划把参数算清楚再动手动手之前先把地址规划做完这一步省不得。libvirt 默认网络用的是192.168.122.0/24如果你只有一台宿主机、一套虚拟网络直接用默认的就行不用改。但如果你同时跑了好几套虚拟化环境比如宿主机上还有容器网络、还有另一套测试用的虚拟网络网段冲突会导致路由混乱症状是虚拟机偶尔能通偶尔不能通非常难查。规划逻辑是这样/24掩码下可用主机地址是 254 个2^8 - 2去掉网络地址和广播地址。约定俗成的分配方式是网关占.1DHCP 动态池从.2开始到.199静态地址留给.200到.254。这样动态分配和手工指定的地址不会打架。如果你需要更多地址把掩码改成/16可用地址变成 65534 个但广播域也会变大在纯软件虚拟网络里无所谓在桥接到物理网络的环境里要谨慎。端口转发要单独规划。NAT 模式下外部访问虚拟机只能靠宿主机做端口映射比如宿主机的 2222 转发到虚拟机的 22。规划时要注意宿主机上 1024 以下的端口需要 root 权限才能绑定用 2222、8080、8443 这种高位端口最省事同一台宿主机上多个虚拟机做转发时宿主机侧的端口不能重复但虚拟机侧的端口可以重复转发的目标地址要写虚拟机在 NAT 网段里的地址不是 127.0.0.1。用户模式网络QEMU 的-netdev user的地址段是固定的跟 libvirt 的 NAT 完全不是一回事这一点必须记牢客户机网段10.0.2.0/24网关10.0.2.2DNS 代理10.0.2.3DHCP 服务由 QEMU 内置提供地址从10.0.2.15开始分配。你要是按 192.168.122.x 去配静态地址那肯定不通因为那个网段在用户模式里根本不存在。参数libvirt NAT 默认值QEMU 用户模式备注客户机网段192.168.122.0/2410.0.2.0/24两者不可混用网关192.168.122.110.0.2.2用户模式网关是虚拟的DNS由 dnsmasq 下发10.0.2.3用户模式由 QEMU 代理DHCP 池.2 - .254 可配.15 起自动分配libvirt 可在 XML 里改范围外部入站需端口转发需 hostfwd两者配置方式不同4.2 libvirt 默认网络的配置与端口转发默认网络的定义文件在/usr/share/libvirt/networks/default.xml用virsh net-edit default可以直接改。一份带自定义 DHCP 范围和静态绑定的配置长这样network namedefault/name forward modenat/ bridge namevirbr0 stpon delay0/ ip address192.168.122.1 netmask255.255.255.0 dhcp range start192.168.122.10 end192.168.122.199/ host mac52:54:00:3a:7c:1e namekylin-arm64 ip192.168.122.50/ /dhcp /ip /networkhost那条是把 MAC 和固定 IP 绑起来适合需要稳定地址又不方便在客户机里配静态 IP 的场景。改完执行virsh net-destroy default加virsh net-start default或者virsh net-define之后重启网络。注意net-destroy只是停掉网络设备不会删虚拟机但会把正在用的虚拟机的网卡断开操作前确认一下。端口转发用virsh原生命令做不了得靠 iptables 或者 nftables 手工加 DNAT 规则。比如把宿主机的 2222 映射到 192.168.122.50 的 22 端口iptables -t nat -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination 192.168.122.50:22 iptables -A FORWARD -p tcp -d 192.168.122.50 --dport 22 -j ACCEPT第二条 FORWARD 规则别漏漏了就是能连上但立刻断开或者连接超时的经典症状。这套规则重启就没了要持久化得写进 firewalld 的 direct 规则或者单独存一份脚本挂到 systemd 上。如果你用的是 QEMU 命令行直接起虚拟机端口转发在-netdev参数里写qemu-system-aarch64 \ -M virt,accelkvm \ -cpu host \ -smp 4 -m 4096 \ -bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd \ -drive ifvirtio,filekylin-arm64.qcow2,formatqcow2 \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device virtio-net-pci,netdevnet0 \ -nographicaccelkvm在 Linux arm64 服务器上用苹果 Silicon 上换成accelhvf。hostfwdtcp::2222-:22的意思是宿主机所有地址的 2222 映射到客户机 22如果想只监听本地就写成hostfwdtcp:127.0.0.1:2222-:22。这套配置不需要 root 权限临时起一台机器验证东西非常方便。4.3 桥接方案的配置与踩坑点只有在虚拟机必须被同网段其他物理机直接访问、并且端口转发满足不了需求时才考虑桥接。Linux 宿主机上配一个 br0ip link add name br0 type bridge ip link set br0 up ip link set enp3s0 master br0 ip addr del 192.168.1.100/24 dev enp3s0 ip addr add 192.168.1.100/24 dev br0 ip route add default via 192.168.1.1这套命令有个致命问题你在远程 SSH 里执行到ip addr del那一行时连接就断了后面的命令根本没机会执行。正确做法是写成脚本一次性执行或者用nohup加延时。更稳妥的方式是用发行版自带的网络配置工具netplan、NetworkManager来定义网桥让它们负责迁移地址避免手工操作把自己踢下线。无线网卡桥接基本可以放弃。大多数无线驱动不支持 4 地址模式把 wlan0 加进网桥之后网桥看起来是 UP 的brctl show也能看到接口但报文转发不了。少数驱动支持但需要关闭加密或者用特定的接入点模式实际可用性很低。碰到必须桥接又要用无线的场景我一般建议换方案要么加一个 USB 有线网卡要么老老实实用 NAT 加端口转发。桥接还有一个细节MAC 地址过滤。有些交换机或者无线接入点会做 MAC 学习虚拟机的 MAC 不在白名单里就会被丢包。表现是虚拟机 ARP 发出去收不到回应但抓包能看到报文确实发出去了。这种情况在受管控的办公网里很常见排查时不要只盯着宿主机看。4.4 用抓包把问题钉死所有软件层面的配置都检查完还不通就上抓包。这是最后手段也是最快的手段。在宿主机上用tcpdump -i virbr0 -nn port 67 or port 68看 DHCP 交互正常流程是客户机发 DISCOVER源 0.0.0.0:68 目标 255.255.255.255:67dnsmasq 回 OFFER客户机发 REQUESTdnsmasq 回 ACK。缺哪一步就说明问题在哪。只见 DISCOVER 不见 OFFER说明 dnsmasq 没收到或者没处理。检查systemctl status libvirtd和journalctl -u libvirtd里 dnsmasq 子进程的状态看租约文件/var/lib/libvirt/dnsmasq/default.leases有没有内容。dnsmasq 起不来最常见的原因是端口被占用——宿主机上跑了另一个 dnsmasq 或者某个服务占了 53 端口。见到 OFFER 但客户机不回 REQUEST多半是客户机侧的问题策略拦截、多网卡场景下选错了接口、或者防火墙把入站 UDP 68 挡了。地址都拿到了但 ping 网关不通抓tcpdump -i virbr0 -nn arp看 ARP 请求有没有回应。如果看到请求但没回应用bridge fdb show br virbr0查 MAC 表看客户机 MAC 有没有学到。学不到就是网桥配置问题学到了但通信不了就查 iptables 的 FORWARD 链默认策略很多时候默认 DROP 而没加放行规则所有跨网桥流量全被丢。5. 常见问题速查与避坑清单5.1 高频故障速查表下面这张表是我这些年被问得最多的问题按现象归类可以直接对着查。现象最可能的原因快速验证处理方式客户机只有 lo无其他接口网卡型号驱动缺失dmesg | grep -i virtio无输出改用 virtio 模型或补装驱动模块接口存在但一直 DOWN网桥未启用或链路未建立ip link show看 state检查 virbr0 状态重启虚拟网络有接口DHCP 超时宿主机防火墙拦截或 dnsmasq 未启动宿主抓包看有无 OFFER补防火墙规则重启 libvirtdDHCP 成功但无法出网ip_forward 关闭或 masquerade 缺失sysctl net.ipv4.ip_forward开启转发补 NAT 规则能 ping IP域名解析失败resolv.conf 失效或 DNS 不可达cat /etc/resolv.conf修 resolved 或写静态 DNS重启后网络配置被还原多套网络管理器冲突看 NM 与 networkd 状态禁用不用的那一套静态 IP 配置后完全不通用户模式网络下用了 NAT 网段确认启动参数改用 10.0.2.0/24 网段网络时通时断MAC 地址冲突比对同宿主机虚拟机 MAC重新分配唯一 MAC首次访问很慢后恢复正常IPv6 超时回退curl -6测试关闭 IPv6 或调优先级这张表不是穷举但它覆盖了我在实际环境里遇到问题的绝大部分。用的时候注意一点不要看到现象就往表里套先按第 1 章那条主线走一遍拿到确定的分层结论再查表否则容易误判。5.2 几个用真金白银换来的避坑经验第一个坑不要在虚拟机和宿主机之间来回改配置。我见过太多人一发现不通宿主机上改一下网卡模型客户机里改一下 IP再回去改 DHCP 范围改到最后自己都不记得哪一版是能用的了。正确做法是改一项、验证一项、记录一项改之前先把当前配置备份出来。虚拟机的 XML 用virsh dumpxml name backup.xml存一份客户机的网络配置文件cp -a一份出问题能立刻回退。第二个坑麒麟系统的防火墙和服务默认状态在不同镜像上不一样。有的镜像装完 firewalld 是关的有的是开的有的默认 zone 绑了网卡有的没绑。所以脚本化部署的时候不要假设默认值显式写清楚systemctl is-enabled firewalld的结果再决定动作。这一点在批量部署时尤其重要一台机器上验证通过的脚本换一台镜像版本不同的机器可能就卡在防火墙上。第三个坑时间不同步会导致一些看起来跟网络无关的故障。虚拟机的时钟如果和宿主机差太多某些认证、证书校验会失败表现出来像网络连上了但认证不过。用timedatectl看一眼差超过几分钟就先同步时间再排查网络能省掉很多弯路。QEMU 上建议加上-rtc baseutc,clockhost让客户机跟随宿主机时钟。第四个坑不要迷信重装系统能解决。网络不通这类问题重装能解决的场景只有一种就是安装过程中生成的 initramfs 缺驱动而你又不知道怎么补。但重装之后如果还是同样的网卡模型、同样的虚拟网络配置问题大概率原样复现。先把根因定位出来比重装十次都管用。5.3 我现在固定保留的几条习惯养成的第一个习惯是新建 arm64 虚拟机时先在 XML 里把网卡模型锁死成 virtio同时把 MAC 显式写出来不用自动生成。这样做的直接好处是出问题时能快速判断是不是设备层的原因而且固定 MAC 之后 DHCP 分配的地址也比较稳定调试时不用每次都去查新 IP。第二个习惯是每台麒麟虚拟机装完之后立刻做一次网络自检三件套ls /sys/class/net确认接口、ip route确认默认路由、curl -I一个内网地址确认 TCP 通。这三条命令加起来不到五秒但能把大部分隐患在装完系统的那一刻就暴露出来而不是等到装了一堆软件之后才发现出不去。第三个习惯是给每台虚拟机留一个串口或者控制台通道。网络断了的时候SSH 进不去图形界面又慢串口控制台是最可靠的后路。QEMU 加-serial mon:stdio或者 libvirt 里配一个console设备成本很低关键时刻能救命。我个人的体会是这套东西平时看着多余真到网络全断、机器起不来的时候它就是唯一能让你进去看dmesg的入口。