Linux namespace
- Docker 和虛擬機技術一樣,從操作系統(tǒng)級上實現了資源的隔離,它本質上是宿主機上的進程(容器進程),所以資源隔離主要就是指進程資源的隔離。實現資源隔離的核心技術就是 Linux namespace。這技術和很多語言的命名空間的設計思想是一致的
-
隔離意味著可以抽象出多個輕量級的內核(容器進程),這些進程可以充分利用宿主機的資源,宿主機有的資源容器進程都可以享有,但彼此之間是隔離的,同樣,不同容器進程之間使用資源也是隔離的,這樣,彼此之間進行相同的操作,都不會互相干擾,安全性得到保障。
為了支持這些特性,Linux namespace 實現了 6 項資源隔離,基本上涵蓋了一個小型操作系統(tǒng)的運行要素,包括主機名、用戶權限、文件系統(tǒng)、網絡、進程號、進程間通信。
namespace
查看名稱空間
[namespace_id]
uname -r
3.10.0-1127.el7.x86_64
ls -l /proc/PID/ns
total 0
lrwxrwxrwx 1 0 Jun 25 15:30 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 0 Jun 25 15:30 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 0 Jun 25 15:30 net -> net:[4026531968]
lrwxrwxrwx 1 0 Jun 25 15:30 pid -> pid:[4026531836]
lrwxrwxrwx 1 0 Jun 25 15:30 user -> user:[4026531837]
lrwxrwxrwx 1 0 Jun 25 15:30 uts -> uts:[4026531838]
Linux cgroups
- Cgroups是control groups的縮寫,最初由google的工程師提出,后來被整合進Linux內核。Cgroups是Linux內核提供的一種可以限制、記錄、隔離進程組(process groups)所使用的物理資源(如:CPU、內存、IO等)的機制。
- 本質上來說,cgroups 是內核附加在程序上的一系列鉤子(hooks),通過程序運行時對資源的調度觸發(fā)相應的鉤子以達到資源追蹤和限制的目的。實現 cgroups 的主要目的是為不同用戶層面的資源管理,提供一個統(tǒng)一化的接口。從單個進程的資源控制到操作系統(tǒng)層面的虛擬化。
Cgroups 提供了以下四大功能:
1、資源限制(Resource Limitation):cgroups 可以對進程組使用的資源總額進行限制。如設定應用運行時使用內存的上限,一旦超過這個配額就發(fā)出 OOM(Out of Memory)。
2、優(yōu)先級分配(Prioritization):通過分配的 CPU 時間片數量及硬盤 IO 帶寬大小,實際上就相當于控制了進程運行的優(yōu)先級。
3、資源統(tǒng)計(Accounting): cgroups 可以統(tǒng)計系統(tǒng)的資源使用量,如 CPU 使用時長、內存用量等等,這個功能非常適用于計費。
4、進程控制(Control):cgroups 可以對進程組執(zhí)行掛起、恢復等操作。
Docker正是使用cgroup進行資源劃分,每個容器都作為一個進程運行起來,每個業(yè)務容器都會有一個基礎的pause容器也就是POD作為基礎容器。pause容器提供了劃分namespace的內容,并連通同一POD下的所有容器,共享網絡資源。
- 查看linux是否啟用了linux cgroups
對應的CGROUP項為“y”代表已經打開linux cgroups功能。
cat /boot/config-3.10.0-1127.el7.x86_64 | grep CGROUP
CONFIG_CGROUPS=y
# CONFIG_CGROUP_DEBUG is not set
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_SCHED=y
CONFIG_BLK_CGROUP=y
# CONFIG_DEBUG_BLK_CGROUP is not set
CONFIG_NETFILTER_XT_MATCH_CGROUP=m
CONFIG_NET_CLS_CGROUP=y
CONFIG_NETPRIO_CGROUP=y
CGroup 支持的文件種類

1、控制族群(control group)??刂谱迦壕褪且唤M按照某種標準劃分的進程。Cgroups
中的資源控制都是以控制族群為單位實現。一個進程可以加入到某個控制族群,也從一個進程組遷移到另一個控制族群。一個進程組的進程可以使用
cgroups 以控制族群為單位分配的資源,同時受到 cgroups 以控制族群為單位設定的限制。
2、層級(hierarchy)。控制族群可以組織成 hierarchical的形式,既一顆控制族群樹??刂谱迦簶渖系淖庸?jié)點控制族群是父節(jié)點控制族群的孩子,繼承父控制族群的特定的屬性。
3、子系統(tǒng)(subsytem)。一個子系統(tǒng)就是一個資源控制器,比如 cpu 子系統(tǒng)就是控制 cpu
時間分配的一個控制器。子系統(tǒng)必須附加(attach)到一個層級上才能起作用,一個子系統(tǒng)附加到某個層級以后,這個層級上的所有控制族群都受到這個子系統(tǒng)的控制。
CGroup子系統(tǒng)
1.CPU:使用調度程序為cgroup任務提供 CPU 的訪問。
2.cpuacct:產生cgroup任務的 CPU 資源報告。
3.cpuset:如果是多核心的CPU,這個子系統(tǒng)會為cgroup任務分配單的CPU和內存。
4.devices:允許或拒絕cgroup任務對設備的訪問。
5.freezer:暫停和恢復cgroup任務。
6.memory:設置每個cgroup 的內存限制以及產生內存資源報告。
7.net_cls:標記每個網絡包以供 cgroup方便使用。
8.ns:命名空間子系統(tǒng)。
9.perf event:增加了對每個group的監(jiān)測跟蹤的能力,可以監(jiān)測屬于某個特定的group 的所有線程以及運行在特定CPU上的線程。
CGroup 層級圖

如圖所示的 CGroup 層級關系顯示,CPU 和 Memory 兩個子系統(tǒng)有自己獨立的層級系統(tǒng),而又通過 Task Group 取得關聯(lián)關系。
yarn 實現的資源隔離
資源調度和資源隔離是YARN作為一個資源管理系統(tǒng),最重要和最基礎的兩個功能。
- 資源調度:由resourcemanager完成
- 資源隔離:各個nodemanager完成
1、內存資源隔離
- 進程監(jiān)控的方案
首先可計算當前每個運行的Container使用的內存總量。
但需要注意的是,不能僅憑該內存量是否超過設定的內存最高值來決定是否殺死一個Container。在創(chuàng)建一個子進程時,JVM采用了"fork()+exec()"模型,這意味著進程創(chuàng)建之后、執(zhí)行之前會復制一份父進程內存空間,進而使得進程樹在某一小段時間內存使用量翻倍
Linux中所有的進程都是通過fork()復制來實現的,而為了減少創(chuàng)建進程帶來的堆棧消耗和性能影響,Linux使用了寫時復制機制來快速創(chuàng)建進程。也就是說,一個子進程剛剛產生時,它的堆??臻g和父進程是完全一致的,那么從一開始它就擁有和父進程同樣的ru_maxrss,如果父進程的ru_maxrss比較大,那么由于rusage計算值取最大值,就算在觸發(fā)寫時復制后,子進程使用的實際最大駐留集大小被更新,我們獲得的也還是父進程的那個值,也就是說我們永遠拿不到子進程真實使用的內存。
Java創(chuàng)建子進程時采用了“fork() + exec()”的方案,子進程啟動瞬間,它的內存使用量與父進程是一致的,exec系函數,這個系別的函數通過將當前進程的使用權轉交給另一個程序,這時候進程原有的所有運行堆棧等數據將全部被銷毀,因此ru_maxrss也會被清零計算,然后子進程的內存會恢復正常;也就是說,Container(子進程)的創(chuàng)建過程中可能會出現內存使用量超過預先定義的上限值的情況(取決于父進程,也就是NodeManager的內存使用量);此時,如果使用Cgroup進行內存資源隔離,這個Container就可能會被“kill”
Linux寫時拷貝技術(copy-on-write)
在Linux程序中,fork()會產生一個和父進程完全相同的子進程,但子進程在此后多會exec系統(tǒng)調用,出于效率考慮,linux中引入了“寫時復制“技術,也就是只有進程空間的各段的內容要發(fā)生變化時,才會將父進程的內容復制一份給子進程。那么子進程的物理空間沒有代碼,怎么去取指令執(zhí)行exec系統(tǒng)調用呢?
在fork之后exec之前兩個進程用的是相同的物理空間(內存區(qū)),子進程的代碼段、數據段、堆棧都是指向父進程的物理空間,也就是說,兩者的虛擬空間不同,但其對應的物理空間是同一個。當父子進程中有更改相應段的行為發(fā)生時,再為子進程相應的段分配物理空間,如果不是因為exec,內核會給子進程的數據段、堆棧段分配相應的物理空間(至此兩者有各自的進程空間,互不影響),而代碼段繼續(xù)共享父進程的物理空間(兩者的代碼完全相同)。而如果是因為exec,由于兩者執(zhí)行的代碼不同,子進程的代碼段也會分配單獨的物理空間。
傳統(tǒng)的fork()系統(tǒng)調用直接把所有的資源復制給新創(chuàng)建的進程。這種實現過于簡單并且效率低下,因為它拷貝的數據也許并不共享,更糟的情況是,如果新進程打算立即執(zhí)行一個新的映像,那么所有的拷貝都將前功盡棄。Linux的fork()使用寫時拷貝(copy-on-write)頁實現。寫時拷貝是一種可以推遲甚至免除拷貝數據的技術。內核此時并不復制整個進程地址空間,而是讓父進程和子進程共享同一個拷貝。只有在需要寫入的時候,數據才會被復制,從而使各個進程擁有各自的拷貝。也就是說,資源的復制只有在需要寫入的時候才進行,在此之前,只是以只讀方式共享。這種技術使地址空間上的頁的拷貝被推遲到實際發(fā)生寫入的時候。在頁根本不會被寫入的情況下—舉例來說,fork()后立即調用exec()—它們就無需復制了。fork()的實際開銷就是復制父進程的頁表以及給子進程創(chuàng)建惟一的進程描述符。在一般情況下,進程創(chuàng)建后都會馬上運行一個可執(zhí)行的文件,這種優(yōu)化可以避免拷貝大量根本就不會被使用的數據(地址空間里常常包含數十兆的數據)。由于Unix強調進程快速執(zhí)行的能力,所以這個優(yōu)化是很重要的
1、 fork() 和 vfork() 參數是寫死的,而 clone() 是可選的,它可以選擇當前創(chuàng)建的進程哪些部分是共享的,哪些部分是獨立的;
2、vfork() 是歷史的產物,當調用 fork() 的時候,需要將父進程的線性區(qū)和頁表都拷貝一份,而調用 exec() 執(zhí)行新程序后,又要把所有頁表刪除重置新的頁表,建立映射關系,效率很低;
3、所以要有 vfork(),vfork() 的 clone_flags 位置了 CLONE_VM ,表示共享父進程的地址空間,vfork() 中創(chuàng)建的進程沒有分配自己的地址空間,而是通過一個 mm_struct 指針指向父進程的地址空間,這個進程是為了在之后調用 exec() 執(zhí)行新的程序;
4、而在有了 Copy-on-write 技術后,fork() 出的子進程只創(chuàng)建了自己的地址空間,然后用父進程的地址空間初始化,每個頁表的項置為父進程的頁表項,共享父進程的物理頁面,并將所有 私有/可寫 頁面改為只讀;
5、當我們改變父子進程的數據后,cpu在運行過程中會發(fā)生一個缺頁錯誤,cpu轉交控制權給操作系統(tǒng),操作系統(tǒng)查找VMA發(fā)現該頁權限為只讀,但所在段又是可寫的,產生一個矛盾,這就是識別Copy-on-write的方法,接著 OS 給子進程分配一個新的物理頁,并將頁表該頁的地址修改成新的物理頁地址;
6、這樣 fork() 后再調用 exec() 就不用那么麻煩了,可以直接將新的物理頁與子進程的虛擬空間建立映射
原文鏈接:https://blog.csdn.net/chen892704067/article/details/76596225
為了避免誤殺Container,Hadoop賦予每個進程年齡屬性,并規(guī)定剛啟動進程的年齡是1,且MonitoringThread線程每更新一次,各個進程年齡加一
在此基礎上,選擇被殺死Container的標準如下:
- 如果一個Container對應的進程樹中所有進程(年齡大于0)總內存超過(用戶設置的)最大值的兩倍。
-
有年齡大于1的進程總內存量超過(用戶設置的)最大值。
MonitoringThread線程直接掃描linux proc目錄下,某個container進程的stat文件,統(tǒng)計使用內存大小
則認為該Container過量使用內存,則向Container發(fā)送ContainerEventType.KILL_CONTAINER事件將其殺死。
- 基于輕量級資源隔離技術Cgroups的方案
Cgroup會嚴格限制應用程序的內存使用上限,一旦使用量超過預先定義的上限值,就會將該應用程序“殺死”,因此無法有效地使用Cgroup進行內存資源隔離。
2、cpu資源隔離
Yarn 3.0 版本中,在 Linux 系統(tǒng)環(huán)境下,ContainerExecutor 有兩種實現:
1、DefaultContainerExecutor: 簡稱 DCE , 如其名,是默認的 ContainerExecutor 實現。 如果用戶未指定 ContainerExecutor 的具體實現,NM 就會使用它。 DCE 直接使用 bash 來啟動 container 進程,所有 container 都使用 NM 進程用戶 (yarn) 啟動,安全性低且沒有任何CPU資源隔離機制。
2、LinuxContainerExecutor: 簡稱 LCE,相比于 DCE ,它能提供更多有用的功能,如用戶權限隔離,支持使用提交任務用戶來啟動 container;支持使用 cgroup 進行資源限制; 支持運行 docker container (合并了2.x 版本中的 DockerContainerExecutor)。 LCE 使用可執(zhí)行的二進制文件 container-executor 來啟動 container 進程,container 的用戶根據配置可以統(tǒng)一使用默認用戶,也可以使用提交任務的用戶(需要提前在 NM 上添加所有支持的用戶),從而以應用提交者的身份創(chuàng)建文件,運行/銷毀 Container,允許用戶在啟動Container后直接將CPU份額和進程ID寫入cgroup路徑的方式實現CPU資源隔離。
YARN使用了Cgroups子系統(tǒng)中的CPU和Memory子系統(tǒng),CPU子系統(tǒng)用于控制Cgroups中所有的進程可以使用的CPU時間片。Memory子系統(tǒng)可用于限定一個進程的內存使用上限,一旦超過該限制,將認為它為OOM,會將其殺死。
對于內存資源隔離,YARN采用了與MRv1這種基于線程監(jiān)控的資源控制方式,這樣做到的主要出發(fā)點是:這種方式更加靈活,且能夠防止內存驟增驟降導致內存不足而死掉。
對于CPU資源隔離,YARN采用了輕量級的Cgroups。
注:默認情況下,NM未啟用任何CPU資源隔離機制,如果想要啟用該機制,需使用LinuxContainerExecutor,它能夠以應用程序提交者的身份創(chuàng)建文件,運行Container和銷毀Container.
需要注意的是,YARN允許你配置每個節(jié)點上可使用的物理cpu個數,以及物理cpu與虛擬cpu個比例,而用戶申請資源時,只能申請?zhí)摂Mcpu。默認情況下,物理cpu和虛擬cpu是1:1的,如果你的集群是異構的,某些節(jié)點上的CPU擁有更強的計算能力,則可以調整物理cpu和虛擬cpu的比例。虛擬cpu的概念是借鑒“物理內存和虛擬內存”的,主要目的是消除集群中cpu計算能力的異構性。
1、bootfs(boot file system)主要包含bootloader和kernel,bootloader主要引導加載kernel,linux剛啟動的時候會加載bootfs文件系統(tǒng),在docker鏡像最底層是bootfs。這一層和我們典型的linux系統(tǒng)一樣,包含bootloader和kernel,當bootloader加載完kernel之后,kernel就在內存中了,此時內存的使用權由bootloader交給內核,此時系統(tǒng)會卸載bootfs
2、rootfs(root file system) 在bootfs之上,包含典型的Linux系統(tǒng)的/dev,/proc,/bin,/etc等標準目錄和文件,rootfs就是各種不同操作系統(tǒng)的發(fā)行版
總結
Linux內核提供namespace完成隔離,Cgroup完成資源限制。namespace+Cgroup構成了容器的底層技術(rootfs是容器文件系統(tǒng)層技術)
參考
https://blog.csdn.net/zhangzhebjut/article/details/37730013
https://www.cnblogs.com/janeysj/p/11274515.html
https://www.xiaoheidiannao.com/220959.html
https://developer.aliyun.com/article/446778
https://segmentfault.com/a/1190000019016039
https://smarthanwang.github.io/2019/10/12/yarn-container-executor/
http://www.itdecent.cn/p/b9245242472b/
https://www.shuzhiduo.com/A/6pdDYYNGzw/
http://www.itdecent.cn/p/8f700177d4e4
https://www.cnblogs.com/biyeymyhjob/archive/2012/07/20/2601655.html
https://blog.csdn.net/chen892704067/article/details/76596225

