Docker/K8s 網路基礎:從手動操作 Linux Network Namespace 開始
在現代的雲端運算與容器化技術(如 Docker、Kubernetes)中,網路的隔離是一個基本的核心概念,在Multi-tenancy的環境中,我們不只希望隔離Process的檔案系統和Process ID,更重要的是要隔離彼此之間的網路環境,而Linux…
UpdatedMarch 24, 2026•3 min read
JJhihHao Wu**近期研究重點包含 AI Agent 的供應鏈攻擊、PII 偵測模型評估,以及醫療 AI 在臨床流程中的安全落地。
在這裡,我分享深度技術實測報告(如 NVIDIA NeMo, WildGuard)與職場技術成長心得,致力於在 AI 浪潮中打造具備資安韌性的解決方案。
On this page
Docker/K8s 網路基礎:從手動操作 Linux Network Namespace 開始網路介面在 Namespace 之間移動1. 建立 Namespace 並移入介面處理「撞名」移動介面並同時重新命名當 Namespace 被刪除時會發生什麼?實體介面的「回家」之路與命名衝突systemd 與可預測的網路介面命名
Docker/K8s 網路基礎:從手動操作 Linux Network Namespace 開始
在現代的雲端運算與容器化技術(如 Docker、Kubernetes)中,網路的隔離是一個基本的核心概念,在Multi-tenancy的環境中,我們不只希望隔離Process的檔案系統和Process ID,更重要的是要隔離彼此之間的網路環境,而Linux 核心提供了一個強大的功能來實現這一點,就是 網路命名空間 (Network Namespace)。
簡單來說,你可以把每個 Network Namespace 想像成宿舍內一個個獨立的「網路房間」,每個房間都有自己專屬的網路設備、IP 位址、路由表、防火牆規則等等,這也代表,在 namespace1 這個房間裡的程序,看不到也無法直接存取 namespace2 房間的網路,實現了完美的網路隔離。
而這一切的基礎都建立在一個關鍵規則上:一個網路介面(Network Interface)在同一時間只能存在於一個 Network Namespace 中,就像前面的例子,每一扇門一次只能安裝在一間房間上。
那我們該如何管理這些「門」,在不同的「房間」之間移動它們呢?
網路介面在 Namespace 之間移動
要在不同的 Namespace 之間移動網路介面,最常用的工具就是 ip 指令。
1. 建立 Namespace 並移入介面
讓我們先從一個最簡單的範例開始。假設我們要建立一個新的網路空間 namespace1,並創建一個虛擬的 dummy0 介面,然後將它移入 namespace1。
步驟 1:建立新的 Network Namespace
# 建立一個名為 namespace1 的網路命名空間
$ ip netns add namespace1
# 確認 ns1 裡面只有一個預設的 lo (loopback) 介面
$ ip netns exec namespace1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
步驟 2:在root Namespace 建立一個虛擬介面
# 建立一個名為 dummy0 的 dummy 類型介面
$ ip link add dummy0 type dummy
# 確認一下,現在 dummy0 存在於我們操作的預設環境中
$ ip link show dummy0
24: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 4e:3d:fe:03:2c:c8 brd ff:ff:ff:ff:ff:ff
步驟 3:將 dummy0 移入 ns1
# 使用 ip link set 指令將 dummy0 移動到 namespace1
$ ip link set dummy0 netns namespace1
上面的指令,其實是透過 Netlink 向 Linux 核心發送了一個 RTM_NEWLINK 請求(一般是網路介面的狀態有發生變動時會觸發),並帶上目標 Namespace 的識別資訊。
步驟 4:驗證結果
# root Namespace 中,dummy0 消失了
$ ip link show dummy0
Device "dummy0" not found.
# 在 namespace1 中,我們成功找到了 dummy0
$ ip netns exec namespace1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether e2:79:ba:8f:ad:3e brd ff:ff:ff:ff:ff:ff
處理「撞名」移動介面並同時重新命名
介面名稱其實在現實世界是一種麻煩事,如果在目標 Namespace namespace1 中,已經有一個叫做 dummy0 的介面了,會發生什麼事?
# 假設 namespace1 裡面已經有 dummy0 了,我們再移一個dummy0進去
$ ip link add dummy0 type dummy
$ ip link set dummy0 netns namespace1
RTNETLINK answers: File exists
系統會很直接地告訴你:「檔案已存在 (File exists)」,操作失敗,因為在同一個 Namespace 內,介面名稱必須是唯一的,也因此,ip 指令允許我們在移動的同時,為介面指定一個新的名字。
# 在移動 dummy0 到 ns1 的同時,將它改名為 dummy1
$ ip link set dummy0 netns namespace1 name dummy1
# 檢查 namespace1,可以看到新的介面 dummy1
$ ip netns exec namespace1 ip link
...
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether e2:79:ba:8f:ad:3e brd ff:ff:ff:ff:ff:ff
5: dummy1: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether e2:79:ba:8f:ad:3e brd ff:ff:ff:ff:ff:ff
這次的操作,在送給核心的 Netlink 訊息中,除了目標 Namespace,還多了一個 IFLA_IFNAME 屬性,明確告知了新的介面名稱。
當 Namespace 被刪除時會發生什麼?
我們把介面搬進了 Namespace,那如果這個 Namespace 被刪除了呢?比如 ip netns del namespace1,裡面的介面會何去何從?
答案取決於介面的「類型」:
虛擬介面 (Virtual Interface):像是
dummy,veth,bridge等,這些介面是依附於 Namespace 的,因此當 Namespace 消失時,它們也會跟著直接被刪除。實體介面 (Physical Interface):像是你的實體網卡
eth0或enp3s0,這些是實際的硬體設備,不能隨便刪除,也因為這樣,當它們所在的 Namespace 被刪除時,核心會自動將它們移回到最初的 Root Namespace。
實體介面的「回家」之路與命名衝突
當實體介面要「回家」時,也可能遇到撞名問題,如果Root Namespace 已經有一個同名介面了怎麼辦?
核心的處理邏輯如下:
嘗試使用它原本的名字。
如果原名已被佔用,核心會幫它取一個新的名字,通常是
dev%d的格式(%d是介面的索引編號 ifindex)。如果連
dev%d都被佔用了,就會使用dev%%d樣板來確保能找到一個獨一無二的名字。
讓我們來看一個有趣的實驗:
# 1. 將實體介面 dev0 移入 namespace1
$ ip link set dev0 netns namespace1
# 2. 在根 Namespace 建立一個同名的虛擬介面 dev0
$ ip link add dev0 type dummy
# 3. 此時根 Namespace 有一個虛擬的 dev0,namespace1 有一個實體的 dev0
# 現在,我們刪除 namespace1
$ ip netns del namespace1
# 4. 檢查根 Namespace 的介面
$ ip link
...
# 這是我們手動建立的虛擬 dev0
4: dev0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
# 這是從 namespace1 回家的實體介面,因為 dev0 被佔用,被自動改名為 dev1
6: dev1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 11:22:33:44:55:66 brd ff:ff:ff:ff:ff:ff
這個自動重新命名的機制,確保了系統的穩定性,避免了因 Namespace 刪除而導致實體設備遺失或衝突。
systemd 與可預測的網路介面命名
在過去,網路介面名稱是 eth0, eth1... 這種不穩定的方式,順序可能因為開機時核心偵測到設備的順序而改變。
systemd 後來加入了可預測網路介面命名機制 (Predictable Network Interface Names),它會根據硬體的韌體資訊、PCI 插槽位置、MAC 位址等穩定特徵來為介面命名,例如 enp3s0(表示在 PCI bus 3, slot 0 上的乙太網路卡)或 wlp2s0(表示在 PCI bus 2, slot 0 上的無線網卡),總共有五種命名方式,也可以關掉自動命名。
同時,systemd-udevd 服務在開機時監聽核心事件,並根據 /etc/udev/rules.d/ 中的規則為新發現的網路設備賦予這些穩定的名稱,這樣衣來,無論開機順序如何,你的網卡名稱始終如一,大大簡化了網路設定和管理。
上面這些跟Docker/K8S有什麼關係?
我一直以來個人理解是這樣的XD 如果有錯誤請鞭小力一點
Linux Namespace & Cgroups:是每個宿舍的房間與門禁,每個房間內有哪些資源可以用,有誰可以進出。
Systemd:是每個樓層(每台主機/節點)的管理員,確保水電(Kubelet、Container Runtime)正常供應。
Kubernetes:是整棟宿舍大樓的舍監管理層,負責規劃誰該住什麼房間(Pod)、房間要幾間(ReplicaSet),以及如何管理所有房間(service、LB等),或是各種需求(規劃新生,轉學生申請宿舍)時的安排等。
簡單來說,Kubernetes 進行最上層的規劃與決策,將任務派發給節點上的 systemd 所管理的 kubelet,kubelet 再指揮 container runtime,最終由 container runtime 利用 Linux Namespace 和 Cgroups 這些核心功能,完成容器的建立與運行。

Top comments (0)