
這篇是我把 Ubuntu 24.04 Minimal Server 放進 KVM/libvirt `default NAT` 網路時，從「`apt update` 完全失敗」一路排到「Docker Engine 正常運作」的完整紀錄。

重點不是只給一組指令，而是把每個檢查點都寫清楚，避免之後重建環境又卡在同一個地方。

## Host 端虛擬化環境準備（CachyOS）

先在 Host 安裝虛擬化與網路相關套件。

Host:

```bash
paru -S virt-manager qemu-desktop libvirt edk2-ovmf dnsmasq
```

用途：

1. `virt-manager`：圖形化管理 VM。
2. `qemu-desktop`：KVM/QEMU hypervisor。
3. `libvirt`：VM 與虛擬網路管理核心。
4. `edk2-ovmf`：UEFI 韌體支援。
5. `dnsmasq`：`default NAT` 會用來做 DHCP/DNS。

啟動服務：

Host:

```bash
sudo systemctl enable --now libvirtd
systemctl status libvirtd
```

把目前使用者加入 `libvirt` 群組：

Host:

```bash
sudo usermod -aG libvirt $(whoami)
```

重新登入後生效。

## Ubuntu 24.04 Minimal 安裝時要先勾的項目

在安裝流程中，至少確認：

1. IPv4 有啟用。
2. IPv6 有啟用。
3. 勾選安裝 OpenSSH server。
4. 網路模式選 `libvirt default NAT`。

如果一開始沒裝 `OpenSSH server`，後面只能先在 VM Console 打指令，操作效率會很差。

## Minimal 版本的現實：工具很少是正常現象

安裝完看到下面狀態不用緊張，這就是 Minimal 的設計：

1. 沒有 `ping`。
2. 沒有 `vim`。
3. 沒有 `nano`。
4. 沒有 `net-tools`。
5. 通常只有 `ip` 可用。

所以最初排障核心會是 `ip addr`、`ip route`、`/proc/net/route`、`/etc/resolv.conf`。

## 問題起點：`apt update` 失敗

VM:

```bash
sudo apt update
```

典型錯誤：

```text
Temporary failure resolving 'archive.ubuntu.com'
```

這個錯誤表面看起來像 DNS，但實務上常常是「路由 + DNS 同時壞掉」。

## 關鍵普查：先確認 Host/VM 連接現況再修

### 先確認 VM 網卡實際名稱

VM:

```bash
ip addr
```

要抓到：

1. 介面真名（例如 `enp0s1`、`ens1`、`enp1s0`）。
2. 不要假設一定叫 `eth0`。

Ubuntu 24.04 在不同虛擬化平台，命名可能不一樣，指令裡寫錯介面名會讓後續設定全部失效。

### IPv6 視覺誤區：看到 `inet6` 不代表可上網

在 `ip addr` 中常見情況是：

1. 介面有 `inet6`。
2. 卻沒有可用的 `inet` (IPv4)。

這通常代表 IPv4 DHCP 沒拿到地址，只剩 IPv6 link-local。它是「網路未完整對接」的警訊，不是正常可上網狀態。

### 檢查 VM 路由是否有 default gateway

VM:

```bash
ip route
cat /proc/net/route
```

如果 `/proc/net/route` 幾乎只剩表頭，或 `ip route` 沒有 `default via ...`，就代表 kernel 根本不知道封包要往哪裡送。

### 檢查 VM DNS

VM:

```bash
cat /etc/resolv.conf
```

如果 nameserver 是空的、無效的，或指向不可達目標，`apt` 會直接卡在網域解析。

### 普查 Host 虛擬網橋與 gateway

Host:

```bash
ip addr
```

找 `virbr*` 介面，通常是 `virbr0`，但名稱不保證固定。

再看它的 IPv4，例如：

```text
inet 192.168.122.1/24
```

這個位址就是 VM 的 gateway 候選值。

### 檢查 Host 端 forwarding 與 NAT

檢查 bridge 狀態：

Host:

```bash
ip addr show virbr0
```

檢查 IP forwarding：

Host:

```bash
sysctl net.ipv4.ip_forward
```

如果是：

```text
net.ipv4.ip_forward = 0
```

VM 幾乎不可能透過 Host 出網。

檢查 FORWARD chain：

Host:

```bash
sudo iptables -L FORWARD -n -v
```

要注意 policy 是否是 `DROP`，以及是否允許 `virbr` 封包轉送。

檢查 NAT POSTROUTING：

Host:

```bash
sudo iptables -t nat -L POSTROUTING -n -v
```

要看到類似：

```text
MASQUERADE  all  --  192.168.122.0/24  anywhere
```

沒有 MASQUERADE 的話，VM 即使有 gateway 也常常出不去外網。

## Host 端修復（若上述普查不通）

啟用 IP forwarding：

Host:

```bash
sudo sysctl -w net.ipv4.ip_forward=1
```

把 FORWARD policy 調成 ACCEPT：

Host:

```bash
sudo iptables -P FORWARD ACCEPT
```

新增 NAT 規則：

Host:

```bash
sudo iptables -t nat -A POSTROUTING -s 192.168.122.0/24 -j MASQUERADE
```

重新驗證：

Host:

```bash
sudo iptables -L FORWARD -n -v
sudo iptables -t nat -L POSTROUTING -n -v
```

## VM 端強制手動接通流程

先確認兩件事：

1. VM 網卡名，例如 `enp1s0`。
2. Host `virbr` IP，例如 `192.168.122.1`。

手動綁 IP：

VM:

```bash
sudo ip addr add 192.168.122.100/24 dev enp1s0
```

建立同網段路由：

VM:

```bash
sudo ip route add 192.168.122.0/24 dev enp1s0
```

加預設閘道：

VM:

```bash
sudo ip route add default via 192.168.122.1 dev enp1s0
```

檢查路由：

VM:

```bash
ip route
```

要看到 `default via 192.168.122.1 dev enp1s0`。

強制注入 DNS：

VM:

```bash
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
```

連通測試：

VM:

```bash
ping 8.8.8.8
ping archive.ubuntu.com
sudo apt update
```

只要這一步通了，後續工具與服務安裝就能接上。

## SSH 遠端管理：解決 VM Console 不好用

常見痛點是 VM console 無法順暢複製貼上，長指令容易打錯。

先裝核心工具：

VM:

```bash
sudo apt update && sudo apt install openssh-server vim
```

啟動 SSH：

VM:

```bash
sudo systemctl enable --now ssh
```

從 Host 連入：

Host:

```bash
ssh user@192.168.122.100
```

之後改用本機終端維運 VM，效率會高很多。

## Docker 引擎佈署：選 Docker 官方來源而不是 `docker.io`

Ubuntu 內建倉庫的 `docker.io` 可用，但我這次改用 Docker 官方來源，原因是：

1. 版本更新通常比較快。
2. `buildx` 與 `compose v2` 插件整合更完整。
3. 安全更新節奏跟官方同步。

## Docker 官方安裝流程（Ubuntu 24.04）

建立 keyring 目錄：

VM:

```bash
sudo install -m 0755 -d /etc/apt/keyrings
```

導入 Docker GPG：

VM:

```bash
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
```

加入 Docker repository：

VM:

```bash
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

安裝 Docker Engine 與插件：

VM:

```bash
sudo apt update && sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

設定免 sudo（目前使用者）：

VM:

```bash
sudo usermod -aG docker $USER
newgrp docker
```

驗證：

VM:

```bash
docker ps
```

## 最終驗收清單

Host 端：

1. `libvirtd` 正常 running。
2. `virbr*` bridge 存在且有 IPv4。
3. `net.ipv4.ip_forward = 1`。
4. FORWARD 鏈可放行 VM 封包。
5. NAT 有對 VM 網段做 MASQUERADE。

VM 端：

1. 介面有有效 IPv4。
2. 路由表有 `default via <virbr IP>`。
3. `/etc/resolv.conf` 有可用 nameserver。
4. `apt update` 可成功。
5. SSH 可登入。
6. Docker `hello-world` 可成功執行。

到這裡就完成「Minimal 初始不可用」到「可維運、可部署容器」的整條路徑。

## 參考資料

1. [libvirt: NAT forwarding (virtual networks)](https://wiki.libvirt.org/Networking.html)
2. [libvirt: Network XML format（含 default NAT 範例）](https://libvirt.org/formatnetwork.html)
3. [Ubuntu Server docs: Networking（How-to）](https://ubuntu.com/server/docs/how-to/networking/)
4. [Ubuntu Server docs: About Netplan](https://ubuntu.com/server/docs/explanation/networking/about-netplan/)
5. [Docker Docs: Install Docker Engine on Ubuntu](https://docs.docker.com/engine/install/ubuntu/)
6. [Docker Docs: Linux post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/)
