Linux的Namespace與Cgroups介紹


Namespace 的概念

Linux Namespace 是kernel 的一個功能,它可以隔離一系列系統的資源,比如PID(Process ID),User ID, Network等等。一般看到這里,很多人會想到一個命令chroot,就像chroot允許把當前目錄變成根目錄一樣(被隔離開來的),Namesapce也可以在一些資源上,將進程隔離起來,這些資源包括進程樹,網絡接口,掛載點等等。

比如一家公司向外界出售自己的計算資源。公司有一台性能還不錯的服務器,每個用戶買到一個tomcat實例用來運行它們自己的應用。有些調皮的客戶可能不小心進入了別人的tomcat實例,修改或者關閉了其中的某些資源,這樣就會導致各個客戶之間互相干擾。也許你會說,我們可以限制不同用戶的權限,讓用戶只能訪問自己名下的tomcat,但是有些操作可能需要系統級別的權限,比如root。我們不可能給每個用戶都授予root權限,也不可能給每個用戶都提供一台全新的物理主機讓他們互相隔離,因此這里Linux Namespace就派上了用場。使用Namespace, 我們就可以做到UID級別的隔離,也就是說,我們可以以UID為n的用戶,虛擬化出來一個namespace,在這個namespace里面,用戶是具有root權限的。但是在真實的物理機器上,他還是那個UID為n的用戶,這樣就解決了用戶之間隔離的問題。當然這個只是Namespace其中一個簡單的功能。

除了User Namespace ,PID也是可以被虛擬的。命名空間建立系統的不同視圖, 對於每一個命名空間,從用戶看起來,應該像一台單獨的Linux計算機一樣,有自己的init進程(PID為1),其他進程的PID依次遞增,A和B空間都有PID為1的init進程,子容器的進程映射到父容器的進程上,父容器可以知道每一個子容器的運行狀態,而子容器與子容器之間是隔離的。從圖中我們可以看到,進程3在父命名空間里面PID 為3,但是在子命名空間內,他就是1.也就是說用戶從子命名空間 A 內看進程3就像 init 進程一樣,以為這個進程是自己的初始化進程,但是從整個 host 來看,他其實只是3號進程虛擬化出來的一個空間而已。

Namespace 是 Linux 內核用來隔離內核資源的方式通過 namespace 可以讓一些進程只能看到與自己相關的一部分資源,而另外一些進程也只能看到與它們自己相關的資源,這兩撥進程根本就感覺不到對方的存在。具體的實現方式是把一個或多個進程的相關資源指定在同一個 namespace 中。

Linux namespaces 是對全局系統資源的一種封裝隔離,使得處於不同 namespace 的進程擁有獨立的全局系統資源,改變一個 namespace 中的系統資源只會影響當前 namespace 里的進程,對其他 namespace 中的進程沒有影響。

Namespace 的用途

可能絕大多數的使用者和我一樣,是在使用 docker 后才開始了解 linux 的 namespace 技術的。實際上,Linux 內核實現 namespace 的一個主要目的就是實現輕量級虛擬化(容器)服務。在同一個 namespace 下的進程可以感知彼此的變化,而對外界的進程一無所知。這樣就可以讓容器中的進程產生錯覺,認為自己置身於一個獨立的系統中,從而達到隔離的目的。也就是說 linux 內核提供的 namespace 技術為 docker 等容器技術的出現和發展提供了基礎條件。
我們可以從 docker 實現者的角度考慮該如何實現一個資源隔離的容器。比如是不是可以通過 chroot 命令切換根目錄的掛載點,從而隔離文件系統。為了在分布式的環境下進行通信和定位,容器必須要有獨立的 IP、端口和路由等,這就需要對網絡進行隔離。同時容器還需要一個獨立的主機名以便在網絡中標識自己。接下來還需要進程間的通信、用戶權限等的隔離。最后,運行在容器中的應用需要有進程號(PID),自然也需要與宿主機中的 PID 進行隔離。也就是說這六種隔離能力是實現一個容器的基礎,讓我們看看 linux 內核的 namespace 特性為我們提供了什么樣的隔離能力:

上表中的前六種 namespace 正是實現容器必須的隔離技術,至於新近提供的 Cgroup namespace 目前還沒有被 docker 采用。相信在不久的將來各種容器也會添加對 Cgroup namespace 的支持。

Namespace  的發展歷史

Linux 在很早的版本中就實現了部分的 namespace,比如內核 2.4 就實現了 mount namespace。大多數的 namespace 支持是在內核 2.6 中完成的,比如 IPC、Network、PID、和 UTS。還有個別的 namespace 比較特殊,比如 User,從內核 2.6 就開始實現了,但在內核 3.8 中才宣布完成。同時,隨着 Linux 自身的發展以及容器技術持續發展帶來的需求,也會有新的 namespace 被支持,比如在內核 4.6 中就添加了 Cgroup namespace。

Linux 提供了多個 API 用來操作 namespace,它們是 clone()、setns() 和 unshare() 函數,為了確定隔離的到底是哪項 namespace,在使用這些 API 時,通常需要指定一些調用參數:CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER、CLONE_NEWUTS 和 CLONE_NEWCGROUP。如果要同時隔離多個 namespace,可以使用 | (按位或)組合這些參數。同時我們還可以通過 /proc 下面的一些文件來操作 namespace。下面就讓讓我們看看這些接口的簡要用法。

查看進程所屬的 Namespace

從版本號為 3.8 的內核開始,/proc/[pid]/ns 目錄下會包含進程所屬的 namespace 信息,使用下面的命令可以查看當前進程所屬的 namespace 信息:

$ ll /proc/$$/ns

首先,這些 namespace 文件都是鏈接文件。鏈接文件的內容的格式為 xxx:[inode number]。其中的 xxx 為 namespace 的類型,inode number 則用來標識一個 namespace,我們也可以把它理解為 namespace 的 ID。如果兩個進程的某個 namespace 文件指向同一個鏈接文件,說明其相關資源在同一個 namespace 中。
其次,在 /proc/[pid]/ns 里放置這些鏈接文件的另外一個作用是,一旦這些鏈接文件被打開,只要打開的文件描述符(fd)存在,那么就算該 namespace 下的所有進程都已結束,這個 namespace 也會一直存在,后續的進程還可以再加入進來。
除了打開文件的方式,我們還可以通過文件掛載的方式阻止 namespace 被刪除。比如我們可以把當前進程中的 uts 掛載到 ~/uts 文件:

$ touch ~/uts
$ sudo mount --bind /proc/$$/ns/uts ~/uts

使用 stat 命令檢查下結果:

很神奇吧,~/uts 的 inode 和鏈接文件中的 inode number 是一樣的,它們是同一個文件。

 -----------------------------------------------------------------------------------------------------------------------------------------------

上面是構建Linux容器的namespace技術,它幫進程隔離出自己單獨的空間,但Docker又是怎么限制每個空間的大小,保證他們不會互相爭搶呢?那么就要用到Linux的Cgroups技術。

Cgroups概念

Cgroups(Control Groups) 是Linux內核提供的一種可以限制、記錄、隔離進程組(process groups)所使用的物理資源(如:cpu,memory,IO等等)的機制。可以對一組進程及將來的子進程的資源的限制、控制和統計的能力,這些資源包括CPU,內存,存儲,網絡等。通過Cgroups,可以方便的限制某個進程的資源占用,並且可以實時的監控進程的監控和統計信息。最初由google的工程師提出,后來被整合進Linux內核。Cgroups也是LXC為實現虛擬化所使用的資源管理手段,可以說沒有cgroups就沒有LXC (Linux Container)。

ask:

一個進程

control group:

控制組群,按照某種標准划分的進程組

hierarchy:

層級,control group可以形成樹形的結構,有父節點,子節點,每個節點都是一個control group,子節點繼承父節點的特定屬性。

subsystem:

子系統。

子系統就是資源控制器,每種子系統就是一個資源的分配器,比如cpu子系統是控制cpu時間分配的。

可以使用lssubsys -all來列出系統支持多少種子系統,和使用ls /sys/fs/cgroup/ (ubuntu)來顯示已經掛載的子系統:

Image(6)

可以看到這里的幾個子系統,比如cpu是控制cpu時間片的,memory是控制內存使用的。

Cgroups可以做什么?

Cgroups最初的目標是為資源管理提供的一個統一的框架,既整合現有的cpuset等子系統,也為未來開發新的子系統提供接口。現在的cgroups適用於多種應用場景,從單個進程的資源控制,到實現操作系統層次的虛擬化(OS Level Virtualization)。Cgroups提供了一下功能:
1.限制進程組可以使用的資源數量(Resource limiting )。比如:memory子系統可以為進程組設定一個memory使用上限,一旦進程組使用的內存達到限額再申請內存,就會出發OOM(out of memory)。
2.進程組的優先級控制(Prioritization )。比如:可以使用cpu子系統為某個進程組分配特定cpu share。
3.記錄進程組使用的資源數量(Accounting )。比如:可以使用cpuacct子系統記錄某個進程組使用的cpu時間
4.進程組隔離(isolation)。比如:使用ns子系統可以使不同的進程組使用不同的namespace,以達到隔離的目的,不同的進程組有各自的進程、網絡、文件系統掛載空間。
5.進程組控制(control)。比如:使用freezer子系統可以將進程組掛起和恢復。

 

通過mount -t cgroup命令或進入/sys/fs/cgroup目錄,我們看到目錄中有若干個子目錄,我們可以認為這些都是受 cgroups 控制的資源以及這些資源的信息。

  • blkio — 這​​​個​​​子​​​系​​​統​​​為​​​塊​​​設​​​備​​​設​​​定​​​輸​​​入​​​/輸​​​出​​​限​​​制​​​,比​​​如​​​物​​​理​​​設​​​備​​​(磁​​​盤​​​,固​​​態​​​硬​​​盤​​​,USB 等​​​等​​​)。
  • cpu — 這​​​個​​​子​​​系​​​統​​​使​​​用​​​調​​​度​​​程​​​序​​​提​​​供​​​對​​​ CPU 的​​​ cgroup 任​​​務​​​訪​​​問​​​。​​​
  • cpuacct — 這​​​個​​​子​​​系​​​統​​​自​​​動​​​生​​​成​​​ cgroup 中​​​任​​​務​​​所​​​使​​​用​​​的​​​ CPU 報​​​告​​​。​​​
  • cpuset — 這​​​個​​​子​​​系​​​統​​​為​​​ cgroup 中​​​的​​​任​​​務​​​分​​​配​​​獨​​​立​​​ CPU(在​​​多​​​核​​​系​​​統​​​)和​​​內​​​存​​​節​​​點​​​。​​​
  • devices — 這​​​個​​​子​​​系​​​統​​​可​​​允​​​許​​​或​​​者​​​拒​​​絕​​​ cgroup 中​​​的​​​任​​​務​​​訪​​​問​​​設​​​備​​​。​​​
  • freezer — 這​​​個​​​子​​​系​​​統​​​掛​​​起​​​或​​​者​​​恢​​​復​​​ cgroup 中​​​的​​​任​​​務​​​。​​​
  • memory — 這​​​個​​​子​​​系​​​統​​​設​​​定​​​ cgroup 中​​​任​​​務​​​使​​​用​​​的​​​內​​​存​​​限​​​制​​​,並​​​自​​​動​​​生​​​成​​​​​內​​​存​​​資​​​源使用​​​報​​​告​​​。​​​
  • net_cls — 這​​​個​​​子​​​系​​​統​​​使​​​用​​​等​​​級​​​識​​​別​​​符​​​(classid)標​​​記​​​網​​​絡​​​數​​​據​​​包​​​,可​​​允​​​許​​​ Linux 流​​​量​​​控​​​制​​​程​​​序​​​(tc)識​​​別​​​從​​​具​​​體​​​ cgroup 中​​​生​​​成​​​的​​​數​​​據​​​包​​​。​​​
  • net_prio — 這個子系統用來設計網絡流量的優先級
  • hugetlb — 這個子系統主要針對於HugeTLB系統進行限制,這是一個大頁文件系統。

如何為Cgroup分配限制的資源

首先明白下,是先掛載子系統,然后才有control group的。意思就是比如想限制某些進程的資源,那么,我會先掛載memory子系統,然后在memory子系統中創建一個cgroup節點,在這個節點中,將需要控制的進程id寫入,並且將控制的屬性寫入。

1、以memory子系統為例:

進入/sys/fs/cgroup/memory這個目錄,我們創建一個test文件夾就相當於創建了一個control group了,進入test目錄,你會發現test目錄下自動創建了許多文件

這些文件的含義如下:

於是,限制內存使用我們就可以設置memory.limit_in_bytes

將一個進程ID加入到這個test中: echo $$ > tasks

這樣就將當前這個終端進程加入到了內存限制的cgroup中了,如果需要刪除cgroup只要刪除剛創建的目錄就可以了。

 2、以CPU子系統為例:

跑一個耗費cpu的腳本

x=0
while [ True ];do
    x=$x+1
done;

用top命令可以看到這個腳本基本占了100%的cpu資源

下面用cgroups控制這個進程的cpu資源


mkdir -p /sys/fs/cgroup/cpu/hello/ #新建一個控制組hello echo 50000 > /sys/fs/cgroup/cpu/hello/cpu.cfs_quota_us #將cpu.cfs_quota_us設為50000,相對於cpu.cfs_period_us的100000是50% echo "$PID" > /sys/fs/cgroup/cpu/hello/tasks

然后觀察top的實時統計數據,會發現cpu占用率將近50%,看來cgroups關於cpu的控制起了效果。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM