docker之namespace與cgroup簡介

前言

當談論docker時,常常會聊到docker的實現方式。很多開發者都知道,docker容器本質上是宿主機的進程,Docker通過namespace實現了資源隔離,通過cgroups實現了資源限制,通過寫時複製機制(copy-on-write)實現了高效的文件操作。

對於Linux容器的最小組成,可以由下面公式來表示

容器=namespace+cgroup+rootfs+容器引擎(用戶態工具)

其中各項的功能分別爲:

  • Namespace:訪問隔離
  • Cgroup:資源控制
  • rootfs:文件系統隔離
  • 容器引擎:生命週期控制

目前市場上所有的Linux容器都包含了以上組件,一般來說,容器技術主要包括NamespaceCroup這兩個內核特性

容器創建過程

下面通過三段代碼來簡要說明容器的創建過程

代碼一

pid = clone(fun,stack,flags,clone_arg);
(flags: CLONE_NEWPID | CLONE_NEWNS |
	CLONE_NEWUSER | CLONE_NEWNET |
	CLONE_NEWIPC | CLONE_NEWUTS
	...)

對於代碼一,通過clone系統調用,並傳入各個Namespace對應的clone flag,創建了一個新的子進程。該進程擁有自己的Namespace。根據以上代碼可知,該進程擁有自己的pid,mount,user,net,ipc,ust namespace。

代碼二

echo $pid > /sys/fs/cgroup/cpu/tasks
echo $pid > /sys/fs/cgroup/cpuset/tasks
echo $pid > /sys/fs/cgroup/blkio/tasks
echo $pid > /sys/fs/cgroup/memory/tasks
echo $pid > /sys/fs/cgroup/devices/tasks
echo $pid > /sys/fs/cgroup/freezer/tasks

對於代碼二,將代碼一中產生的進程pid寫入各個Cgroup子系統中,這樣該進程就可以受到相應Cgroup子系統的控制。

代碼三

fun()
{
	...
	pivot_root("path_of_rootfs/",path);
	...
	exec("/bin/bash");
	...
}

對於代碼三,該fun函數由上面生成的新進程執行,在fun函數中,通過調用pivot_root系統調用,使進程進入一個新的rootfs,之後通過exec系統調用,在新的Namespace,Cgroup,rootfs中執行“/bin/bash”程序

Namespace

namespace又稱命名空間,它主要做訪問隔離。其原理是針對一類資源進行抽象,並將其封裝在一起提供給一個容器使用,對於這類資源,因爲每個容器都有自己的抽象,而它們彼此之間是不可見的,所有就可以做到訪問隔離。

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

舉個例子,執行sethostname這個系統調用時,可以改變系統的主機名,這個主機名就是一個內核的全局資源。內核通過實現UTS Namespace,可以將不同的進程分隔在不同的UTS namespace中,在某個Namespace修改主機名時,另一個namespace的主機名還是保持不變。

目前Linux內核總共實現了6種namespace:

  • IPC:隔離System V IPC和POSIX消息隊列

    IPC是Inter-Process Communication的簡寫,也就是進程間通信。Linux提供了很多種進程間通信的機制,IPC Namespace針對的是SystemV IPC和Posix消息隊列。這些IPC機制都會用到標識符,例如用標識符來區別不同的消息隊列,然後兩個進程通過標識符找到對應的消息隊列進行通信等。

    IPC Namespace能做到的事情是,是相同的標識符在兩個Namespace中代表不同的消息隊列,這樣也就使得兩個Namespace中的進程不能通過IPC進程通信了

  • Network:隔離網絡資源

    這個Namespace會對網絡相關的系統資源進行隔離,每個Network Namespace都有自己的網絡設備、IP地址、路由表、/proc/net目錄、端口號等。網絡隔離的必要性是很明顯的,舉一個例子,在沒有隔離的情況下,如果兩個不同的容器都想要運行同一個Web應用,而這個應用有需要使用80端口,那就會有衝突了。

  • Mount:隔離文件系統掛載點

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

  • PID:隔離進程ID

    PID Namespace用於隔離進程PID號,這樣一來,不同的Namespace裏的進程PID號就可以是一樣的了

  • UTS:隔離主機名和域名

    UST Namespace用於對主機名和域名進行隔離,爲什麼要使用UTS Namespace做隔離?這是因爲主機名可以用來代替IP地址,因此,業績可以使用主機名在網絡上訪問某臺機器了,如果不做隔離,這個機制在容器裏就會出問題

  • User:隔離用戶ID和組ID

    User Namespace用來隔離用戶和組ID,也就是說一個進程在Namespace裏的用戶和組ID與它在host裏的ID可用不一樣,User Namespace最有用的地方在於,host的普通用戶進程在容器裏可以是0號用戶,也就是root用戶。這樣,進程在容器內可以做各種特權操作,但是它的特權被限定在容器內,離開了這個容器它就只有普通用戶的權限了。

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

Cgroup

cgroup是control group 的簡稱,又稱控制組,它主要是做資源控制。其原理是將一組進程放在一個控制組裏,通過給這個控制組分配指定的可用資源,達到控制這一組進程可用資源的目的。

cgroup屬於linux內核提供的一個特性,用於限制和隔離一組進程對系統資源的使用,也就是做資源QoS,這些資源主要包括CPU,內存,block I/O和網絡帶寬。

Cgroup 是將任意進程進行分組化管理的 Linux 內核功能。Cgroup 本身是提供將進程進行分組化管理的功能和接口的基礎結構,I/O 或內存的分配控制等具體的資源管理功能是通過這個功能來實現的。這些具體的資源管理功能稱爲 Cgroup 子系統或控制器。Cgroup 子系統有控制內存的 Memory 控制器、控制進程調度的 CPU 控制器等。運行中的內核可以使用的 Cgroup 子系統由/proc/cgroup 來確認。

Cgroup 提供了一個 Cgroup 虛擬文件系統,作爲進行分組管理和各子系統設置的用戶接口。要使用 CGroup,必須掛載 Cgroup 文件系統。這時通過掛載選項指定使用哪個子系統。

我們可以通過mount -t cgroup 查詢系統中已經掛載的cgroup的文件系統

在這裏插入圖片描述

cgroup子系統介紹

blkio – 這個子系統爲塊設備設定輸入/輸出限制,比如物理設備(磁盤,固態硬盤,USB 等等)。

cpu – 這個子系統使用調度程序提供對 CPU 的 cgroup 任務訪問。

cpuacct – 這個子系統自動生成 cgroup 中任務所使用的 CPU 報告。

cpuset – 這個子系統爲 cgroup 中的任務分配獨立 CPU(在多核系統)和內存節點。

devices – 這個子系統可允許或者拒絕 cgroup 中的任務訪問設備。

freezer – 這個子系統掛起或者恢復 cgroup 中的任務。

memory – 這個子系統設定 cgroup 中任務使用的內存限制,並自動生成由那些任務使用的內存資源報告。

net_cls – 這個子系統使用等級識別符(classid)標記網絡數據包,可允許 Linux 流量控制程序(tc)識別從具體 cgroup 中生成的數據包。

ns – 名稱空間子系統。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章