麒麟系统装在ARM64虚拟机里,界面能起来、桌面也正常,唯独网络那一栏永远是个红叉或者灰点——这个场景我遇到过不止一次,帮同事看、帮项目现场看,前前后后踩了七八回坑,每次的表面现象都差不多,真正的病因却分布在完全不同的层。有人以为是虚拟机软件没配好,有人怀疑是驱动缺了,还有人直接重装系统,结果重装完照样没网。这篇就把这套问题的排查路径完整摊开讲:从怎么判断是"网卡没被识别"还是"网卡识别了但拿不到地址",到ARM64虚拟化环境下virtio网卡的特殊性,再到NetworkManager接管权、DHCP客户端、路由DNS、MTU与校验和这些容易忽略的边角。适合正在用麒麟系统做ARM64虚拟化验证的开发、运维和适配人员看,也适合刚接触国产化环境、被这个问题卡住半天的新手——照着走一遍,基本能定位到自己那一层。
1. 先给网络故障分层:从"网卡不存在"到"能ping通却打不开页面"
网络不通这四个字太笼统了,不同层级的表现看起来一样,排查入口却完全不同。我习惯在动手改任何配置之前,先用两三条命令把故障归到某一个层,避免在错误的层上瞎折腾。这一步花不了五分钟,但能省掉后面一个小时。
1.1 三类现象对应三个完全不同的排查方向
第一类是系统里根本看不到网卡设备。执行ip link只看到lo一个回环接口,连eth0、ens33这种名字都没有。这种情况问题在虚拟化层或驱动层,跟系统内部的网络配置一点关系都没有,你去改ifcfg文件、去重启 NetworkManager 全是白费力气。
第二类是网卡在,但没有IP地址。ip addr能看到enp0s1之类的接口,状态是UP或者DOWN,但inet那一行是空的。这说明驱动和硬件识别都没问题,卡在了地址获取环节——要么 DHCP 没跑起来,要么 NetworkManager 没接管这个设备,要么虚拟机的网络模式压根没提供 DHCP 服务。
第三类是地址有了,但访问不了外部。ip addr显示192.168.122.50/24,ping网关通,ping外网IP通,但浏览器打不开网页,或者ping域名直接报"未知的主机"。这时候问题在路由、DNS 或者上层协议,网卡和地址都是好的。
这三类的排查路径几乎没有重叠。我见过太多人一上来就systemctl restart NetworkManager,重启十遍也没用,因为他的问题其实是第一类——虚拟机根本没把网卡设备透给系统。
1.2 一条组合命令完成初步分层
不想记太多命令的话,开局先跑这一条:
ip -br link; echo "---"; ip -br addr; echo "---"; nmcli device statusip -br是 brief 模式,输出紧凑,一眼能看全。三段输出分别回答三个问题:有哪些链路层设备、每个设备有没有IP、NetworkManager 怎么看待这些设备。
判断逻辑很直接:
| 输出特征 | 故障层级 | 下一步方向 |
|---|---|---|
只有lo,无其他接口 | 设备未识别 | 查虚拟机网卡型号、查内核驱动 |
有接口但nmcli显示unmanaged | 管理权未接管 | 查 NetworkManager 配置文件 |
有接口、nmcli显示disconnected | 未激活或无DHCP | 查虚拟机网络模式、手动激活 |
有接口有IP,nmcli显示connected | 上层配置问题 | 查路由、DNS、防火墙 |
nmcli device status这一列特别关键。很多人只看ip addr有地址就以为万事大吉,实际上地址可能是手工配的临时地址,NetworkManager 并没有接管,重启就没了,而且 DNS 也不会自动写入。
我印象比较深的一次是某项目现场,同事反馈麒麟虚拟机"网络时好时坏"。跑完上面这条命令发现,网卡处于unmanaged状态,但系统里残留了一个手工写的静态地址,所以有时候能通。这种半吊子状态最坑人,因为现象不稳定,容易误判成"网络抖动"。
2. ARM64虚拟化环境里,网卡到底有没有被内核认出来
ARM64 平台和 x86 在设备枚举上有一处根本差异:x86 靠 PCI 总线和 ACPI 枚举设备,ARM64 除了 PCIe 之外还可能是设备树(Device Tree)或者 ACPI 的混合模式。虚拟化软件如果没把这一层配好,网卡设备就压根不会出现在系统里,表现为第一类故障。
2.1 从内核日志确认virtio-net驱动是否加载
ARM64 虚拟机里最推荐的网卡型号是virtio-net,它由半虚拟化驱动配合,性能好、兼容性稳。先确认驱动模块在不在:
lsmod | grep -i virtio正常应该能看到virtio_net、virtio_pci、virtio_ring、virtio这一组。如果virtio_net缺席,说明模块没加载或者内核里没编进去。
再往下挖一层,看内核在启动时有没有发现设备:
dmesg | grep -iE "virtio|eth|net"健康的输出里会有一串类似virtio_net virtio0 enp0s1: renamed from eth0的记录。看到renamed from eth0就说明设备已经认到了,只是被 udev 改了名字,这属于正常现象。如果dmesg里压根没有任何 virtio 网络设备的痕迹,那问题在更下面——虚拟机的 XML 配置或者图形界面里,网卡设备可能根本没挂上去。
lspci在 ARM64 上同样可用,前提是虚拟化平台暴露了 PCIe 总线:
lspci -nn | grep -i -E "ethernet|network"能列出设备就说明 PCIe 枚举成功,列不出来就得回头查虚拟机配置。这里有个细节:部分 ARM64 虚拟化方案(尤其是一些轻量级方案)默认给的是e1000这种 x86 时代的模拟网卡,在 ARM64 上要么不被支持,要么驱动缺失,表现就是设备列表空空如也。把网卡型号改成virtio基本能一次解决。
2.2 网卡命名规则变化带来的认知偏差
麒麟系统用的是 systemd 的可预测网卡命名规则,网卡名可能是enp0s1、ens3、enx加 MAC 地址,不再是你熟悉的eth0。这件事本身不构成故障,但会让排查走偏——很多人ifconfig没输出就断定"没网卡",其实ifconfig在新系统里可能压根没装,得用ip addr。
ifconfig属于 net-tools 包,麒麟的某些最小化安装镜像默认不带。我遇到过同事在终端敲ifconfig报 command not found,转头就说"系统没网卡",其实ip addr一跑全都在。
如果你确实想让网卡名回到eth0这种老样子(比如有脚本依赖这个名字),可以在内核启动参数里加net.ifnames=0 biosdevname=0,然后重建 initramfs。不过我的建议是别改,顺着新命名规则走,脚本里用ip -o link动态取名字更稳妥。改命名规则这事,改完当时能用,下次内核升级或者换虚拟机又得重新折腾,得不偿失。
2.3 设备树与ACPI路径下的枚举差异
这一点偏底层,但值得说清楚,因为它是 ARM64 特有的坑。ARM64 虚拟机启动时,固件(通常是 UEFI/EDK2)会向内核提供一份硬件描述,形式可能是 ACPI 表,也可能是设备树。如果虚拟机配置里既没提供 ACPI,设备树又写得残缺,内核就枚举不到 PCIe 上的网卡设备。
判断方法是看内核启动日志里关于 ACPI 的部分:
dmesg | grep -i "ACPI:.*PCI"或者直接看/proc/device-tree目录存不存在:
ls /proc/device-tree 2>/dev/null | head有内容说明走的是设备树路径。这种情况下,如果网卡没出现,多半是虚拟机配置文件里device tree或者ACPI开关没开。主流虚拟化平台默认都会配好,只有自己手写 QEMU 命令行的时候容易漏。
我踩过一次:手工拼qemu-system-aarch64命令启动,参数里忘了加-machine virt,结果系统能起来但所有 PCIe 设备都没有,网卡自然也没有。加上这个参数就好了。所以如果你是自己写启动脚本的,先检查 machine 类型。
3. NetworkManager与旧式网络脚本抢方向盘:地址为什么拿不到
设备认出来了,网卡也在,但就是拿不到地址——这是最高频的一类。麒麟系统内部同时存在多套网络管理机制:NetworkManager、传统的 network-scripts、systemd-networkd,甚至 cloud-init 也会插一手。几套机制互相覆盖配置,结果就是谁都没能把地址配上。
3.1 查看设备是否处于unmanaged状态
nmcli device status输出里,如果STATE列是unmanaged,说明 NetworkManager 明确拒绝管理这个设备。原因通常藏在配置文件里:
grep -r "managed" /etc/NetworkManager/NetworkManager.conf /etc/NetworkManager/conf.d/ 2>/dev/null如果看到managed=false,那就是全局关闭了设备管理。改成true或者直接删掉这一行,然后重启服务:
systemctl restart NetworkManager nmcli device status还有一种情况是某个设备被单独排除。/etc/NetworkManager/conf.d/下可能有类似这样的片段:
[keyfile] unmanaged-devices=interface-name:enp0s1这种配置一般是为了让某个网卡交给别的工具管理(比如网桥场景),但如果是历史遗留,就会导致新装的虚拟机网卡一直被晾着。
unmanaged状态特别容易误判。因为设备本身是好的,链路层也是 UP 的,只是没人给它配地址。你手工ip addr add加一个地址也能通,但重启就没了,而且 DNS 不会配。所以看到unmanaged一定要去配置里找原因,别用手工命令糊弄过去。
3.2 DHCP客户端缺失或不工作的典型表现
设备被接管了,状态显示disconnected或者一直在connecting循环,多半是 DHCP 拿不到租约。先看虚拟机侧的网络模式:NAT 模式一般自带 DHCP 服务,桥接模式则要求外部网络里有 DHCP 服务器,仅主机模式需要虚拟化平台自己起 DHCP。
确认虚拟机侧没问题之后,再看系统侧。麒麟系统默认用 NetworkManager 内置的 DHCP 客户端,但有些精简镜像里dhclient或dhcpcd包没装,会报类似dhcp4 (enp0s1): activation: failed for connection的错误。查看方式:
journalctl -u NetworkManager -n 100 --no-pager日志里如果出现DHCP client failed或者dhcp4: request timed out,基本就是这一层。补齐客户端:
rpm -q dhclient || yum install -y dhclient装完再激活连接:
nmcli connection up "有线连接 1"这里有个小坑:连接名称可能不是中文的"有线连接 1",用nmcli connection show看准确的 NAME 列。中文名称在命令行里需要加引号,实在嫌麻烦可以重命名成英文:
nmcli connection modify "有线连接 1" connection.id eth-dhcp3.3 手工配静态地址:一份可以直接抄的完整流程
DHCP 这条路实在走不通,或者虚拟机所在网络本来就没有 DHCP,那就配静态地址。我倾向于用nmcli而不是直接改文件,因为nmcli写的是 keyfile 格式,NetworkManager 会正确加载,DNS 也会一起管好。
以 NAT 网络192.168.122.0/24、网关192.168.122.1为例:
nmcli connection add type ethernet ifname enp0s1 con-name eth-static \ ipv4.method manual \ ipv4.addresses 192.168.122.50/24 \ ipv4.gateway 192.168.122.1 \ ipv4.dns "192.168.122.1,223.5.5.5"几条要点:
ifname必须和ip link里看到的接口名完全一致,写错了连不上。ipv4.method manual少了这一项,地址配了也会被 DHCP 覆盖。- DNS 用逗号分隔,第一个是内网 DNS,第二个是公网兜底。
- 配完立刻
nmcli connection up eth-static,再用ip route确认默认路由出来了。
验证三步走:
ip route show default # 应该有 default via 192.168.122.1 ping -c 3 192.168.122.1 # 通网关 ping -c 3 223.5.5.5 # 通外网IP三层都通,网络层就打通了。剩下的 DNS 解析和上层协议另说。
4. 地址有了却不通:路由、DNS、MTU这三关逐一过
到这一步,ip addr里地址有了,nmcli显示connected,但用户还是说"上不了网"。这时候问题往往藏在三个地方:默认路由缺失、DNS 解析失败、MTU 不匹配。这三关的症状有微妙差别,分清楚能少走很多弯路。
4.1 默认网关缺失与路由表异常
看路由表:
ip route健康的输出里应该有一行default via 192.168.122.1 dev enp0s1。没有这一行,说明网关没配上,表现是能 ping 同网段设备、但访问任何外网地址都超时。
这种情况在用ip addr add手工配地址时特别常见——ip addr只管地址,不管路由。正确做法是配完地址再补一条:
ip route add default via 192.168.122.1 dev enp0s1不过手工路由重启就丢,长期方案还是回到nmcli的ipv4.gateway参数。
还有一种更隐蔽的:路由表里有默认路由,但指向了错误的接口。比如系统里同时存在两张网卡,一张连 NAT 一张连仅主机网络,默认路由指到了仅主机那张上,外网自然不通。用ip route get 223.5.5.5可以精确看到某个目标地址会走哪个接口:
ip route get 223.5.5.5输出里的dev字段就是实际出口。如果和目标预期不符,调整路由优先级或者删掉多余的路由。
多网卡场景下,
ip route会显示多条default,靠metric值决定优先级,数值小的优先。调整方式是在nmcli里设ipv4.route-metric。
4.2 DNS解析失败的隐蔽原因
能 ping 通外网 IP,但ping www.example.com报"未知的主机",那就是 DNS 层。先看当前用的 DNS:
cat /etc/resolv.conf麒麟系统里这个文件可能是软链接,指向/run/NetworkManager/resolv.conf或者/run/systemd/resolve/resolv.conf,具体指向哪个取决于哪套服务在管。如果里面有nameserver 127.0.0.53,说明是 systemd-resolved 接管了 DNS,得再查一层:
resolvectl status如果 NetworkManager 和 systemd-resolved 同时在跑,很容易互相覆盖,导致/etc/resolv.conf里的内容一会儿有一会儿没有。我的处理方式是二选一:要么停掉 systemd-resolved,让 NetworkManager 直接写文件;要么让 NetworkManager 把 DNS 推给 systemd-resolved。前者简单粗暴:
systemctl disable --now systemd-resolved rm -f /etc/resolv.conf systemctl restart NetworkManager另一种常见原因是/etc/resolv.conf被设成了不可修改(chattr +i),NetworkManager 写不进去。用lsattr /etc/resolv.conf看一下,如果有i属性:
chattr -i /etc/resolv.conf这个属性一般是安全加固脚本加的,加完就忘了,后面改 DNS 一直不生效,排查起来很费时间。
4.3 虚拟化环境特有的MTU与校验和问题
这一类属于"能 ping 通小包、大包就卡死"的典型症状。表现是ping网关正常,ping外网IP也正常,但一打开网页、一拉大文件就超时或者卡在加载中。原因通常是 MTU 不匹配或者校验和卸载在虚拟化环境里出了问题。
MTU 排查:
ip link show enp0s1 | grep mtu ping -M do -s 1472 223.5.5.5-M do表示禁止分片,-s 1472加上 IP 头 20 字节和 ICMP 头 8 字节正好是 1500。如果这个包通不了,但-s 1400能通,说明链路上实际可用的 MTU 小于 1500,常见于叠加了隧道或者多层虚拟化的环境。调整:
nmcli connection modify eth-static 802-3-ethernet.mtu 1400 nmcli connection up eth-static校验和问题则表现为抓包看数据都发出去了,但对端收不到或者校验失败。检查卸载状态:
ethtool -k enp0s1 | grep -E "tx-checksum|rx-checksum"在 virtio 网卡上,校验和卸载一般是开启的,正常不需要动。但在一些嵌套虚拟化或者老旧虚拟化平台上,这个特性会导致丢包。临时关闭验证:
ethtool -K enp0s1 tx off rx off如果关掉之后正常了,说明就是这个问题,可以写进 NetworkManager 的 dispatcher 脚本里持久化,或者干脆换一个虚拟化平台的版本。
5. 虚拟机网络模式选型:NAT、桥接、仅主机在ARM64上的差异
很多"连不上网"的问题,根子不在系统里,而在虚拟机网络模式选错了,或者模式对了但配套服务没起来。选型这一步做对,后面能省掉一半的排查工作。
5.1 三种模式的适用边界与配置要点
| 模式 | 地址来源 | 外网可达 | 宿主机可达 | 外部设备可达虚拟机 | 适用场景 |
|---|---|---|---|---|---|
| NAT | 虚拟化平台内置DHCP | 是 | 是 | 否(需端口转发) | 单机验证、日常开发 |
| 桥接 | 外部网络DHCP或静态 | 是 | 是 | 是 | 需要被外部访问的服务 |
| 仅主机 | 虚拟化平台内置DHCP | 否 | 是 | 否 | 隔离测试、多机组网 |
NAT 模式最省事,虚拟机启动就能拿到地址,网关一般是平台分配的固定地址。以常见的 libvirt 默认网络为例,网段是192.168.122.0/24,网关.1,DHCP 范围从.2开始。如果nmcli一直拿不到地址,先确认宿主机上这套 NAT 网络是不是启动了:
virsh net-list --all状态不是active的话:
virsh net-start default virsh net-autostart default桥接模式在 ARM64 上要特别注意物理网卡是否支持。有些平台在 ARM64 上创建桥接时,用的是虚拟网桥而不是真的把物理网卡桥出去,导致虚拟机能拿到地址但访问不了外部网络。验证方法是看虚拟机的 MAC 地址是否出现在外部网络的 ARP 表里。
仅主机模式最容易踩的坑是它默认不带 NAT 转发,虚拟机之间能通、宿主机能通,但出不去。如果只是做几台虚拟机之间的组网测试,这个模式最干净;如果误以为它能上网,就会一直困惑"为什么网关能 ping 通但外网不通"。
5.2 麒麟系统自带安全机制对网络行为的干扰
麒麟系统带了一些安全增强组件,默认状态下可能会对网络行为做一些限制。这类限制平时不显山露水,但在虚拟机环境里排查网络问题时容易被误认为是配置错误。
排查思路是先确认这些组件的当前状态,再判断是否与故障相关。如果系统里有对应的状态查询命令,先跑一遍看输出。如果确实处于启用状态且策略较严格,可以临时调整到宽松模式做一次对照测试——如果网络立刻恢复,就说明是策略在起作用,这时候应该针对性放行而不是长期关闭。
需要强调的是,不要一上来就关安全组件。先做对照测试确认因果关系,再有针对性地加白名单,这是更负责任的做法。我见过有人为了"快速解决",把安全策略全关了,结果系统上线后出了别的问题,回头一查才发现当初的网络故障跟安全策略压根没关系,白关了。
另一个容易忽略的点是防火墙。麒麟系统默认可能启用 firewalld,systemctl status firewalld看状态。如果是active,先看规则:
firewall-cmd --list-all出站流量默认是放行的,所以单纯"上不了网"很少是防火墙导致的。但如果是在做端口转发、NAT 共享这类场景,防火墙就需要显式放行。诊断的时候可以临时停掉做对照:
systemctl stop firewalld确认恢复后记得重新启动,并且用firewall-cmd --permanent加上正确的规则,而不是长期关闭。
6. 一次完整排障的链路复盘:从现象到修复的每一跳
前面讲的都是分层的知识点,这一节把一次真实的排查过程完整走一遍,让整个链路串起来。你会发现真正花时间的不是修,而是定位。
6.1 故障现象与最初的错误假设
场景是这样:某项目现场,一台 ARM64 虚拟机装好麒麟系统,桌面正常,任务栏网络图标显示未连接。同事的第一反应是"虚拟机网络没配",于是在虚拟化平台上把网络模式从 NAT 改成桥接,又改回 NAT,来回折腾了半小时,没有任何变化。
第二个假设是"系统没装网卡驱动",准备重新装系统。这时候我接手看了一下,先跑ip -br link,输出是:
lo UNKNOWN 00:00:00:00:00:00 enp0s1 UP 52:54:00:xx:xx:xx网卡明明在,而且链路是 UP 的。所以第一个假设和第二假设都不成立,问题在地址获取这一层。这一条命令就把排查范围从"整个系统"缩小到了"NetworkManager 和 DHCP"。
6.2 逐层验证与最终定位
接着跑nmcli device status:
DEVICE TYPE STATE CONNECTION enp0s1 ethernet disconnected -- lo loopback unmanaged --disconnected说明设备被 NetworkManager 识别了,但没有激活任何连接配置。再看有没有现成的连接:
nmcli connection show输出是空的,一个连接配置都没有。这就解释了为什么disconnected——系统里压根没有为这张网卡创建过连接配置文件。
正常情况下,NetworkManager 检测到新网卡会自动创建一个 DHCP 连接。自动创建没发生,说明要么是被配置禁用了,要么是创建失败了。查配置:
grep -r "no-auto-default" /etc/NetworkManager/果然,/etc/NetworkManager/NetworkManager.conf里有这么一行:
[main] no-auto-default=*这行的意思是"任何设备都不要自动创建默认连接"。这个配置一般是批量部署时为了统一管理而加的,但如果部署脚本没配套地把连接配置写进去,新机器就成了有网卡没配置的状态。
修复很直接,两种方式任选:删掉这一行后重启服务让它自动创建,或者手工创建一个 DHCP 连接。我选了后者,更可控:
nmcli connection add type ethernet ifname enp0s1 con-name eth-dhcp ipv4.method auto nmcli connection up eth-dhcp执行完再ip addr show enp0s1,地址192.168.122.87/24出来了,网关和 DNS 也一起配好了。ping网关通,ping外网通,浏览器正常打开。
整个过程从接手到修好不到十分钟,其中八分钟在确认现象,改配置只花了三十秒。
6.3 复盘时容易被忽略的三个细节
第一个细节是不要跳过现象确认直接动手。如果一开始就去重装系统,问题依然存在,因为no-auto-default=*这个配置是镜像自带的,重装还是同样的镜像,还是同样的配置,还是同样的结果。很多人陷入"重装—不通—再重装"的循环,根本原因就在这里。
第二个细节是**nmcli device status的 STATE 列信息量远大于ip addr**。ip addr只能告诉你有没有地址,nmcli能告诉你 NetworkManager 对这个设备的态度。unmanaged、disconnected、connecting、connected四种状态对应四条完全不同的排查路径,这一步判断对了,后面基本是照着走。
第三个细节是配置文件里的"批量部署痕迹"值得优先怀疑。像no-auto-default=*、unmanaged-devices=...、managed=false这类配置,几乎都是部署脚本加进去的,目的是统一管理。但部署脚本往往是"一次性"的,配置留在镜像里,后面的人接手就会莫名其妙。我现在的习惯是,遇到任何"系统行为不符合默认预期"的情况,先去/etc/NetworkManager/conf.d/和主配置里翻一遍,十有八九能找到人为干预的痕迹。
最后分享一个我常用的兜底脚本,把前面这些检查串成一条命令,接手一台陌生虚拟机时先跑一遍,输出直接能看到卡在哪一层:
echo "=== 设备 ==="; ip -br link echo "=== 地址 ==="; ip -br addr echo "=== NM状态 ==="; nmcli device status echo "=== 连接配置 ==="; nmcli connection show echo "=== 默认路由 ==="; ip route show default echo "=== DNS ==="; cat /etc/resolv.conf | grep -v "^#" echo "=== 驱动 ==="; lsmod | grep virtio_net六个段落对应六个层,哪一段输出不对劲,问题就在哪一层。这个脚本我放在自己的常用命令别名里,实测下来比翻笔记快得多。