单机三虚拟机集群:为什么网络一定要选 Bridge,而不是 NAT
在一台 Ubuntu 22 物理机上,用 Virtual Machine Manager(virt-manager / KVM/libvirt) 跑起三台虚拟机(node-01 / node-02 / node-03),组成一个学习与测试用的集群,部署 K8S、Redis Cluster、Elasticsearch。本文先把"为什么选 Bridge"讲透,再给出一套可照抄的固定 IP 配置流程:宿主机建桥 → VMM 部署虚拟机 → 每台虚拟机用 netplan 固定 IP → 屏蔽 cloud-init 防覆盖。
一、来龙去脉:为什么要在一台机器上起一个集群
很多分布式中间件,本质上是"多节点"的:
- Kubernetes 至少要有一个控制面 + 若干工作节点,才能体会调度、网络、故障转移;
- Redis Cluster 官方要求最少 6 个实例(3 主 3 从),节点之间靠 Gossip 协议互相发现;
- Elasticsearch 想验证分片、副本、选主,也得有 3 个节点起步。
可现实是:大多数人手里只有一台还算能打的机器。买三台物理服务器太奢侈,上云按小时计费又心疼。于是最务实的方案就是------一台宿主机,虚拟出三台"准物理机",既省钱,又能逼真地复现多节点环境。
到这里,真正的分水岭出现了:这三台虚拟机,要怎么联网?
这不是个无所谓的细节。它直接决定了你的集群"像不像真的",以及局域网里其他机器(你的笔记本、同事的电脑、另一台测试机)能不能直接访问到这三个节点。
二、核心诉求:节点必须像真实物理机一样"出现在局域网里"
把需求一句话讲清:
局域网内其他机器,要能像访问一台普通服务器那样,直接访问 node-01 / node-02 / node-03。
为什么这个诉求如此关键?因为一旦做集群,你几乎一定会遇到这些场景:
- 在笔记本上用
kubectl直连虚拟机里的 K8S API Server; - 用客户端连 Redis Cluster------而 Redis Cluster 会把节点自己的 IP 通过
MOVED/ASK重定向给客户端,客户端必须能直接连上那个 IP; - 用浏览器或 Kibana 访问 ES 的
9200/5601; - 节点之间互相发现、心跳、选主,要求彼此地址对等、可路由。
这些场景的共同点是:虚拟机需要一个局域网里真实、独立、可被别人主动连入的 IP,而不是藏在宿主机背后的"内部地址"。
能不能满足这个诉求,就看你选 NAT 还是 Bridge。
三、NAT vs Bridge:从本质看区别
两种模式的差异,可以用"虚拟机在网络里的身份"来理解。
NAT 模式:虚拟机藏在宿主机背后
NAT(网络地址转换)下,libvirt 会建一个私有虚拟网络 (默认 virbr0,网段如 192.168.122.0/24),虚拟机拿到的是这个私有网段的地址。对外通信时,所有流量都被"伪装"成宿主机的 IP 发出去。
这就像住在一栋楼里、没有独立门牌号 的房间:你能主动出门(访问外网 OK),但外面的人想找你,只能先敲楼的总门(宿主机),再由前台转发------你必须在宿主机上一条条配端口转发(hostfwd / iptables DNAT),否则外部根本无法主动连入。
对一个动辄十几个端口、还会互相重定向 IP 的集群来说,这是灾难。
Bridge 模式:虚拟机是局域网里的"正式居民"
Bridge(桥接)的做法完全不同。宿主机上建一个网桥 br0 ,它扮演一台虚拟交换机:把宿主机的物理网卡和每台虚拟机的虚拟网卡,统统"插"到这台交换机上。
结果是:虚拟机直接接入了你家/公司的物理局域网,由路由器(192.168.1.1)像给真实设备发地址一样,给每台虚拟机分配一个 192.168.1.x 的 IP 。它和宿主机、和其他物理机处在同一个二层网段,彼此平等、互相可达,不需要任何端口转发。
这正是我们要的------虚拟机像真实物理机一样,出现在局域网中。
| 维度 | NAT 模式 | Bridge 模式(本文选择) |
|---|---|---|
| 虚拟机 IP 来源 | libvirt 私有网段(如 192.168.122.x) | 路由器分配的局域网 IP(192.168.1.x) |
| 与局域网的关系 | 隔离,藏在宿主机背后 | 同一二层网段,平等成员 |
| 外部主动访问虚拟机 | 需逐端口配置转发,繁琐 | 直接访问,零额外配置 |
| 虚拟机访问外网 | 开箱即用 | 正常可用 |
| 节点间互相发现 | 受限,IP 不可对外 | 天然支持,地址对等 |
| 适合场景 | 单机自用、临时测试 | 多节点集群、需被外部访问 |
结论很清晰:做集群、要被局域网访问,就选 Bridge。
四、目标拓扑图
下图是采用 Bridge 模式后的网络结构。核心是中间那台"虚拟交换机" br0:物理网卡和三台虚拟机都挂在它下面,从而都成为 192.168.1.0/24 的正式成员。
#mermaid-svg-I6FzwhhY0HMG1XBY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-I6FzwhhY0HMG1XBY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-I6FzwhhY0HMG1XBY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-I6FzwhhY0HMG1XBY .error-icon{fill:#552222;}#mermaid-svg-I6FzwhhY0HMG1XBY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-I6FzwhhY0HMG1XBY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-I6FzwhhY0HMG1XBY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-I6FzwhhY0HMG1XBY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-I6FzwhhY0HMG1XBY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-I6FzwhhY0HMG1XBY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-I6FzwhhY0HMG1XBY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-I6FzwhhY0HMG1XBY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-I6FzwhhY0HMG1XBY .marker.cross{stroke:#333333;}#mermaid-svg-I6FzwhhY0HMG1XBY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-I6FzwhhY0HMG1XBY p{margin:0;}#mermaid-svg-I6FzwhhY0HMG1XBY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-I6FzwhhY0HMG1XBY .cluster-label text{fill:#333;}#mermaid-svg-I6FzwhhY0HMG1XBY .cluster-label span{color:#333;}#mermaid-svg-I6FzwhhY0HMG1XBY .cluster-label span p{background-color:transparent;}#mermaid-svg-I6FzwhhY0HMG1XBY .label text,#mermaid-svg-I6FzwhhY0HMG1XBY span{fill:#333;color:#333;}#mermaid-svg-I6FzwhhY0HMG1XBY .node rect,#mermaid-svg-I6FzwhhY0HMG1XBY .node circle,#mermaid-svg-I6FzwhhY0HMG1XBY .node ellipse,#mermaid-svg-I6FzwhhY0HMG1XBY .node polygon,#mermaid-svg-I6FzwhhY0HMG1XBY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-I6FzwhhY0HMG1XBY .rough-node .label text,#mermaid-svg-I6FzwhhY0HMG1XBY .node .label text,#mermaid-svg-I6FzwhhY0HMG1XBY .image-shape .label,#mermaid-svg-I6FzwhhY0HMG1XBY .icon-shape .label{text-anchor:middle;}#mermaid-svg-I6FzwhhY0HMG1XBY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-I6FzwhhY0HMG1XBY .rough-node .label,#mermaid-svg-I6FzwhhY0HMG1XBY .node .label,#mermaid-svg-I6FzwhhY0HMG1XBY .image-shape .label,#mermaid-svg-I6FzwhhY0HMG1XBY .icon-shape .label{text-align:center;}#mermaid-svg-I6FzwhhY0HMG1XBY .node.clickable{cursor:pointer;}#mermaid-svg-I6FzwhhY0HMG1XBY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-I6FzwhhY0HMG1XBY .arrowheadPath{fill:#333333;}#mermaid-svg-I6FzwhhY0HMG1XBY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-I6FzwhhY0HMG1XBY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-I6FzwhhY0HMG1XBY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I6FzwhhY0HMG1XBY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-I6FzwhhY0HMG1XBY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I6FzwhhY0HMG1XBY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-I6FzwhhY0HMG1XBY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-I6FzwhhY0HMG1XBY .cluster text{fill:#333;}#mermaid-svg-I6FzwhhY0HMG1XBY .cluster span{color:#333;}#mermaid-svg-I6FzwhhY0HMG1XBY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-I6FzwhhY0HMG1XBY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-I6FzwhhY0HMG1XBY rect.text{fill:none;stroke-width:0;}#mermaid-svg-I6FzwhhY0HMG1XBY .icon-shape,#mermaid-svg-I6FzwhhY0HMG1XBY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I6FzwhhY0HMG1XBY .icon-shape p,#mermaid-svg-I6FzwhhY0HMG1XBY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-I6FzwhhY0HMG1XBY .icon-shape .label rect,#mermaid-svg-I6FzwhhY0HMG1XBY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I6FzwhhY0HMG1XBY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-I6FzwhhY0HMG1XBY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-I6FzwhhY0HMG1XBY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-I6FzwhhY0HMG1XBY .router>*{fill:#1e3a5f!important;stroke:#4a90d9!important;color:#fff!important;stroke-width:1.5px!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .router span{fill:#1e3a5f!important;stroke:#4a90d9!important;color:#fff!important;stroke-width:1.5px!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .router tspan{fill:#fff!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .other>*{fill:#2a2a2a!important;stroke:#888888!important;color:#dddddd!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .other span{fill:#2a2a2a!important;stroke:#888888!important;color:#dddddd!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .other tspan{fill:#dddddd!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .nic>*{fill:#3d3320!important;stroke:#d9b84a!important;color:#fff!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .nic span{fill:#3d3320!important;stroke:#d9b84a!important;color:#fff!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .nic tspan{fill:#fff!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .bridge>*{fill:#3d2f5f!important;stroke:#b79bff!important;color:#fff!important;stroke-width:3px!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .bridge span{fill:#3d2f5f!important;stroke:#b79bff!important;color:#fff!important;stroke-width:3px!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .bridge tspan{fill:#fff!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .vm>*{fill:#1f3d2f!important;stroke:#5fb88a!important;color:#fff!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .vm span{fill:#1f3d2f!important;stroke:#5fb88a!important;color:#fff!important;}#mermaid-svg-I6FzwhhY0HMG1XBY .vm tspan{fill:#fff!important;} 🖥️ 宿主机 Ubuntu22
🌐 局域网入口层
虚拟机集群 (K8S / Redis / ES)
直接访问
路由器 / 网关
192.168.1.0/24 · 192.168.1.1
局域网其他机器
192.168.1.xxx
物理网卡 enp55s0
桥接成员 · 无独立 IP
网桥 br0(虚拟交换机)
宿主机 IP: 192.168.1.188
node-01
192.168.1.189
node-02
192.168.1.190
node-03
192.168.1.191
读图三个要点:
br0是中枢(图中紫色六边形)。它就是虚拟交换机,所有成员都接在它上面。- 物理网卡没有独立 IP 。在桥接里,网卡只是
br0的一个"端口",宿主机自己的 IP(192.168.1.188)落在br0上,而不是网卡上。这一步是新手最容易配错的地方。 - 其他机器到虚拟机是直达的(虚线)。因为大家在同一二层网段,数据帧经交换/网桥直接转发,不必绕路由器做转换。
五、落地四步总览
从零到一,整套部署就是四步,后面逐步展开:
| 步骤 | 在哪做 | 做什么 |
|---|---|---|
| ① 建网桥 br0 | 宿主机 | 屏蔽 cloud-init,用 netplan 把物理网卡桥接成 br0,宿主机 IP 落到 br0(192.168.1.188) |
| ② 部署虚拟机 | 宿主机(VMM) | 用 Virtual Machine Manager 创建 3 台 Ubuntu 22 虚拟机,网卡都选 br0 |
| ③ 固定虚拟机 IP | 每台虚拟机 | 同样屏蔽 cloud-init,用 netplan 配静态 IP(.189/.190/.191),用 routes 替代已弃用的 gateway4 |
| ④ 名称解析 | 所有节点 | 统一写 /etc/hosts,集群内用 node-01/02/03 互访 |
贯穿全程的关键点:屏蔽 cloud-init。 Ubuntu 22 默认用 cloud-init 管网络,每次开机会自动生成
/etc/netplan/50-cloud-init.yaml,覆盖你手写的配置。无论宿主机还是虚拟机,只要你想用自己的固定 IP,就必须先把 cloud-init 的网络管理关掉(见每一步的说明)。
六、第一步:宿主机建网桥 br0
6.1 先屏蔽 cloud-init 的网络管理
让你手写的 netplan 生效、不被开机覆盖:
bash
sudo nano /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
写入:
yaml
network: {config: disabled}
保存退出后,cloud-init 就不会再生成或覆盖 50-cloud-init.yaml。可顺手删掉旧文件避免冲突:
bash
sudo rm -f /etc/netplan/50-cloud-init.yaml
6.2 用 netplan 建桥
核心思想:把物理网卡的 IP 摘下来,改成"无 IP 的桥接成员",再把 IP 配到 br0 上 。新建 /etc/netplan/01-netcfg.yaml(网卡名 enp55s0 用 ip link show 查你自己的):
yaml
network:
version: 2
renderer: networkd
ethernets:
enp55s0: # 物理网卡名,用 ip link show 查看
dhcp4: no
dhcp6: no # 网卡本身不再要 IP
bridges:
br0:
interfaces: [enp55s0] # 把物理网卡接入网桥
dhcp4: no
dhcp6: no
addresses: [192.168.1.188/24]
routes:
- to: default # 用 routes 替代已弃用的 gateway4
via: 192.168.1.1
nameservers:
addresses: [192.168.1.1, 8.8.8.8]
6.3 设权限并应用
bash
sudo chmod 600 /etc/netplan/01-netcfg.yaml
sudo chown root:root /etc/netplan/01-netcfg.yaml
sudo netplan apply
确保 systemd-networkd 在运行:
bash
sudo systemctl enable systemd-networkd
sudo systemctl start systemd-networkd
完成后 ip a 应看到 IP 在 br0 上、enp55s0 无 IP------这就是桥接生效的标志。
⚠️ 远程操作要小心 :改桥接会瞬断网络。SSH 操作前务必有控制台兜底,或先
sudo netplan try(超时自动回滚)。
七、第二步:用 Virtual Machine Manager 部署三台虚拟机
打开 Virtual Machine Manager(virt-manager),依次创建 node-01 / node-02 / node-03(均为 Ubuntu 22 Server),关键只在网络这一步:
File → New Virtual Machine,选择 Ubuntu 22 安装介质(ISO);- 分配 CPU / 内存 / 磁盘(集群节点建议 ≥2 vCPU、≥4GB、≥40GB);
- 最后一步勾选 "Customize configuration before install";
- 在网络设置里,Network source 选
Bridge device...并填br0(或选已有的桥接来源),网卡型号选virtio以获得更好性能; - 完成创建,装系统。三台虚拟机重复以上步骤。
这一步等价于命令行的
virt-install --network bridge=br0,model=virtio ...。VMM 只是把它做成了图形界面。装好后,虚拟机网卡会临时从路由器拿到一个192.168.1.x,下一步我们把它固定下来。
八、第三步:给每台虚拟机配固定 IP(netplan)
每台虚拟机也是 Ubuntu 22,同样要先屏蔽 cloud-init ,否则重启后 50-cloud-init.yaml 会覆盖你的静态配置:
bash
# 在每台虚拟机里执行
echo 'network: {config: disabled}' | sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
sudo rm -f /etc/netplan/50-cloud-init.yaml
然后在每台虚拟机新建 /etc/netplan/01-netcfg.yaml,用 routes 替代 gateway4,分别固定 IP。网卡名 ens3 用 ip link show 确认。
node-01 → 192.168.1.189
yaml
network:
version: 2
renderer: networkd
ethernets:
ens3: # 虚拟机网卡名,用 ip link show 查看
dhcp4: no
dhcp6: no
addresses: [192.168.1.189/24]
routes:
- to: default
via: 192.168.1.1
nameservers:
addresses: [192.168.1.1, 8.8.8.8]
node-02 → 192.168.1.190 (仅 addresses 不同)
yaml
network:
version: 2
renderer: networkd
ethernets:
ens3:
dhcp4: no
dhcp6: no
addresses: [192.168.1.190/24]
routes:
- to: default
via: 192.168.1.1
nameservers:
addresses: [192.168.1.1, 8.8.8.8]
node-03 → 192.168.1.191
yaml
network:
version: 2
renderer: networkd
ethernets:
ens3:
dhcp4: no
dhcp6: no
addresses: [192.168.1.191/24]
routes:
- to: default
via: 192.168.1.1
nameservers:
addresses: [192.168.1.1, 8.8.8.8]
每台虚拟机应用配置:
bash
sudo netplan apply
# 想先验证、出错自动回滚:
sudo netplan try
为什么用
routes而不是gateway4? 较新版本的 netplan 已弃用gateway4,推荐用routes:下的to: default / via: <网关>来声明默认路由。现在就用新写法,免得日后告警或失效。
九、第四步:统一 /etc/hosts,集群内用名字互访
集群组件(K8S、ES)里写一串 IP 既难记又易错。在每台机器 (宿主机 + 三台虚拟机)的 /etc/hosts 末尾追加同一段映射,之后就能用 node-01 这样的名字互访:
text
192.168.1.188 host-ubuntu
192.168.1.189 node-01
192.168.1.190 node-02
192.168.1.191 node-03
十、验证与后续访问方式
判断整套网络是否成功,只看一件事:局域网里另一台机器,能不能直接连上虚拟机的服务。
- 在你的笔记本上
ping 192.168.1.189通 → 二层打通; - 三台虚拟机之间互相
ping node-02都通 → 集群内部网络与解析就绪。
固定 IP 后的访问方式:
- 宿主机 :
192.168.1.188(在br0上); - 虚拟机 :
192.168.1.189 / .190 / .191; - 示例:
ssh root@192.168.1.189 -p 22redis-cli -h 192.168.1.190 -p 6379- Kubernetes 各节点互通直接用各自固定 IP(或
/etc/hosts里的名字)。
十一、使用场景:为什么这些组件"非 Bridge 不可"
回到最初的三个目标组件,看看 Bridge + 固定 IP 在它们身上具体解决了什么:
Kubernetes ------ 节点要互相通信、Pod 网络要可路由,外部还要用 kubectl 连 API Server。NAT 下节点 IP 对外不可见,kubeconfig 里写的地址外面连不上;Bridge 让每个节点都有真实局域网 IP,固定下来后 kubeadm/证书里的地址也不会因重启漂移。
Redis Cluster ------ 这是最典型的"NAT 杀手"。Redis Cluster 在重定向时会把节点自身记录的 IP 返回给客户端;如果那是个 NAT 私有地址,客户端根本连不上,集群形同虚设。Bridge + 固定 IP 下节点地址真实且稳定,重定向天然正确。
Elasticsearch ------ 节点靠相互发现组成集群、对外暴露 9200。Bridge 让发现和访问都走真实 IP,配 discovery.seed_hosts 时直接写 192.168.1.189/190/191(或 hosts 名字)即可,且重启不变。
一句话:凡是"节点用自己的真实 IP 对外打交道"的分布式系统,都依赖 Bridge,且强烈建议固定 IP。
十二、常见坑与注意事项
- cloud-init 覆盖配置 :这是最高频的坑。手写 netplan 不生效、重启又变回 DHCP,十有八九是没屏蔽 cloud-init。宿主机和每台虚拟机都要做
network: {config: disabled}。 - 改桥接断网 :
netplan apply会重置网络,远程操作前务必准备好控制台兜底,或用netplan try。 - IP 配错了位置 :桥接后 IP 必须在
br0上,物理网卡不能再留 IP,否则路由混乱。 - 网卡名不对 :宿主机(如
enp55s0)和虚拟机(如ens3)网卡名不同,务必ip link show确认,别照抄。 gateway4已弃用 :统一用routes: - to: default / via:写默认网关。- WiFi 网卡难桥接 :大多数无线网卡不支持桥接,宿主机尽量用有线网卡 做
br0。 - 企业网络限制:部分公司网络对一个物理口下挂多个 MAC/IP 有端口安全限制,会导致虚拟机拿不到地址,需联系网管。
- MTU 一致性 :跨节点大流量(如 ES 分片同步)时,确保宿主机、
br0、虚拟机 MTU 一致,避免分片丢包。
十三、小结
在一台 Ubuntu 22 上搭三节点集群,最该想清楚的不是 CPU 内存怎么分,而是网络模式 + IP 稳定性:
- 目标:让 node-01/02/03 像真实物理机一样出现在局域网,被直接访问,且 IP 永久固定。
- 手段 :宿主机用
br0网桥把物理网卡和虚拟机接到同一台"虚拟交换机";用 Virtual Machine Manager 部署虚拟机并选br0;每台虚拟机用 netplan(routes替代gateway4)配静态 IP;宿主机与虚拟机都屏蔽 cloud-init 防止覆盖;最后用/etc/hosts统一解析。 - 取舍 :NAT 适合单机自用、不需外部访问;只要做集群、要被局域网访问,就选 Bridge。
把 br0 理解成一台交换机、把 cloud-init 这个"网络管家"先请走,整套配置就顺理成章了。剩下的,就是在三台"准物理机"上尽情折腾 K8S、Redis Cluster 和 ES 了。