您好,登錄后才能下訂單哦!
這篇文章主要介紹“Docker中Namespace隔離機制是什么”,在日常操作中,相信很多人在Docker中Namespace隔離機制是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Docker中Namespace隔離機制是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
Docker 容器能夠在服務器中高效運行,離不開容器底層技術的支持。
Docker 目前采用標準的 C/S 架構,即服務端—客戶端架構,服務端用于管理數據,客戶端負責與用戶交互,將獲取的用戶信息交由服務器處理,如圖所示????
那么上面的名詞是什么意思呢?我這里重新畫了一幅圖來解析
服務器與客戶機既可以運行在同一臺機器上,也可以運行在不同機器上,通過 Socket(套接字)或者 RESTful API 進行通信。
Docker 服務端也就是 Docker daemon,一般在宿主機后臺運行,接收來自客戶的請求、并處理這些請求。在設計上,Docker 服務端是一個模塊化的架構,通過專門的 Engine 模塊來分發、管理各個來自客戶端的任務。
Docker 服務端默認監聽本地的 unix:///var/run/Docker.sock 套接字,只允許本地的 root 用戶或 Docker 用戶組成員訪問,可以通過 -H 參數來修改監聽的方式。
例如,讓服務器監聽本地的 TCP 連接 1234 端口,代碼如下所示:
此外,Docker 還支持通過 HTTPS 認證的方式來驗證訪問。
在 Debian/Ubuntu14.04 等使用 upstart 管理啟動服務的系統中,Docker 服務端的默認啟動配置文件在 /etc/default/Docker。
在使用 systemd 管理啟動服務的系統,配置文件在 /etc/systemd/system/Docker.service.d/Docker.conf。
用戶不能與服務端直接交互,Docker 客戶端為用戶提供一系列可執行命令,用戶通過這些命令與 Docker 服務端進行交互。
用戶使用的 Docker 可執行命令就是客戶端程序。與 Docker 服務端不同的是,客戶端發送命令后,等待服務端返回信息,收到返回信息后,客戶端立刻執行結束并退出。用戶執行新的命令時,需要再次調用客戶端命令。
同樣,客戶端默認通過本地的 unix:///var/run/Docker.sock 套接字向服務端發送命令。如果服務端不在默認監聽的地址,則需要用戶在執行命令時指定服務端地址,如圖所示
例如:假設服務端監聽本地的 TCP 連接 1234 端口 tcp://127.0.0.1:1234,只有通過 -H 參數指定了正確的地址信息才能連接到服務端。
首先,查看 Docker 信息,示例代碼如下:
從以上示例中可以看到,Docker 并沒有連接到服務端,但 Docker 客戶端仍可以為用戶提供服務。
然后,通過命令指定正確的地址信息,再次查看 Docker 信息,示例代碼如下:
從以上示例中可以看到,指定了正確的地址信息之后,Docker 順利連接到服務端。
Docker 服務端運行在主機上, 通過 Socket 連接從客戶端訪問,服務端從客戶端接受命令并管理運行在主機上的容器。
Linux 操作系統中,容器用來實現“隔離”的技術稱為 Namespace(命名空間)。
Namespace 技術實際上修改了應用進程看待整個計算機的 “視圖”,即應用進程的 “視線” 被操作系統做了限制,只能 “看到” 某些指定的內容,如圖所示
但對于宿主機來說,這些被進行“隔離”的進程跟其他進程并沒有太大區別。
下面運行一個 CentOS 7 容器,示例代碼如下:
從以上示例中可以看到,bash 是這個容器內部的第 1 號進程,即 PID=1,而這個容器里一共只有兩個進程在運行,這就意味著,前面執行的 /bin/sh,以及剛剛執行的 ps,已經被 Docker 隔離在一個與宿主機完全不同的空間當中。
理論上,每當在宿主機上運行一個 /bin/sh 程序,操作系統都會給它分配一個進程編號,例如,PID=100。這個編號是進程的唯一標識,就像員工的工號一樣。所以,PID=100,可以粗略地理解為這個 /bin/sh 是公司里的第 100 號員工。
而現在,要通過 Docker 把 /bin/sh 運行在一個容器當中。這時,Docker 就會在這個第 100 號員工入職時給他施一個 “障眼法” 讓他永遠看不到前面的其他 99 個員工,這樣,他就會以為自己就是公司里的第 1 號員工。
這種機制其實就是對被隔離應用的進程空間做了手腳,使這些進程只能看到重新計算過的進程號,例如 PID=1。可實際上,它們在宿主機的操作系統里,還是原來的第 100號 進程,如圖所示
下面通過宿主機查看容器進程號,示例代碼如下:
在宿主機中通過容器的 ID 號查看其進程號,可以看出其進程號為 7548,這就是 Linux 里的 Namespace 機制。
在 Linux 系統中創建線程調用是 clone() 函數,例如:int pid = clone(main_function, stack_size, SIGCHLD, NULL);
這個調用會創建一個新的進程,并且返回它的進程號。
系統調用 clone() 創建一個新進程時,可以在參數中指定 CLONE_NEWPID ,例如:int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
這時,新創建的這個進程將會 “看到” 一個全新的進程空間,在這空間里,它的進程號是 1。在宿主機真實的進程空間里,這個進程的真實進程號不變。
當然,可以多次執行上面的 clone() 調用,這樣就會創建多個 PID Namespace,而每個 Namespace 里的應用進程,都會認為自己是當前容器里的第 1 號進程,它們既看不到宿主機里真正的進程空間,也看不到其他 PID Namespace 里的具體情況。
命名空間分為多種類型,對應用程序進行不同程度的隔離,下面挨個介紹。
Mount Namespace 將一個文件系統的頂層目錄與另一個文件系統的子目錄關聯起來,使其成為一個整體。該子目錄稱為掛載點,這個動作稱為掛載。
UTS(UNIX Time-sharing System,UNIX 分時系統)Namespace 提供主機名和域名的隔離,使子進程有獨立的主機名和域名,這一特性在 Docker 容器技術中被運用,使 Docker 容器在網絡上被視作一個獨立的節點,而不僅僅是宿主機上的一個進程。
IPC(Inter-Process Communication,進程間通信)Namespace 是 UNIX 與 Linux 下進程間通信的一種方式。
IPC 有共享內存、信號量、消息隊列等方式。此外,也需要對 IPC 進行隔離,如此一來,只有在同一個 Namespace 下的進程才能相互通信。
IPC 需要有一個全局的 ID,既然是全局的,就意味著 Namespace 需要對這個 ID 號進行 隔離,不能讓其他 Namespace 的進程 “看到”。
PID Namespace 用來隔離進程的 ID 空間,使不同 PID Namespace 里的進程 ID 號可以重復且相互之間不影響。
PID Namespace 可以嵌套,也就是說有父子關系。在當前 Namespace 里面創建的所有新的 Namespace 都是當前 Namespace 的子 Namespace。
在父 Namespace 里面可以 “看到” 所有子 Namespace 里的進程信息,而在子 Namespace 里看不到父 Namespacelode 與其他子 Namespacelode 進程信息,如圖所示????
每個容器擁有獨立的網絡設備,IP 地址,IP 路由表,/proc/net 目錄,端口號等。這也使得一個 host 上多個容器內的網絡設備都是互相隔離的。
User Namespace 用來隔離 User 權限相關的 Linux 資源,包括 User IDs 和 Group IDs。
這是目前實現的 Namespace 中最復雜的一個,因為 User 和權限息息相關,而權限又關聯著容器的安全問題。
在不同的 User Namespace 中,同樣一個用戶的 User ID 和 Group ID 可以不一樣。也就是說,一個用戶可以在父 User Namespace 中是普通用戶,在子 User Namespace 中是超級用戶。
下面通過一段簡單的代碼來查看 Namespace 是如何實現的,示例代碼如下:
在以上示例中,代碼段通過 clone() 調用,傳入各個 Namespace 對應的 clone flag,創建了一個新的子進程,該進程擁有自己的 Namespace。根據以上代碼可知,該進程擁有自己的 PID、Mount、User、Net、IPC 以及 UTS Namespace。
所以,Docker 在創建容器進程時,指定了這個進程所需要啟動的一組 Namespace 參數。這樣,容器就只能 “看到” 當前 Namespace 所限定的資源、文件、設備、狀態、配置信息等。至于宿主機以及其他不相關的程序,它就完全看不到了。容器,其實是 Linux 系統中一種特殊的進程。
Linux 中 Docker 創建的隔離空間雖然是看不見摸不著,但是一個進程的 Namespace 信息在宿主機上是真實存在的,并且是以文件的方式存在,因為在 Linux 操作系統中,一切皆文件。
一個進程可以選擇加入到某個進程已有的 Namespace 當中,從而達到 “進入” 這個進程所在容器的目的,這正是 docker exec 的實現原理。
下面通過示例進行詳細講解,首先運行一個 CentOS 容器,示例代碼如下:
以上示例中在運行 Docker 容器的命令中添加了參數 -d,表示使容器在后臺運行。
查看當前正在運行 Docker 容器的進程號,示例代碼如下:
查看宿主機的 /proc 文件,可以看到這個 8589 進程所有 Namespace 對應的文件,示例代碼如下:
可以看到,一個進程的每種 Namespace 都在它對應的 /proc/[進程號]/ns 下有一個對應的虛擬文件,并且鏈接到一個真實的 Namespace 文件。
有了這樣的文件,就可以對 Namespace 做一些實質性的操作。例如,將進程加入到一個已經存在的 Namespace 當中。
這個操作依賴一個名為 setns() 的 Linux 系統調用,示例代碼如下:
上述代碼共接收了兩個參數。
arvg[1],即當前進程要加入的 Namespace 文件的路徑,如 /proc/8589/ns/net。用戶要在這個 Namespace 里運行的進程,如 /bin/bash。
代碼的核心操作則是通過 open() 打開指定的 Namespace 文件,并把這個文件的描述符 fd 交給 setns() 使用。在 setns() 執行后,當前進程就加入了這個文件對應的 Namespace 當中。
強大的 Namespace 機制可以實現容器間的隔離,是容器底層技術中非常重要的一項,但也有不可否認的不足。
下面總結基于 Namespace 的隔離機制相對于虛擬化技術的不足之處,以便在生產環境中設法克服。
容器只是運行在宿主機上的一種特殊的進程,多個容器之間使用的是同一個宿主機的操作系統內核。
盡管可以在容器中通過 Mount Namespace 單獨掛載其他版本的操作系統文件,如 CentOS 或者 Ubuntu,但這并不能改變它們共享宿主機內核的事實。
在 Windows 宿主機上運行 Linux 容器,或者在低版本的 Linux 宿主機上運行高版本的 Linux 容器,都是行不通的。
相比之下,擁有硬件虛擬化技術和獨立 Guest OS 的虛擬機就要好用得多。最極端的例子是 Microsoft 的云計算平臺 Azure,它就是運行在 Windows 服務器集群上的,但這并不妨礙用戶在上面創建各種 Linux 虛擬機。
如果容器中的程序調用 settimeofday() 修改了時間,整個宿主機的時間都會被修改。相較于在虛擬機里面可以任意做修改,在容器里部署應用時,需要用戶的操作上更加謹慎。
因為共享宿主機內核,容器中的應用暴露出來的攻擊面很大。
盡管生產實踐中可以使用 seccomp 等技術,對容器內部發起的所有系統調用進行過濾和甄別來進行安全加固,但這類方法因為多了一層對系統調用的過濾,會拖累容器的性能。
通常情況下,也不清楚到底該開啟哪些系統調用,禁止哪些系統調用。
到此,關于“Docker中Namespace隔離機制是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。