如何在網路命名空間中使用綁定掛載?
我有一個在網路命名空間中執行的應用程序。這很好用。
我想在不同的命名空間中多次執行該應用程序。為方便起見,我想將應用程序的工作目錄綁定到命名空間內的 /tmp/nsX 之類的目錄。
如果我只是
mount --bind /tmp/nsX /var/lib/my-app
在命名空間中執行,那麼當我退出命名空間時,掛載就會消失。通過進入/退出命名空間,我的意思是
ip netns exec bash
我在看,
unshare
但nsenter
我不知道該怎麼做。我想要:
- 為命名空間配置網路
- 在命名空間中為我的應用程序的工作目錄創建綁定掛載。
- 在命名空間中生成我的應用程序。如果有幫助,它有一個“叉子”選項。
- 能夠離開和進入命名空間而不會死亡或消失。
如果我需要使用其他一些命名空間類型,那很好。
為什麼會這樣?
- 網路命名空間不會更改掛載設置:它處理網路
- 但是一些與網路命名空間相關的掛載設置,最顯著的
/sys/class/net
和/proc/sys/net
,如上一個連結中所述,確實取決於網路命名空間這裡的行為已經有所不同:
/proc/sys/net
當進入新的命名空間時,當已經掛載時會即時更改,/sys/class/net
但不會。這意味著當使用真正只更改網路名稱空間的命令時:unshare -n -- sh -c 'ls -1d /proc/sys/net/*/conf/* /sys/class/net/*'
人們會看到以前的網路介面已經消失
/proc/sys/net/
(只留下一個新的lo
介面實例)但仍然可見/sys/class/net/
:防止與新的網路命名空間的介面互動,並且仍然允許與以前的網路命名空間的介面互動,這可能不是一個好主意.
- 要解決這個與網路相關的問題,
/sys
必須從新的網路命名空間(重新)安裝。為了避免影響專用於前(初始)網路命名空間的環境,這必須在較新的掛載命名空間中完成。這就是這樣ip netns exec
做的原因:為了為應用程序準備一個連貫的網路環境,它既進入一個現有的網路命名空間(通過綁定掛載創建並保持存在ip netns add
),又取消共享一個新的掛載命名空間。- 這個掛載命名空間只有通過(a)程序引用它才能保持存在。一旦沒有留下任何程序,掛載命名空間以及在其中完成的任何掛載都會消失(只能從此類程序中看到)。
- 所以單獨使用
unshare -m
orip netns exec
(它甚至不打算首先處理掛載)不會在呼叫之間保持綁定掛載。解決方案
這種掛載應該在使用之前完成
ip netns exec ...
,一次完成,而不是在創建和銷毀(掛載)命名空間的單獨步驟中完成。
/etc/netns
實際上
ip netns exec
已經在它自己的呼叫中管理了這種綁定掛載,但是在一個特定的地方:/etc/netns
. 每次ip netns exec foo
呼叫時,如果目錄和/或文件存在且匹配,它會自動將 mount 任何東西綁定/etc/netns/foo/*
到其匹配的/etc/*
. 由於這是一項旨在促進在單獨的命名空間中執行服務的多個實例的功能,因此應該首選此解決方案。例如,應用程序應該從 中的特定位置檢索其配置
/etc/
,/etc/my-app/
並且應該有一個 per-netns 不同的文件,該文件將包含將應用程序指向其工作目錄的內容,無論它在哪裡,例如/var/lib/my-app/nsX
(或什至/tmp/nsX
)。該目錄
/etc/my-app/
應該存在,並且可能包含某種模板文件,腳本將使用這些模板文件來準備執行實例,但其內容將隱藏在其他命名空間中,因為它將是綁定掛載的目標。mkdir -p /etc/netns # it is usually not provided by the distribution for instance in foo bar baz; do mkdir "/etc/netns/$instance" cp -a /etc/my-app /etc/netns/$instance/ done
然後使用腳本或手動,每個實例都應該被定制(數據的位置,pid 文件的位置等),相關的目錄添加到其他地方(在
/var/
或/run
(可能在一些引導工具/配置的幫助下,如tmpfiles.d)。一旦正確完成,應該能夠在不同的 netns 中執行應用程序的多個實例,就像這樣:
ip netns exec foo my-app ip netns exec bar my-app ip netns exec baz my-app
這不會在它們之間發生衝突(或者這意味著之前必須做更多的事情)。
unshare -m
加ip netns exec
在一起如果應用程序無法接收參數
/etc
或讓包裝器執行它並且堅持使用 only/var/lib/my-app
,那麼緊隨其後的綁定安裝ip netns exec
也將起作用:創建 ipnetns 命名空間:
ip netns add foo ip netns add bar ip netns add baz
準備網路配置:
ip -n foo link ....
執行應用程序:
unshare -m sh -c 'mount --bind /tmp/foo /var/lib/my-app; exec ip netns exec foo my-app' unshare -m sh -c 'mount --bind /tmp/bar /var/lib/my-app; exec ip netns exec bar my-app' unshare -m sh -c 'mount --bind /tmp/baz /var/lib/my-app; exec ip netns exec baz my-app'
雖然這些綁定坐騎稍後仍會消失,但它們將被現在實例化的
my-app
.它或多或少地複制了已經內置的功能,
ip netns exec
並帶有一個額外的中間掛載命名空間。包裝器
您還可以簡單地使用一個包裝腳本,該腳本採用額外的掛載參數來避免這個額外的掛載命名空間:
my-app.wrapper
(沒有檢查):#!/bin/sh mount --bind /tmp/"$1" /var/lib/my-app shift exec my-app "$@"
並執行:
ip netns exec foo my-app.wrapper foo
一體化
無論選擇哪種方法,都應該將其集成到一些啟動腳本中。例如
systemd
,實例化的特性(這裡是我的一個例子)可以與上述方法之一相結合,以動態創建和執行同一應用程序的新 netns 實例。