Docker核心技術Namespace淺析

簡介

Linux Namespace提供了一種內核級別隔離系統資源的方法,經過將系統的全局資源放在不一樣的Namespace中,來實現資源隔離的目的。不一樣Namespace的程序,能夠享有一份獨立的系統資源。目前Linux中提供了六類系統資源的隔離機制,分別是:node

  • Mount: 隔離文件系統掛載點
  • UTS: 隔離主機名和域名信息
  • IPC: 隔離進程間通訊
  • PID: 隔離進程的ID
  • Network: 隔離網絡資源
  • User: 隔離用戶和用戶組的ID

下面簡單的介紹一下這些Namespace的使用和功能。markdown

使用

涉及到Namespace的操做接口包括clone()setns()unshare()以及還有/proc下的部分文件。爲了使用特定的Namespace,在使用這些接口的時候須要指定如下一個或多個參數:網絡

  • CLONE_NEWNS: 用於指定Mount Namespace
  • CLONE_NEWUTS: 用於指定UTS Namespace
  • CLONE_NEWIPC: 用於指定IPC Namespace
  • CLONE_NEWPID: 用於指定PID Namespace
  • CLONE_NEWNET: 用於指定Network Namespace
  • CLONE_NEWUSER: 用於指定User Namespace

下面簡單概述一下這幾個接口的用法。dom

clone系統調用

能夠經過clone系統調用來建立一個獨立Namespace的進程,它的函數描述以下:ide

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

它經過flags參數來控制建立進程時的特性,好比新建立的進程是否與父進程共享虛擬內存等。好比能夠傳入CLONE_NEWNS標誌使得新建立的進程擁有獨立的Mount Namespace,也能夠傳入多個flags使得新建立的進程擁有多種特性,好比:函數

flags = CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC;

傳入這個flags那麼新建立的進程將同時擁有獨立的Mount NamespaceUTS NamespaceIPC Namespacespa

經過/proc文件查看已存在的Namespace

在3.8內核開始,用戶能夠在/proc/$pid/ns文件下看到本進程所屬的Namespace的文件信息。例如PID爲2704進程的狀況以下圖所示:Docker核心技術Namespace淺析其中4026531839是Namespace的ID,若是兩個進程的Namespace ID相同代表進程同處於一個命名空間中。這裏須要注意的是:只/proc/$pid/ns/對應的Namespace文件被打開,而且該文件描述符存在,即便該PID所屬的進程被銷燬,這個Namespace會依然存在。能夠經過掛載的方式打開文件描述符:code

touch ~/mnt
mount --bind /proc/2704/mnt ~/mnt

這樣就能夠保留住PID爲2704的進程的Mount Namespace了,即便2704進程被銷燬或者退出,ID爲4026531840的Mount Namespace依然會存在。對象

setns加入已存在的Namepspace

setns()函數能夠把進程加入到指定的Namespace中,它的函數描述以下:blog

int setns(int fd, int nstype);

它的參數描述以下:

  • fd參數:表示文件描述符,前面提到能夠經過打開/proc/$pid/ns/的方式將指定的Namespace保留下來,也就是說能夠經過文件描述符的方式來索引到某個Namespace
  • nstype參數:用來檢查fd關聯Namespace是否與nstype代表的Namespace一致,若是填0的話表示不進行該項檢查。

經過在程序中調用setns來將進程加入到指定的Namespace中。

unshare脫離到新的Namespace

unshare()系統調用用於將當前進程和所在的Namespace分離,並加入到一個新的Namespace中,相對於setns()系統調用來講,unshare()不用關聯以前存在的Namespace,只須要指定須要分離的Namespace就行,該調用會自動建立一個新的Namespaceunshare()的函數描述以下:

int unshare(int flags);

其中flags用於指明要分離的資源類別,它支持的flagsclone系統調用支持的flags相似,這裏簡要的敘述一下幾種標誌:

  • CLONE_FILES: 子進程通常會共享父進程的文件描述符,若是子進程不想共享父進程的文件描述符了,能夠經過這個flag來取消共享。
  • CLONE_FS: 使當前進程再也不與其餘進程共享文件系統信息。
  • CLONE_SYSVSEM: 取消與其餘進程共享SYS V信號量。
  • CLONE_NEWIPC: 建立新的IPC Namespace,並將該進程加入進來。

注意事項

這裏須要注意的是unshare()setns()系統調用對PID Namespace的處理不太相同,當unshare PID namespace時,調用進程會爲它的子進程分配一個新的PID Namespace,可是調用進程自己不會被移到新的Namespace中。並且調用進程第一個建立的子進程在新Namespace中的PID爲1,併成爲新Namespace中的init進程。setns()系統調用也是相似的,調用者進程並不會進入新的PID Namespace,而是隨後建立的子進程會進入。爲何建立其餘的Namespace時unshare()setns()會直接進入新的Namespace,而惟獨PID Namespace不是如此呢?由於調用getpid()函數獲得的PID是根據調用者所在的PID Namespace而決定返回哪一個PID,進入新的PID namespace會致使PID產生變化。而對用戶態的程序和庫函數來講,他們都認爲進程的PID是一個常量,PID的變化會引發這些進程奔潰。換句話說,一旦程序進程建立之後,那麼它的PID namespace的關係就肯定下來了,進程不會變動他們對應的PID namespace。

小結

經過上面簡單的概述,對於Namespace的操做有如下方式:

  • 一、能夠在進程剛建立的時候經過clone系統調用爲新進程分配一個或多個新的Namespace
  • 二、經過setns()將進程加入到已有的Namespace中。
  • 三、經過unshare()爲已存在的進程建立一個或多個新的Namespace

接下來詳細的介紹一下各個Namespace的功能和特性。

Mount Namespace

Mount Namespace用來隔離文件系統的掛載點,不一樣Mount Namespace的進程擁有不一樣的掛載點,同時也擁有了不一樣的文件系統視圖。Mount Namespace是歷史上第一個支持的Namespace,它經過CLONE_NEWNS來標識的。

掛載的概念

掛載的過程是經過mount系統調用完成的,它有兩個參數:一個是已存在的普通文件名,一個是能夠直接訪問的特殊文件,一個是特殊文件的名字。這個特殊文件通常用來關聯一些存儲卷,這個存儲卷能夠包含本身的目錄層級和文件系統結構。mount所達到的效果是:像訪問一個普通的文件同樣訪問位於其餘設備上文件系統的根目錄,也就是將該設備上目錄的根節點掛到了另一個文件系統的頁節點上,達到給這個文件系統擴充容量的目的。能夠經過/proc文件系統查看一個進程的掛載信息,具體作法以下:

cat /proc/$pid/mountinfo

其輸出結果以下:Docker核心技術Namespace淺析其中輸出的格式以下:Docker核心技術Namespace淺析

掛載傳播

進程在建立Mount Namespace時,會把當前的文件結構複製給新的Namespace,新的Namespace中的全部mount操做僅影響自身的文件系統。但隨着引入掛載傳播的特性,Mount Namespace變得並非徹底意義上的資源隔離,這種傳播特性使得多Mount Namespace之間的掛載事件能夠相互影響。掛載傳播定義了掛載對象之間的關係,系統利用這些關係來決定掛載對象中的掛載事件對其餘掛載對象的影響。其中掛載對象之間的關係描述以下:

  • 共享關係(MS_SHARED):一個掛載對象的掛載事件會跨Namespace共享到其餘掛載對象。
  • 從屬關係(MS_SLAVE): 傳播的方向是單向的,即只能從Master傳播到Slave方向。
  • 私有關係(MS_PRIVATE): 不一樣Namespace的掛載事件是互不影響的(默認選項)。
  • 不可綁定關係(MS_UNBINDABLE): 一個不可綁定的私有掛載,與私有掛載相似,可是不能執行掛載操做。

其中給掛載點設置掛載關係的例子以下:

mount --make-shared /mntS      # 將掛載點設置爲共享關係屬性
 mount --make-private /mntP     # 將掛載點設置爲私有關係屬性
 mount --make-slave /mntY       # 將掛載點設置爲從屬關係屬性
 mount --make-unbindable /mntU  # 將掛載點設置爲不可綁定屬性

注意在設置私有關係屬性時,在本命名空間下的這個掛載點是Slave,而父命名空間下這個掛載點是Master,掛載傳播的方向只能由Master傳給Slave。

綁定掛載

綁定掛載的引入使得mount的其中一個參數不必定要是一個特殊文件,也能夠是該文件系統上的一個普通文件目錄。Linux中綁定掛載的用法以下:

mount --bind /home/work /home/alpha
mount -o bind /home/work /home/alpha

其中/home/work是磁盤上的存在的一個目錄,而不是一個文件設備(好比磁盤分區)。若是須要將Linux中兩個文件目錄連接起來,能夠經過綁定掛載的方式,掛載後的效果相似於在兩個文件目錄上創建了硬連接。在綁定掛載中同時會涉及到掛載的傳播特性,掛載傳播的特性參考:Linux綁定掛載

UTS Namespace

UTS Namespace提供了主機名和域名的隔離,也就是struct utsname裏的nodenamedomainname兩個字段。不一樣Namespace中能夠擁有獨立的主機名和域名。那麼爲何須要對主機名和域名進行隔離呢?由於主機名和域名能夠用來代替IP地址,若是沒有這一層隔離,同一主機上不一樣的容器的網絡訪問就可能出問題。

IPC Namespace

IPC Namespace是對進程間通訊的隔離,進程間通訊常見的方法有信號量、消息隊列和共享內存。IPC Namespace主要針對的是SystemV IPC和Posix消息隊列,這些IPC機制都會用到標識符,好比用標識符來區分不一樣的消息隊列,IPC Namespace要達到的目標是相同的標識符在不一樣的Namepspace中表明不一樣的通訊介質(好比信號量、消息隊列和共享內存)。原文

更多深度文章,關注:二進制社區

相關文章
相關標籤/搜索