500行程式碼手寫docker-實現硬體資源限制cgroups

2023-05-29 12:00:22

(5)500行程式碼手寫docker-實現硬體資源限制cgroups

本系列教學主要是為了弄清楚容器化的原理,紙上得來終覺淺,絕知此事要躬行,理論始終不及動手實踐來的深刻,所以這個系列會用go語言實現一個類似docker的容器化功能,最終能夠容器化的執行一個程序。

本章的原始碼已經上傳到github,地址如下:

https://github.com/HobbyBear/tinydocker/tree/chapter5

之前我們對容器的網路名稱空間,檔案系統名稱空間都進行了設定,說到底這些都是為了資源更好的隔離,但是他們無法辦到對硬體資源使用的隔離,比如,cpu,記憶體,頻寬,而今天要介紹的cgroups技術便能夠對硬體資源的使用產生隔離。

cgroups技術簡介

cgroups技術是核心提供的功能,可以通過虛擬檔案系統介面對其進行存取和更改。mount 命令可以檢視cgroups在虛擬檔案系統下的掛載目錄。

root@ecs-295280:~# mount | grep  cgroup
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
root@ecs-295280:~#

一般預設的掛載目錄是在/sys/fs/cgroup 目錄下,系統核心在開機時,會預設掛載cgroup目錄。這樣便能通過存取檔案的方式對cgroup功能進行使用。

在/sys/fs/cgroup/ 目錄下,我們看到的每個目錄例如cpu,blkio被稱作subsystem子系統,每個子系統下可以設定各自要管理的程序id。

root@ecs-295280:~# ls /sys/fs/cgroup/
blkio    cpu,cpuacct  freezer  net_cls           perf_event  systemd
cpu      cpuset       hugetlb  net_cls,net_prio  pids        unified
cpuacct  devices      memory   net_prio          rdma

拿cpu這個目錄下的檔案舉例

root@ecs-295280:/sys/fs/cgroup/cpu# ls
cgroup.clone_children  cpuacct.usage_percpu_sys   cpu.stat
cgroup.procs           cpuacct.usage_percpu_user  ebpf-agent
cgroup.sane_behavior   cpuacct.usage_sys          hostguard
cpuacct.stat           cpuacct.usage_user         notify_on_release
cpuacct.usage          cpu.cfs_period_us          release_agent
cpuacct.usage_all      cpu.cfs_quota_us           tasks
cpuacct.usage_percpu   cpu.shares
root@ecs-295280:/sys/fs/cgroup/cpu# ll -l

在cpu子系統這個目錄下,有兩個檔案cgroup.procs,tasks檔案,它們都是用來管理cgroup中的程序。但是,它們的使用方式略有不同:

cgroup.procs檔案用於向cgroup中新增或刪除程序,只需要將程序的task id寫入該檔案即可。

tasks檔案則是用於將整個行程群組新增到cgroup中。如果將一個行程群組的pid寫入tasks檔案,則該行程群組中的所有程序都會被新增到cgroup中。

程序被加入到這個cgroup組以後,其使用的cpu頻寬將會受到cpu.cfs_quota_us和cpu.cfs_period_us的影響。通過shell命令檢視他們的內容。

root@ecs-295280:/sys/fs/cgroup/cpu/test# cat cpu.cfs_period_us
100000
root@ecs-295280:/sys/fs/cgroup/cpu/test# cat cpu.cfs_quota_us
-1

預設情況下,cpu.cfs_period_us是100000,單位是微秒,cpu.cfs_period_us代表了cpu執行一個週期的時長,100000代表了100ms,cpu.cfs_quota_us代表程序所佔用的週期時長,-1代表不限制程序使用cpu週期時長,如果cpu.cfs_quota_us是50000(50ms)則代表在cpu一個排程週期內,該cgroup下的程序最多隻能執行半個週期,如果達到了執行週期的限制,那麼它必須等待下一個時間片才能繼續執行了。

命名行實踐下cgroups隔離特性

我們來實驗下:

對cpu使用率進行限制

在cpu的一級目錄下,是包含了當前系統所有程序,為了不影響它們,我們在cpu的一級目錄下建立一個test目錄,然後單獨的在test目錄中的tasks檔案加入程序id。