01.12 Linux Namespace淺析

Linux Namespace淺析

編者注:Namespace是將內核的全局資源做封裝,使得每個Namespace都有一份獨立的資源,因此不同的進程在各自的Namespace內對同一種資源的使用不會互相干擾。

目前Linux內核總共支持以下6種Namespace:

  • IPC:隔離System V IPC和POSIX消息隊列。
  • Network:隔離網絡資源。
  • Mount:隔離文件系統掛載點。
  • PID:隔離進程ID。
  • UTS:隔離主機名和域名。
  • User:隔離用戶ID和組ID。

Linux對Namespace的操作,主要是通過clone、setns和unshare這3個系統調用來完成的,clone創建新進程時,接收一個叫flags的參數,這些flag包括CLONE_NEWNS、CLONE_NEWIPC、CLONE_NEWUTS、CLONE_NEWNET(Mount namespace)、CLONE_NEWPID和CLONE_NEWUSER,用於創建新的namespace,這樣clone創建出來新進程之後就屬於新的namespace了,後續新進程創建的進程默認屬於同一namespace。

如果想要給已存在進程設置新的namespace,可通過unshare函數(long unshare(unsigned long flags))完成設置,其入參flags表示新的namespace。當想要給已存在進程設置已存在的namespace,可通過setns函數(int setns(int fd, int nstype))來完成設置,每個進程在procfs目錄下存儲其相關的namespace信息,可找到已存在的namesapce,然後通過setns設置即可:

<code>[root@centos~]#ls-l/proc/10401/ns
總用量0
lrwxrwxrwx1rootroot01月1211:36ipc->ipc:[4026531839]
lrwxrwxrwx1rootroot01月1211:36mnt->mnt:[4026531840]
lrwxrwxrwx1rootroot01月1211:36net->net:[4026531956]
lrwxrwxrwx1rootroot01月1211:36pid->pid:[4026531836]
lrwxrwxrwx1rootroot01月1211:36user->user:[4026531837]
lrwxrwxrwx1rootroot01月1211:36uts->uts:[4026531838]
/<code>

上述每個虛擬文件對應該進程所處的namespace,如果其他進程想進入該namespace,open該虛擬文件獲取到fd,然後傳給setns函數的fd入參即可,注意虛擬文件type和nstype要對應上。

目前Linux內核總共支持以下6種Namespace,分別是IPC、Network、Mount、PID、UTS、User

IPC

IPC也就是進程間通信,Linux下有多種進程間通信,比如socket、共享內存、Posix消息隊列和SystemV IPC等,這裡的IPC namespace針對的是SystemV IPC和Posix消息隊列,其會用標識符表示不同的消息隊列,進程間通過找到標識符對應的消息隊列來完成通信,IPC namespace做的事情就是相同的標識符在不同namespace上對應不同的消息隊列,這樣不同namespace的進程無法完成進程間通信。

Network

Network Namespace隔離網絡資源,每個Network Namespace都有自己的網絡設備、IP地址、路由表、/proc/net目錄、端口號等。每個Network Namespace會有一個loopback設備(除此之外不會有任何其他網絡設備)。因此用戶需要在這裡面做自己的網絡配置。IP工具已經支持Network Namespace,可以通過它來為新的Network Namespace配置網絡功能。

Mount

Mount namesapce用戶隔離文件系統掛載點,每個進程能看到的文件系統都記錄在/proc/xx/mounts裡。在創建了一個新的Mount Namespace後,進程系統對文件系統掛載/卸載的動作就不會影響到其他Namespace。

PID

PID Namespace用於隔離進程PID號,這樣一來,不同的Namespace裡的進程PID號就可以是一樣的了。當創建一個PID Namespace時,第一個進程的PID號是1,也就是init進程。init進程有一些特殊之處,例如init進程需要負責回收所有孤兒進程的資源。另外,發送給init進程的任何信號都會被屏蔽,即使發送的是SIGKILL信號,也就是說,在容器內無法“殺死”init進程。

注意,但是當用ps命令查看系統的進程時,會發現竟然可以看到host的所有進程:

Linux Namespace淺析

這是因為ps命令是從procfs讀取信息的,而procfs並沒有得到隔離。雖然能看到這些進程,但由於它們其實是在另一個PID Namespace中,因此無法向這些進程發送信號。

UTS

UTS Namespace用於對主機名和域名進行隔離,也就是uname系統調用使用的結構體structutsname裡的nodename和domainname這兩個字段,UTS這個名字也是由此而來的。為什麼需要uts namespace呢,因為為主機名可以用來代替IP地址,比如局域網通過主機名訪問機器。

User

User Namespace用來隔離用戶資源,比如一個進程在Namespace裡的用戶和組ID與它在host裡的ID可以不一樣,這樣可以做到,一個host的普通用戶可以在該容器(user namespace)下擁有root權限,但是它的特權被限定在容器內。(容器內的這類root用戶,實際上還是有很多特權操作不能執行,基本上如果這個特權操作會影響到其他容器或者host,就不會被允許)

小結

一般namespace都是和cgroup結合來使用的,但是直接操作Namespace和Cgroup並不是很容易,因此docker的出現就顯得有必要了,Docker通過Libcontainer來處理這些底層的事情。這樣一來,Docker只需要簡單地調用Libcontainer的API,就能將完整的容器搭建起來。而作為Docker的用戶,就更不用操心這些事情了,只需要通過一兩條簡單的Docker命令啟動容器即可。

來源:https://mp.weixin.qq.com/s/4QGaaeO3u97_WG6k726Y1A


分享到:


相關文章: