貴哥小時候沒修過網路的課, 後來玩 linux 勉強學會一點點 網路基本指令 (<==以下假設讀者可以手動設定有線網路並且玩過 qemu/kvm 虛擬機)。 這幾天爬了好多文終於學會用 bridge 跟 tap 手工建立虛擬區網。 我們的目標是要在你的電腦 (Host) 上面用 qemu/kvm 開兩部虛擬機 GuestA 跟 GuestB, 把它們放在同一個虛擬區域網路裡面, 讓 GuestA、 GuestB、 Host 可以直接看到彼此的 IP。 當然, GuestA 跟 GuestB 要能夠上網。
只想用虛擬機做事的讀者不必讀本文, 因為只要改採用更高階的 libvirt, 這些 「接水管」 的問題就不必自己操心了。 或是採用 手工精簡版 qemu-kvm 虛擬區域網路 也比本文簡單很多。 想當水管工的, 或是 [沒有足夠的設備也硬是要] 開設網路實習課程的老師才請進。
一、 行前準備
先談一下很簡單、 根本不需要讀本文的狀況。
如果只是要讓虛擬機能上網 => 什麼都不必設定。
這等同於在 kvm 命令列上使用 -net nic -net user
選項, 建立一個
SLIRP 類型的簡單網路。
如果
虛擬機要提供服務給 Host (例如 ssh 或 http 等等) =>
在 kvm 命令列上使用 -redir tcp:13022::22
就夠了,
也不需要做複雜的設定。 然後就可以從 Host 這樣連線:
ssh -p 13022 localhost
。
[2017/5/14] 如果虛擬機要有自己的私有 IP, 那就開始有點挑戰了。 不需要學細節, 只想快解決問題的讀者, 可以直接看 精簡版手工 qemu-kvm 虛擬區域網路。 以下開始很囉嗦的解釋。
更挑戰的是, 這幾年當中, linux 的網路指令/系統服務/kvm指令選項都有一些變動。 有些文章採用舊指令; 有些文章採用新指令。 特別是 qemu/kvm, 爬文幾乎都採舊指令; 但 qemu networking 跟 kvm networking 兩份最重要的官網文件採用的卻是新指令, 卻又很不完整。 我先用舊指令實驗成功, 最後再從猜測及錯誤訊息搜尋當中試出新的指令。 實驗環境是 lubuntu 15.04。 虛擬機跑的是簡單又強大的 finnix 開機光碟。 我在 .bashrc 裡面有這樣兩句設定:
alias mykvm='kvm -monitor stdio -m 1024 -vga std -cpu host' alias myfkvm='mykvm -cdrom /x/cdrom/finnix-ckhung16c.iso -boot order=dc'
所以原本我打 myfkvm 就可以直接開一部 finnix 虛擬機。
(如果滑鼠不能動, 請加上 -usbdevice mouse
重試一次看看)
你可以把 tun/tap 想成是虛擬網路線。
在 Host 那頭, 用 ip tuntap add name 虹吸管 mode tap
建立一條名為 「虹吸管」 的虛擬網路線的一頭;
用 ip link show
可以看見虹吸管。
(好啦, 我寫得好累, 請准我亂取名字舒緩一下心情; 請你要用英文名字)
在 GuestX 那頭, 啟動 kvm 時要用相同的名字,
這樣就在 Host 跟 GuestX 之間建立了一條虛擬網路線。
tun 是 IP 層的通道; tap 是 ethernet 層的通道。
這篇 tun/tap 簡介 很白話易懂;
這篇 第一節也很值得讀。
二、 實作
本文只用 tap, 而且在 Host 這邊, 不採用指令而是寫在設定檔裡。 請在 Host 的 /etc/network/interfaces 裡面加上這幾句:
auto qibr29 iface qibr29 inet static address 10.0.29.254 netmask 255.255.255.0 bridge_ports qitap2931 qitap2932 qitap2933 bridge_stp off bridge_fd 0 post-up echo 1 > /proc/sys/net/ipv4/ip_forward post-up iptables -t nat -A POSTROUTING -s '10.0.29.0/24' -o ethz -j MASQUERADE
然後照第三節第一個提示啟動新的 qibr29 網路服務。
以上是從
Debian wiki 的 NAT 內網那一節 改來的。
對沒錯, 網卡的名字又是隨意取的,
不過這次是有意義的! 代表 "qemu-internel bridge 29"。
還有 bridge_ports 後面的三個裝置名稱也是任意取的,
分別會 (用一條虹吸管) 接到一部虛擬機的虛擬網卡。
注意! 其中的 "-o ethz" 請改成 Host 對外的真實網卡。
每次修改過 /etc/network/interfaces 之後,
要下 ifdown qibr29 ; ifup qibr29
或是 service ifup@qibr29 restart
才會生效。
趁虛擬機還沒打開前,
先查看一下 Host 上有哪些 (實體及虛擬) 網卡:
ip link show > ~/netdev0.txt
。
再來啟動虛擬機:
舊語法: myfkvm -net nic,macaddr=52:54:00:12:34:1f -net tap,ifname=qitap2931,script=no,downscript=no
新語法: myfkvm -device e1000,netdev=net0,mac=52:54:00:12:34:1f -netdev tap,id=net0,ifname=qitap2931,script=no
舊語法的詳細解釋請見 suse linux 的文件。 新語法很清楚地把 GuestA 那頭關心的 (-device ...) 跟 Host 那頭關心的 (-netdev tap,...) 區分開來。
在 Host 上又執行 ip link show > ~/netdev1.txt
,
然後比較前後差異: diff ~/netdev0.txt ~/netdev1.txt
,
會看到多出一個 qitap2931 的裝置。
在 GuestA 上執行:
ifconfig eth0 10.0.29.31 netmask 255.255.255.0 route add default gw 10.0.29.254
然後它對外的網路就通了 --
挑一個英文網站試試看 w3m http://www.eff.org/
。
再來, 還可以安裝輕薄短小的網頁伺服器 nginx:
apt-get update apt-get install nginx w3m localhost vim /var/www/html/index*.html # 小改一下, 再用 w3m 確認有修改到
回到 Host 上, 瀏覽器開到 http://10.0.29.31/ 成功開啟 GuestA 的網頁!
開第二部虛擬機時, 請把上面特殊顏色標記的地方都改掉: 例如 31 改成 32, 1f 改成 20。 請確認一下兩部虛擬機可以造訪彼此的網頁。 當然, 外界看不到它們 -- 就連 Host 所在的網路都看不見它們。
三、 忍不住要問細節
能動是一回事; 追問細節才能學到更多。
service ifup@qibr29 restart
那個指令是怎麼回事? 現在已經沒有 "network" 這個服務了。 根據 這篇 可以用systemctl list-units --type=service
查看有哪些服務。 其中會出現一個 ifup@qibr29 。post-up iptables ... -j MASQUERADE
那一句是從 恆逸資訊洪朝欽老師 幫我做的設定改來的, 主要就是為了把虛擬網域的封包透過真實網卡送出去。 (事實上本文整個設定檔就是從他幫我架設的 proxmox 伺服器簡化而來的 ^_^)- 指定 mac address 的新舊語法不同。 而且 這段不能省略 -- 一開始我偷懶省略, 結果兩部機器各自可上網可被 Host 看見, 但看不到彼此, 因為 qemu 幫每一部虛擬機預設的 mac address 相同, 又放在同一個區網內, 所以就打架了。
- 如果你想在某部虛擬機裡面安裝很多張網卡呢?
(反正不用錢, 哈哈) 在新語法裡面,
顯然會用到好幾對
-device
跟-netdev
。 其中每一對的netdev=net0
跟id=net0
要一致, 這樣虹吸管虛擬網路線的兩頭才可以找到彼此。 (對, net0 的名字可以隨便亂改, 不要叫虹吸管就對了。) 在舊語法裡面, 如果要創造三張網卡, 就要有六句 -net, 每句後面都要加上,vlan=0
之類的, 每一對的數字要相同。 詳見 這篇。 - bridge_ports 那一句目前是寫死的。 如果開超過三部虛擬機,
後面的就沒網路可用了。 理想上, 應該要寫一些指令,
想辦法每開一部虛擬機時就自動開一個新的 tap 裝置加入 qibr29;
每關一部虛擬機時就自動把它的 tap 裝置移除。
是的, kvm 命令列上的
script=...
跟downscript=...
正是拿來這樣用的。 你可以自己寫 shell scrip、 從這裡指向它。 不過那太麻煩了, 所以本文省略。 但是 qemu/kvm 預設會去執行某兩個 scripts, 我們搞不清楚系統在幫我們做什麼, 所以乾脆叫它不要執行預設的這兩個 scripts。 預設的 scripts 放在哪裡? 在 lubuntu 15.04 上, 用strings /usr/bin/qemu-system-x86_64 | grep ifup
可以發現是 /etc/qemu-ifup 跟 /etc/qemu-ifdown。 在 proxmox 3.* 上, 用strings /usr/bin/kvm | grep ifup
可以發現是 /etc/kvm/kvm-ifup 跟 /etc/kvm/kvm-ifdown。 - 可以進一步按照
Debian DHCP 安裝設定教學 (中文) 幫 qibr29 架設一個 dhcp 服務。
我沒仔細讀, 基本上只是關虛擬機、 安裝 isc-dhcp-server、
在 /etc/dhcp/dhcpd.conf 最下面加一段、
重新啟動 isc-dhcp-server、 重開 GuestA 跟 GuestB。
於是 GuestA 跟 GuestB 就自動取得 IP 了。
subnet 10.0.29.0 netmask 255.255.255.0 { INTERFACES="qibr29"; range 10.0.29.31 10.0.29.33; option routers 10.0.29.254; }
四、 融入 Host 所在的區網
那如果想要讓虛擬區網融入 Host 所在的區網呢? 觀念上, 我們只需要在 /etc/network/interfaces 裡面修改三處:
- 直接把 ethz 加入 qibr29 底下的埠。
- qibr29 自己的 IP 該如何設定? 套用原先 ethz 的設定值。
- 省略
post-up iptables ... -j MASQUERADE
那一句。
實作上, 還要加一句 iface ethz inet manual
,
這樣才可以
避免好心的 network-manager 插手管理 ethz 結果越幫越忙。
例如我的 ethz 原先沒有出現在 /etc/network/interfaces 裡面,
而是由 network manager 在管理; 它的 IP 是從實體分享器取得的。
所以現在 /etc/network/interfaces 改成這樣:
iface ethz inet manual auto qibr29 iface qibr29 inet dhcp bridge_ports ethz qitap2931 qitap2932 qitap2933 bridge_stp off bridge_fd 0
那麼我的 GuestA 跟 GuestB 就會跟家裡的其他裝置 (例如我的手機) 看到彼此的 IP 了。 這也是 debian 官網解釋 bridging 的設定方式, 比第二節還簡單。 又因為實體分享器會派送 dhcp, 所以我們不必自己架 dhcp 伺服器, 虛擬機也會自動取得 IP, 超方便的啊! 不過如果你的 Host 是一部真的伺服器, 那麼最好你要在現場, 因為這種設定方式很可能一不小心網路就會壞掉。
心得: 虛擬化環境真的是學習網路設定與觀念的好環境啊!
沒有留言:
張貼留言
因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。