天天看點

《Docker進階與實戰》——2.4節SparkContext概述

本節書摘來自華章社群《docker進階與實戰》一書中的第2章,第2.4節sparkcontext概述,作者華為docker實踐小組,更多章節内容可以通路雲栖社群“華章社群”公衆号檢視

2.4 namespace介紹

2.4.1 namespace是什麼

namespace是将核心的全局資源做封裝,使得每個namespace都有一份獨立的資源,是以不同的程序在各自的namespace内對同一種資源的使用不會互相幹擾。

這樣的解釋可能不清楚,舉個例子,執行sethostname這個系統調用時,可以改變系統的主機名,這個主機名就是一個核心的全局資源。核心通過實作uts namespace,可以将不同的程序分隔在不同的uts namespace中,在某個namespace修改主機名時,另一個namespace的主機名還是保持不變。

目前linux核心總共實作了6種namespace:

ipc:隔離system v ipc和posix消息隊列。

network:隔離網絡資源。

mount:隔離檔案系統挂載點。

pid:隔離程序id。

uts:隔離主機名和域名。

user:隔離使用者id群組id。

2.4.2 namespace的接口和使用

對namespace的操作,主要是通過clone、setns和unshare這3個系統調用來完成的。

clone可以用來建立新的namespace。它接受一個叫flags的參數,這些flag包括clone_newns、clone_newipc、clone_newuts、clone_newnet、clone_newpid和clone_newuser,我們通過傳入這些clone_new*來建立新的namespace。這些flag對應的namespace都可以從字面上看出來,除了clone_newns,這是用來建立mount namespace的。指定了這些flag後,由clone建立出來的新程序,就位于全新的namespace裡了,并且很自然地這個新程序以後建立出來的程序,也都在這個namespace中。

示 mount namespace是第一個實作的namespace,當初實作時并不是為了實作linux容器,是以也就沒有預料到會有新的namespace出現,是以用了clone_newns而不是clone_newmnt之類的名字。

那麼,能不能為已有的程序建立新的namespace呢?答案是可以,unshare就是用來達到這個目的的。調用這個系統調用的程序,會被放進新建立的namespace裡,要建立什麼namespace由flags參數指定,可以使用的flag也就是上面提到的那些。

以上兩個系統調用都是用來建立新的namespace的,而setns則可以将程序放到已有的namespace裡,問題是如何指定已有的namespace?答案在procfs裡。每個程序在procfs下都有一個目錄,在那裡面就有namespace相關的資訊,如下。

這裡每個虛拟檔案都對應了這個程序所處的namespace。是以,如果另一個程序要進入這個程序的namespace,可以通過open系統調用打開這裡面的虛拟檔案并得到一個檔案描述符,然後把檔案描述符傳給setns,調用傳回成功的話,就進入這個程序的namespace了。

docker exec指令的實作原理就是setns。

以下是一個簡單的程式,在linux終端調用這個程式就會進入新的namespace,同時也可以打開另一個終端,這個終端是在host的namespace裡,這樣就可以對比兩個namespace的差別了。

這個程式建立了uts namespace,可以通過修改flag,建立其他namespace,也可以建立幾個namespace的組合。這個程式将會用來為下面的内容做示範。

2.4.3 各個namespace介紹

uts namespace

uts namespace用于對主機名和域名進行隔離,也就是uname系統調用使用的結構體struct utsname裡的nodename和domainname這兩個字段,uts這個名字也是由此而來的。

那麼,為什麼要使用uts namespace做隔離?這是因為主機名可以用來代替ip位址,是以,也就可以使用主機名在網絡上通路某台機器了,如果不做隔離,這個機制在容器裡就會出問題。

調用之前的程式後,在namespace終端執行以下指令:

可以看到,host的主機名并沒有變化,這就是namespace所起的作用。

ipc namespace

ipc是inter-process communication的簡寫,也就是程序間通信。linux提供了很多種程序間通信的機制,ipc namespace針對的是systemv ipc和posix消息隊列。這些ipc機制都會用到辨別符,例如用辨別符來差別不同的消息隊列,然後兩個程序通過辨別符找到對應的消息隊列進行通信等。

ipc namespace能做到的事情是,使相同的辨別符在兩個namespace中代表不同的消息隊列,這樣也就使得兩個namespace中的程序不能通過ipc程序通信了。

舉個例子,在namespace終端建立了一個消息隊列:

在這裡看不到任何消息隊列,ipc隔離的效果達到了。

pid namespace

pid namespace用于隔離程序pid号,這樣一來,不同的namespace裡的程序pid号就可以是一樣的了。

當建立一個pid namespace時,第一個程序的pid号是1,也就是init程序。init程序有一些特殊之處,例如init程序需要負責回收所有孤兒程序的資源。另外,發送給init程序的任何信号都會被屏蔽,即使發送的是sigkill信号,也就是說,在容器内無法“殺死”init程序。

但是當用ps指令檢視系統的程序時,會發現竟然可以看到host的所有程序:

mount namespace

mount namespace用來隔離檔案系統挂載點,每個程序能看到的檔案系統都記錄在/proc/$$/mounts裡。在建立了一個新的mount namespace後,程序系統對檔案系統挂載/解除安裝的動作就不會影響到其他namespace。

之前看到,建立pid namespace後,由于procfs沒有改變,是以通過ps指令看到的仍然是host的程序樹,其實可以通過在這個pid namespace裡挂載procfs來解決這個問題,如下:

可如果同時使用mount namespace和pid namespace,新的namespace裡的程序和host上的程序将會看到各自的procfs,故而也就不存在上面的問題了。

network namespace

這個namespace會對網絡相關的系統資源進行隔離,每個network namespace都有自己的網絡裝置、ip位址、路由表、/proc/net目錄、端口号等。網絡隔離的必要性是很明顯的,舉一個例子,在沒有隔離的情況下,如果兩個不同的容器都想運作同一個web應用,而這個應用又需要使用80端口,那就會有沖突了。

新建立的network namespace會有一個loopback裝置,除此之外不會有任何其他網絡裝置,是以使用者需要在這裡面做自己的網絡配置。ip工具已經支援network namespace,可以通過它來為新的network namespace配置網絡功能。首先建立network namespace:

容器的網絡配置是一個很大的話題,後面有專門的章節講解,是以這裡暫不展開。

user namespace

user namespace用來隔離使用者群組id,也就是說一個程序在namespace裡的使用者群組id與它在host裡的id可以不一樣,這樣說可能讀者還不了解有什麼實際的用處。user namespace最有用的地方在于,host的普通使用者程序在容器裡可以是0号使用者,也就是root使用者。這樣,程序在容器内可以做各種特權操作,但是它的特權被限定在容器内,離開了這個容器它就隻有普通使用者的權限了。

意 容器内的這類root使用者,實際上還是有很多特權操作不能執行,基本上如果這個特權操作會影響到其他容器或者host,就不會被允許。

在host上,可以看到我們是lizf使用者。

可以看到,我們成功地将lizf使用者映射成容器裡的root使用者了。對于gid,也可以做類似的操作。

至此,關于namespace和cgroup的知識就講解完了,可以看到,namespace和cgroup的使用是很靈活的,同時這裡面又有不少需要注意的地方,是以直接操作namespace和cgroup并不是很容易。正是因為這些原因,docker通過libcontainer來處理這些底層的事情。這樣一來,docker隻需要簡單地調用libcontainer的api,就能将完整的容器搭建起來。而作為docker的使用者,就更不用操心這些事情了,而隻需要學習docker的使用手冊,就能通過一兩條簡單的docker指令啟動容器。