本系列教學主要是為了弄清楚容器化的原理,紙上得來終覺淺,絕知此事要躬行,理論始終不及動手實踐來的深刻,所以這個系列會用go語言實現一個類似docker的容器化功能,最終能夠容器化的執行一個程序。
本章的原始碼已經上傳到github,地址如下:
https://github.com/HobbyBear/tinydocker/tree/chapter5
之前我們對容器的網路名稱空間,檔案系統名稱空間都進行了設定,說到底這些都是為了資源更好的隔離,但是他們無法辦到對硬體資源使用的隔離,比如,cpu,記憶體,頻寬,而今天要介紹的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下的程序最多隻能執行半個週期,如果達到了執行週期的限制,那麼它必須等待下一個時間片才能繼續執行了。
我們來實驗下:
在cpu的一級目錄下,是包含了當前系統所有程序,為了不影響它們,我們在cpu的一級目錄下建立一個test目錄,然後單獨的在test目錄中的tasks檔案加入程序id。