Cgroups 是 linux 內核提供的一種機制,如果你還不了解 cgroups,請參考前文《Linux cgroups 簡介》先了解 cgroups。當 Linux 的 init 系統發展到 systemd 之后,systemd 與 cgroups 發生了融合(或者說 systemd 提供了 cgroups 的使用和管理接口,systemd 管的東西越來越多啊!)。本文將簡單的介紹 cgroups 與 systemd 的關系以及如何通過 systemd 來配置和使用 cgroups。
Systemd 依賴 cgroups
要理解 systemd 與 cgroups 的關系,我們需要先區分 cgroups 的兩個方面:層級結構(A)和資源控制(B)。首先 cgroups 是以層級結構組織並標識進程的一種方式,同時它也是在該層級結構上執行資源限制的一種方式。我們簡單的把 cgroups 的層級結構稱為 A,把 cgrpups 的資源控制能力稱為 B。
對於 systemd 來說,A 是必須的,如果沒有 A,systemd 將不能很好的工作。而 B 則是可選的,如果你不需要對資源進行控制,那么在編譯 Linux 內核時完全可以去掉 B 相關的編譯選項。
Systemd 默認掛載的 cgroups 系統
在系統的開機階段,systemd 會把支持的 controllers (subsystem 子系統)掛載到默認的 /sys/fs/cgroup/ 目錄下面:
除了 systemd 目錄外,其它目錄都是對應的 subsystem。
/sys/fs/cgroup/systemd 目錄是 systemd 維護的自己使用的非 subsystem 的 cgroups 層級結構。這玩意兒是 systemd 自己使用的,換句話說就是,並不允許其它的程序動這個目錄下的內容。其實 /sys/fs/cgroup/systemd 目錄對應的 cgroups 層級結構就是 systemd 用來使用 cgoups 中 feature A 的。
Cgroup 的默認層級
通過將 cgroup 層級系統與 systemd unit 樹綁定,systemd 可以把資源管理的設置從進程級別移至應用程序級別。因此,我們可以使用 systemctl 指令,或者通過修改 systemd unit 的配置文件來管理 unit 相關的資源。
默認情況下,systemd 會自動創建 slice、scope 和 service unit 的層級(slice、scope 和 service 都是 systemd 的 unit 類型,參考《初識 systemd》),來為 cgroup 樹提供統一的層級結構。
系統中運行的所有進程,都是 systemd init 進程的子進程。在資源管控方面,systemd 提供了三種 unit 類型:
- service: 一個或一組進程,由 systemd 依據 unit 配置文件啟動。service 對指定進程進行封裝,這樣進程可以作為一個整體被啟動或終止。
- scope:一組外部創建的進程。由進程通過 fork() 函數啟動和終止、之后被 systemd 在運行時注冊的進程,scope 會將其封裝。例如:用戶會話、 容器和虛擬機被認為是 scope。
- slice: 一組按層級排列的 unit。slice 並不包含進程,但會組建一個層級,並將 scope 和 service 都放置其中。真正的進程包含在 scope 或 service 中。在這一被划分層級的樹中,每一個 slice 單位的名字對應通向層級中一個位置的路徑。
我們可以通過 systemd-cgls 命令來查看 cgroups 的層級結構:
service、scope 和 slice unit 被直接映射到 cgroup 樹中的對象。當這些 unit 被激活時,它們會直接一一映射到由 unit 名建立的 cgroup 路徑中。例如,cron.service 屬於 system.slice,會直接映射到 cgroup system.slice/cron.service/ 中。
注意,所有的用戶會話、虛擬機和容器進程會被自動放置在一個單獨的 scope 單元中。
默認情況下,系統會創建四種 slice:
- -.slice:根 slice
- system.slice:所有系統 service 的默認位置
- user.slice:所有用戶會話的默認位置
- machine.slice:所有虛擬機和 Linux 容器的默認位置
創建臨時的 cgroup
對資源管理的設置可以是 transient(臨時的),也可以是 persistent (永久的)。我們先來介紹如何創建臨時的 cgroup。
需要使用 systemd-run 命令創建臨時的 cgroup,它可以創建並啟動臨時的 service 或 scope unit,並在此 unit 中運行程序。systemd-run 命令默認創建 service 類型的 unit,比如我們創建名稱為 toptest 的 service 運行 top 命令:
$ sudo systemd-run --unit=toptest --slice=test top -b
然后查看一下 test.slice 的狀態:
創建了一個 test.slice/toptest.service cgroup 層級關系。再看看 toptest.service 的狀態:
top 命令被包裝成一個 service 運行在后台了!
接下來我們就可以通過 systemctl 命令來限制 toptest.service 的資源了。在限制前讓我們先來看一看 top 進程的 cgroup 信息:
$ vim /proc/2850/cgroup # 2850 為 top 進程的 PID
比如我們限制 toptest.service 的 CPUShares 為 600,可用內存的上限為 550M:
$ sudo systemctl set-property toptest.service CPUShares=600 MemoryLimit=500M
再次檢查 top 進程的 cgroup 信息:
在 CPU 和 memory 子系統中都出現了 toptest.service 的名字。同時去查看 /sys/fs/cgroup/memory/test.slice 和 /sys/fs/cgroup/cpu/test.slice 目錄,這兩個目錄下都多出了一個 toptest.service 目錄。我們設置的 CPUShares=600 MemoryLimit=500M 被分別寫入了這些目錄下的對應文件中。
臨時 cgroup 的特征是,所包含的進程一旦結束,臨時 cgroup 就會被自動釋放。比如我們 kill 掉 top 進程,然后再查看 /sys/fs/cgroup/memory/test.slice 和 /sys/fs/cgroup/cpu/test.slice 目錄,剛才的 toptest.service 目錄已經不見了。
通過配置文件修改 cgroup
所有被 systemd 監管的 persistent cgroup(持久的 cgroup)都在 /usr/lib/systemd/system/ 目錄中有一個 unit 配置文件。比如我們常見的 service 類型 unit 的配置文件。我們可以通過設置 unit 配置文件來控制應用程序的資源,persistent cgroup 的特點是即便系統重啟,相關配置也會被保留。需要注意的是,scope unit 不能以此方式創建。下面讓我們為 cron.service 添加 CPU 和內存相關的一些限制,編輯 /lib/systemd/system/cron.service 文件:
$ sudo vim /lib/systemd/system/cron.service
添加紅框中的行,然后重新加載配置文件並重啟 cron.service:
$ sudo systemctl daemon-reload $ sudo systemctl restart cron.service
現在去查看 /sys/fs/cgroup/memory/system.slice/cron.service/memory.limit_in_bytes 和 /sys/fs/cgroup/cpu/system.slice/cron.service/cpu.shares 文件,是不是已經包含我們配置的內容了!
通過 systemctl 命令修改 cgroup
除了編輯 unit 的配置文件,還可以通過 systemctl set-property 命令來修改 cgroup,這種方式修該的配置也會在重啟系統時保存下來。現在我們把 cron.service 的 CPUShares 改為 700:
$ sudo systemctl set-property cron.service CPUShares=700
查看 /sys/fs/cgroup/cpu/system.slice/cron.service/cpu.shares 文件的內容應該是 700,重啟系統后該文件的內容還是 700。
Systemd-cgtop 命令
類似於 top 命令,systemd-cgtop 命令顯示 cgoups 的實時資源消耗情況:
通過它我們就可以分析應用使用資源的情況。
總結
Systemd 是一個強大的 init 系統,它甚至為我們使用 cgorups 提供了便利!Systemd 提供的內在機制、默認設置和相關的操控命令降低了配置和使用 cgroups 的難度,即便是 Linux 新手,也能輕松的使用 cgroups 了。
參考:
The New Control Group Interfaces
systemd for Administrators, Part XVIII
Control Groups vs. Control Groups
RedHat Cgroups doc
Systemd-cgls
Systemd-cgtop