Docker核心原理解讀-namespace

1.容器所在的運行環境統稱爲宿主機,能夠是硬件服務器、虛擬機(其實,docker容器也支持在容器中運行容器)

2.Docker容器本質上是宿主機的進程,經過namespace實現隔離,經過cgroups實現了資源限制,經過寫時複製機制(copy-on-write)實現了高效的文件操做。 

namespace

3.想要實現一個資源隔離的容器,須要6項隔離,Linux內核提供了這6種namespace隔離的系統調用,在同一個namespace下的進程能夠感知彼此的變化,但不瞭解外界的變化,所以,可讓容器中的進程覺得本身在一個獨立的系統環境中,達到獨立和隔離的目的。


4.四種進行namespace隔離的API操做
  • clone() //在建立新進程的同時建立namespace
  • setns() //加入一個已經存在的namespace
  • unshared() //在原先進程上進行namespace隔離,不須要啓動一個新進程
  • /proc下的部分文件 //3.8版本內核開始,能夠在/proc/[pid]/ns文件下看到指向不一樣namespace號的文件
使用時,須要指定以上六個參數中的一個或者多個,來肯定隔離的是哪六項namespace,經過|(位或)操做來實現 
  • UTS(UNIX Time-sharing System) namespace
它提供了主機名和域名的隔離,這樣每一個Docker容器就能夠擁有獨立的主機名和域名了,在網絡上就能夠被視做一個獨立的節點,而非宿主機上的一個進程。因此,在Docker中,每一個鏡像基本都以自身所提供的服務名稱來命名鏡像的hostname,且不會對宿主機產生任何影響。
  • IPC namespace
進程間通訊(Inter-Process Communication,IPC)涉及的IPC資源包括常見的信號量、消息隊列和共享內存。申請IPC資源就申請了一個全局惟一的32位ID,因此IPC namespace中實際上包含了系統IPC標識符以及實現POSIX消息隊列的文件系統。在同一個IPC namespace下的進程彼此可見,不一樣IPC namespace下的進程則互不可見。
  • PID namespace
它對進程PID從新編號,所以兩個不一樣namespace下的進程能夠有相同的PID。每一個PID namespace都有本身的計數程序。內核爲全部PID namespace維護了一個樹狀結構,最頂層的是系統初始建立的,即root namespace,它建立的新PID namespace稱爲child namespace,原來的就稱爲parent namespace,從而造成一個層級體系。所屬的父節點能夠看到子節點中的進程,能夠經過信號等方式對子節點產生影響,反之則不行。
  • 每一個PID namespace中的第一個進程都會像傳統Linux中的init進程同樣擁有特權
  • 一個namespace中的進程,不可能經過kill或ptrace影響父節點或兄弟節點中的進程,由於其餘節點的PID在這個namespace中沒有意義
  • 若是在新的PID namespace中從新掛載/proc文件系統,會發現其下只顯示同屬一個PID namespace中的其餘進程
  • root namespace中能夠看到全部進程,而且遞歸包含全部子節點中的進程
所以,想要在外部監控Docker中運行程序,能夠監控Docker daemon所在的PID namespace下的全部進程及其子進程,再進行篩選。
  • mount namespace
它經過隔離文件系統掛載點對隔離文件系統提供支持。隔離後,不一樣mount namespace中的文件結構發生變化也互不影響。進程在建立mount namespace時,會把當前的文件結構複製給新的namespace,新的namespace中的全部mount操做都隻影響自身的文件系統,對外界不會產生任何影響。
  • network namespace
主要提供關於網絡資源的隔離,包括網絡設備、IPv4和IPv6協議棧、IP路由表防火牆、嵌套字等。一個物理的網絡設備最多存在於一個network namespace中,能夠經過建立虛擬網絡設備對在不一樣的network namespace間建立通道來通訊。說到network namespace時,指的未必是真正的網絡隔離,而是把網絡獨立出來,給外部用戶一種透明的感受,似乎在與一個獨立的網絡實體進行通訊。容器的經典作法就是建立一個weth pair,一端放置在新的namespace中,一端放在原先的namespace中鏈接物理網絡設備,再把多個設備接入網橋或者進行路由轉發來通訊。
  • user namespace
主要隔離了安全相關的標識符和屬性,包括用戶ID、用戶組ID、root目錄、key(密鑰)以及特殊權限。即一個普通用戶的進程經過clone()建立的新進程在新的user namespace中能夠擁有不一樣的用戶和用戶組。這意味着一個進程在容器外屬於一個沒有特權的普通用戶,可是它建立的容器卻屬於擁有全部權限的超級用戶,爲容器提供了極大的自由。(例子:當用戶運行容器,容器內部的root用戶並不等於宿主機內的root用戶,而是映射到宿主上的普通用戶。)
  • user namespace被建立後,第一個進程被賦予了該namespace的所有權限,這樣該init進程就能夠完成全部必要的初始化工做;
  • 從namespace內部觀察到的UID和GID已經與外部不一樣了,默認顯示爲65534,表示還沒有與外部namespace用戶映射。咱們須要對user namespace內部的這個初始user和其外部namespace某個用戶創建映射,這樣能夠保證當涉及到一些對外部namespace的操做時,系統能夠檢驗其權限(好比發送一個信號或操做某個文件)。一樣用戶組也要創建映射;
  • 還有一點雖然不能從輸出中看出來,可是值得注意。用戶在新namespace中有所有權限,可是他在建立他的父namespace中不含任何權限。就算調用和建立他的進程有所有權限也是如此。因此哪怕是root用戶調用了clone()在user namespace中建立出的新用戶在外部也沒有任何權限;
  • 最後,user namespace的建立實際上是一個層層嵌套的樹狀結構。最上層的根節點就是root namespace,新建立的每一個user namespace都有一個父節點user namespace以及零個或多個子節點user namespace,這一點與PID namespace很是類似。
相關文章
相關標籤/搜索