理解mount namespace
用戶通常使用mount命令來掛載普通文件系統,但實際上mount能掛載的東西非常多,甚至連現在功能完善的Linux系統,其內核的正常運行也都依賴于掛載功能,比如掛載根文件系統/
。其實所有的掛載功能和掛載信息都由內核負責提供和維護,mount命令只是發起了mount()系統調用去請求內核。
mount namespace可隔離出一個具有獨立掛載點信息的運行環境,內核知道如何去維護每個namespace的掛載點列表。即「每個namespace之間的掛載點列表是獨立的,各自掛載互不影響」。
內核將每個進程的掛載點信息保存在/proc/<pid>/{mountinfo,mounts,mountstats}
三個文件中:
$?ls?-1?/proc/$$/mount*
/proc/26276/mountinfo
/proc/26276/mounts
/proc/26276/mountstats
「具有獨立的掛載點信息,意味著每個mnt namespace可具有獨立的目錄層次」,這在容器中起了很大作用:容器可以掛載只屬于自己的文件系統。
當創建mount namespace時,內核將拷貝一份當前namespace的掛載點信息列表到新的mnt namespace中,此后兩個mnt namespace就沒有了任何關系(不是真的毫無關系,參考后文shared subtrees)。
創建mount namespace的方式是使用unshare命令的--mount, -m
選項:
#?創建mount+uts?namespace
#?-m或--mount表示創建mount?namespace
#?可同時創建具有多種namespace類型的namespace
unshare?--mount?--uts?<program>
下面做一個簡單的試驗,在root namespace中掛載1.iso文件到/mnt/iso1目錄,在新建的mount+uts namespace中掛載2.iso到/mnt/iso2目錄:
[~]->$?cd
[~]->$?mkdir?iso
[~]->$?cd?iso
[iso]->$?mkdir?-p?iso1/dir1
[iso]->$?mkdir?-p?iso2/dir2??
[iso]->$?mkisofs?-o?1.iso?iso1??#?將iso1目錄制作成鏡像文件1.iso
[iso]->$?mkisofs?-o?2.iso?iso2??#?將iso2目錄制作成鏡像文件2.iso
[iso]->$?ls
1.iso??2.iso??iso1??iso2
[iso]->$?sudo?mkdir?/mnt/{iso1,iso2}
[iso]->$?ls?-l?/proc/$$/ns/mnt
lrwxrwxrwx?1?...?/proc/26276/ns/mnt?->?'mnt:[4026531840]'#?在root?namespace中掛載1.iso到/mnt/iso1目錄
[iso]->$?sudo?mount?1.iso?/mnt/iso1??
mount:?/mnt/iso:?WARNING:?device?write-protected,?mounted?read-only.
[iso]->$?mount?|?grep?iso1
/home/longshuai/iso/1.iso?on?/mnt/iso1?type?iso9660#?創建mount+uts?namespace
[iso]->$?sudo?unshare?-m?-u?/bin/bash
#?雖然這個namespace是mount+uts的namespace
#?但注意mnt?namespace和uts?namespace的inode并不一樣
root@longshuai-vm:/home/longshuai/iso#?ls?-l?/proc/$$/ns
lrwxrwxrwx?...?cgroup?->?'cgroup:[4026531835]'
lrwxrwxrwx?...?ipc?->?'ipc:[4026531839]'
lrwxrwxrwx?...?mnt?->?'mnt:[4026532588]'
lrwxrwxrwx?...?net?->?'net:[4026531992]'
lrwxrwxrwx?...?pid?->?'pid:[4026531836]'
lrwxrwxrwx?...?pid_for_children?->?'pid:[4026531836]'
lrwxrwxrwx?...?user?->?'user:[4026531837]'
lrwxrwxrwx?...?uts?->?'uts:[4026532589]'#?修改主機名為ns1
root@longshuai-vm:/home/longshuai/iso#?hostname?ns1
root@longshuai-vm:/home/longshuai/iso#?exec?$SHELL#?在namespace中,可以看到root?namespace中的掛載信息
root@ns1:/home/longshuai/iso#?mount?|?grep?'iso1'?
/home/longshuai/iso/1.iso1?on?/mnt/iso1?type?iso9660#?namespace中掛載2.iso2
root@ns1:/home/longshuai/iso#?mount?2.iso2?/mnt/iso2/
mount:?/mnt/iso2:?WARNING:?device?write-protected,?mounted?read-only.
root@ns1:/home/longshuai/iso#?mount?|?grep?'iso[12]'
/home/longshuai/iso/1.iso1?on?/mnt/iso1?type?iso9660
/home/longshuai/iso/2.iso2?on?/mnt/iso2?type?iso9660#?在namespace中卸載iso1
root@ns1:/home/longshuai/iso#?umount?/mnt/iso1/
root@ns1:/home/longshuai/iso#?mount?|?grep?'iso[12]'?
/home/longshuai/iso/2.iso2?on?/mnt/iso2?type?iso9660
root@ns1:/home/longshuai/iso#?ls?/mnt/iso1/
root@ns1:/home/longshuai/iso#?ls?/mnt/iso2
dir2####?打開另一個Shell終端窗口
#?iso1掛載仍然存在,且沒有iso2的掛載信息
[iso]->$?mount?|?grep?iso
/home/longshuai/iso/1.iso1?on?/mnt/iso1?type?iso9660
[iso]->$?ls?/mnt/iso2
[iso]->$?ls?/mnt/iso1
dir1
以上是mount namespace的基本內容,只有一個關鍵點:創建mnt namespace時會拷貝當前namespace的掛載點信息,之后兩個namespace就沒有關系了。
mnt namespace: shared subtrees
Linux的每個掛載點都具有一個決定該掛載點「是否共享子掛載點」的屬性,稱為shared subtrees。該屬性以決定某掛載點之下新增或移除子掛載點時,是否同步影響它【副本】掛載點。
簡單說說shared subtrees特性,該特性有什么用呢?以mnt namespace為例來簡單介紹一下shared subtrees特性。
假設基于root namespace創建了一個mnt namespace(ns1),那么ns1將具有當前root namespace的掛載點信息拷貝。如果此時新插入了一塊磁盤并對其分區格式化,然后在root namespace中對其進行掛載,默認情況下,在ns1中將看不到新掛載的文件系統。這種默認行為可以通過修改shared subtrees屬性來改變。
其實,用戶創建namespace,其目的一般是希望創建完全隔離的運行環境,所以默認情況下,拷貝掛載點信息時不會拷貝shared subtrees屬性,而是將mount namespace中的所有掛載點的shared subtrees屬性設置為private。
所以,假如namespace ns1中/mnt/foo是一個掛載點目錄,基于ns1創建了一個mnt namespace ns2,在默認情況下:
-
如果此時在ns1中新增一個掛載點/mnt/foo/bar,將不會影響到ns2中的/mnt/foo
-
如果此時在ns2中新增一個掛載點/mnt/foo/baz,也不會影響到ns1中的/mnt/foo
-
移除掛載點操作也一樣
但這種默認行為可以改變。
unshare
有一個選項--propagation private|shared|slave|unchanged
可控制創建mnt namespace時掛載點的共享方式。
-
private:表示新創建的mnt namespace中的掛載點的shared subtrees屬性都設置為private,即ns1和ns2的掛載點互不影響
-
shared:表示新創建的mnt namespace中的掛載點的shared subtrees屬性都設置為shared,即ns1或ns2中新增或移除子掛載點都會同步到另一方
-
slave:表示新創建的mnt namespace中的掛載點的shared subtrees屬性都設置為slave,即ns1中新增或移除子掛載點會影響ns2,但ns2不會影響ns1
-
unchanged:表示拷貝掛載點信息時也拷貝掛載點的shared subtrees屬性,也就是說掛載點A原來是shared,在mnt namespace中也將是shared
-
不指定
--progapation
選項時,創建的mount namespace中的掛載點的shared subtrees默認值是private
例如:
#參考https://www.junmajinlong.com/linux/mount_bind/
# root namespace:ns0
$?sudo?mount?--bind?foo?bar
$?sudo?mount?--make-shared?bar????#?掛載點設置為shared#?創建mnt namespace:ns1,且也拷貝shared屬性
$?PS1="ns1$?"?sudo?unshare?-m?-u?--propagation?unchanged?sh
[ns1]$?grep?'foo'?/proc/self/mountinfo?
944?682?8:5?foo?bar?rw,relatime?shared:1#?在ns1中bar掛載點下新增子掛載點,子掛載點將同步到ns0
#?因為foo和bar已綁定,且bar的屬性是shared,所以還會同步到foo目錄下
[ns1]$?sudo?mount?--bind?baz?bar/subfoo
[ns1]$?tree?foo?bar?
foo
└──?subfoo└──?subbaz
bar
└──?subfoo└──?subbaz
[ns1]$?grep?'foo'?/proc/self/mountinfo
944?682?8:5?foo?bar?rw,relatime?shared:1
945?944?8:5?baz?bar/subfoo?rw,relatime?shared:1
947?682?8:5?baz?foo/subfoo?rw,relatime?shared:1#?第二個窗口會話中查看ns0的掛載點信息
#?已經同步過來了
$?grep?'foo'?/proc/self/mountinfo
622?29?8:5??foo?bar?rw,relatime?shared:1
948?622?8:5?baz?bar/subfoo?rw,relatime?shared:1
946?29?8:5??baz?foo/subfoo?rw,relatime?shared:1
$?tree?foo?bar?
foo
└──?subfoo└──?subbaz
bar
└──?subfoo└──?subbaz