Docker 与 Linux Cgroups:资源隔离的魔法之旅
這篇文章主要介紹了 Docker 如何利用 Linux 的 Control Groups(cgroups)實(shí)現(xiàn)容器的資源隔離和管理。
最后通過(guò)簡(jiǎn)單 Demo 演示了如何使用 Go 和 cgroups 交互。
如果你對(duì)云原生技術(shù)充滿好奇,想要深入了解更多相關(guān)的文章和資訊,歡迎關(guān)注微信公眾號(hào)。
搜索公眾號(hào)【探索云原生】即可訂閱
1.Docker 是如何使用 Cgroups 的
我們知道 Docker 是通過(guò) Cgroups 實(shí)現(xiàn)容器資源限制和監(jiān)控的,那么具體是怎么用的呢?
演示
包含以下步驟:
- 1)創(chuàng)建容器,指定內(nèi)存限制
- 2)查看 cgroup 情況
- 3)停止容器
- 4)再次查看 cgroup 情況
先啟動(dòng)一個(gè)容器:
[root@iZ2zefmrr626i66omb40ryZ memory]# docker run -itd -m 128m nginx
da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416
這里通過(guò)-m參數(shù)設(shè)置了內(nèi)存限制為 128M。
該命令執(zhí)行后 docker 會(huì)在 memory cgroup 上(也就是 /sys/fs/cgroup/memory 路徑下)創(chuàng)建一個(gè)叫 docker 的子 cgroup,具體如下:
$ ls -l /sys/fs/cgroup/memory/docker/
-rw-r--r-- 1 root root 0 Jan 6 19:53 cgroup.clone_children
--w--w--w- 1 root root 0 Jan 6 19:53 cgroup.event_control
-rw-r--r-- 1 root root 0 Jan 6 19:53 cgroup.procs
# 可以發(fā)現(xiàn)這一長(zhǎng)串ID和創(chuàng)建容器時(shí)打印的是一致的
drwxr-xr-x 2 root root 0 Jan 6 19:56 da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416
# 省略其他文件
內(nèi)部除了 cgroup 相關(guān)的文件外,還有很多目錄,使用容器 ID 作為目錄名,其中每個(gè)目錄即對(duì)應(yīng)一個(gè)容器。
其中,da82f9e...這個(gè)目錄名稱和容器 ID 一致,說(shuō)明 docker 是為每個(gè)容器創(chuàng)建了一個(gè)子 cgroup 來(lái)單獨(dú)限制。
查看一下里面的具體配置:
[root@iZ2zefmrr626i66omb40ryZ docker]# cd da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416/
[root@iZ2zefmrr626i66omb40ryZ da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416]# cat memory.limit_in_bytes
134217728
可以發(fā)現(xiàn),memory.limit_in_bytes 中配置的值為 134217728,轉(zhuǎn)換一下134217728/1024/1024=128M, 剛好就是我們指定的 128M。
然后我們停止該容器
docker stop da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416
再次查看 cgroup 情況
ls -l /sys/fs/cgroup/memory/docker/|grep da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416
發(fā)現(xiàn)目錄已經(jīng)被刪除,說(shuō)明容器對(duì)應(yīng)的子 cgroup 也同步被回收。
把停止的容器 start 一下看看
docker start da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416
再次查看 cgroup 情況
[root@docker ~]# ls -l /sys/fs/cgroup/memory/docker/|grep da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416
drwxr-xr-x 2 root root 0 Jan 6 19:58 da82f9ebd384730dda7f831b4331c9e55893c100c83c0c9b0ce112436aa93416
可以看到,同名目錄又被創(chuàng)建出來(lái)了。
至此,演示完成。
結(jié)論:Docker 容器啟動(dòng)時(shí)創(chuàng)建容器 ID 同名子 group 以實(shí)現(xiàn)資源控制,容器停止時(shí)刪除該子 cgroup。
Demo 中只演示了內(nèi)存限制,其他資源也是類似的
小結(jié)
所以 docker 使用 cgroup 其實(shí)很簡(jiǎn)單,
- 1)為每個(gè)容器創(chuàng)建一個(gè)子 cgroup
- 2)根據(jù) docker run 時(shí)提供的參數(shù)調(diào)整 cgroup 中的配置
- 3)容器被刪除時(shí)同步刪除對(duì)應(yīng)子 cgroup
2.Cgroups 相關(guān)操作命令
這里記錄一下 cgroups 的一些常用操作命令。
hierarchy
創(chuàng)建
由于 Linux Cgroups 是基于內(nèi)核中的 cgroup virtual filesystem 的,所以創(chuàng)建 hierarchy 其實(shí)就是將其掛載到指定目錄。
語(yǔ)法為: mount -t cgroup -o subsystems name /cgroup/name
- 其中 subsystems 表示需要掛載的 cgroups 子系統(tǒng)
- /cgroup/name 表示掛載點(diǎn)(一般為具體目錄)
這條命令同在內(nèi)核中創(chuàng)建了一個(gè) hierarchy 以及一個(gè)默認(rèn)的 root cgroup。
例如:
$ mkdir cg1
$ mount -t cgroup -o cpuset cg1 ./cg1
比如以上命令就是掛載一個(gè) cg1 的 hierarchy 到 ./cg1 目錄,如果指定的 hierarchy 不存在則會(huì)新建。
hierarchy 創(chuàng)建的時(shí)候就會(huì)就會(huì)自動(dòng)創(chuàng)建一個(gè) cgroup 以作為 cgroup 樹中的 root 節(jié)點(diǎn)。
刪除
刪除 hierarchy 則是卸載。
語(yǔ)法為:umount /cgroup/name
- /cgroup/name 表示掛載點(diǎn)(一般為具體目錄)
例如:
$ umount ./cg1
以上命令就是卸載 ./cg1 這個(gè)目錄上掛載的 hierarchy,也就是前面掛載的 cg。
hierarchy 卸載后,相關(guān)的 cgroup 都會(huì)被刪除。
不過(guò) cg1 目錄需要手動(dòng)刪除。
默認(rèn)文件含義
hierarchy 掛載后會(huì)生成一些文件,具體如下:
為了避免干擾,未關(guān)聯(lián)任何 subsystem
$ mkdir cg1
$ mount -t cgroup -o none,name=cg1 cg1 ./cg1
$ tree cg1
cg1
├── cgroup.clone_children
├── cgroup.procs
├── cgroup.sane_behavior
├── notify_on_release
├── release_agent
└── tasks
具體含義如下:
-
cgroup.clone_children:這個(gè)文件只對(duì) cpuset subsystem 有影響,當(dāng)該文件的內(nèi)容為 1 時(shí),新創(chuàng)建的 cgroup 將會(huì)繼承父 cgroup
的配置,即從父 cgroup 里面拷貝配置文件來(lái)初始化新
cgroup,可以參考cgroup.clone_children - cgroup.procs:當(dāng)前 cgroup 中的所有進(jìn)程ID,系統(tǒng)不保證 ID 是順序排列的,且 ID 有可能重復(fù)
-
cgroup.sane_behavior:這個(gè)文件只會(huì)存在于 root cgroup 下面,用于控制某些特性的開(kāi)啟和關(guān)閉。
- 由于 cgroup 一直再發(fā)展,很多子系統(tǒng)有很多不同的特性,因此內(nèi)核用
CGRP_ROOT_SANE_BEHAVIOR來(lái)控制
- 由于 cgroup 一直再發(fā)展,很多子系統(tǒng)有很多不同的特性,因此內(nèi)核用
-
notify_on_release:該文件的內(nèi)容為 1 時(shí),當(dāng) cgroup 退出時(shí)(不再包含任何進(jìn)程和子 cgroup),將調(diào)用 release_agent 里面配置的命令。
- 新 cgroup 被創(chuàng)建時(shí)將默認(rèn)繼承父 cgroup 的這項(xiàng)配置。
-
release_agent:里面包含了 cgroup 退出時(shí)將會(huì)執(zhí)行的命令,系統(tǒng)調(diào)用該命令時(shí)會(huì)將相應(yīng) cgroup 的相對(duì)路徑當(dāng)作參數(shù)傳進(jìn)去。
- 注意:這個(gè)文件只會(huì)存在于 root cgroup 下面,其他 cgroup 里面不會(huì)有這個(gè)文件。
- 相當(dāng)于配置一個(gè)回調(diào)用于清理資源。
- tasks:當(dāng)前 cgroup 中的所有線程 ID,系統(tǒng)不保證 ID 是順序排列的
cgroup.procs 和 tasks 的區(qū)別見(jiàn) cgroup 操作章節(jié)。
release_agent 演示
當(dāng)一個(gè) cgroup 里沒(méi)有進(jìn)程也沒(méi)有子 cgroup 時(shí),release_agent 將被調(diào)用來(lái)執(zhí)行 cgroup 的清理工作。
具體操作流程:
- 首先需要配置 notify_on_release 以開(kāi)啟該功能。
- 然后將腳本內(nèi)容寫入到 release_agent 中去。
- 最后 cgroup 退出時(shí)(不再包含任何進(jìn)程和子 cgroup)就會(huì)執(zhí)行 release_agent 中的命令。
#創(chuàng)建新的cgroup用于演示
dev@ubuntu:~/cgroup/demo$ sudo mkdir test
#先enable release_agent
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 1 > ./test/notify_on_release'
#然后創(chuàng)建一個(gè)腳本/home/dev/cgroup/release_demo.sh,
#一般情況下都會(huì)利用這個(gè)腳本執(zhí)行一些cgroup的清理工作,但我們這里為了演示簡(jiǎn)單,僅僅只寫了一條日志到指定文件
dev@ubuntu:~/cgroup/demo$ cat > /home/dev/cgroup/release_demo.sh << EOF
#!/bin/bash
echo \$0:\$1 >> /home/dev/release_demo.log
EOF
#添加可執(zhí)行權(quán)限
dev@ubuntu:~/cgroup/demo$ chmod +x ../release_demo.sh
#將該腳本設(shè)置進(jìn)文件release_agent
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo /home/dev/cgroup/release_demo.sh > ./release_agent'
dev@ubuntu:~/cgroup/demo$ cat release_agent
/home/dev/cgroup/release_demo.sh
#往test里面添加一個(gè)進(jìn)程,然后再移除,這樣就會(huì)觸發(fā)release_demo.sh
dev@ubuntu:~/cgroup/demo$ echo $$
27597
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 27597 > ./test/cgroup.procs'
dev@ubuntu:~/cgroup/demo$ sudo sh -c 'echo 27597 > ./cgroup.procs'
#從日志可以看出,release_agent被觸發(fā)了,/test是cgroup的相對(duì)路徑
dev@ubuntu:~/cgroup/demo$ cat /home/dev/release_demo.log
/home/dev/cgroup/release_demo.sh:/test
cgroup
創(chuàng)建
創(chuàng)建 cgroup 很簡(jiǎn)單,在父 cgroup 或者 hierarchy 目錄下新建一個(gè)目錄就可以了。
具體層級(jí)關(guān)系就和目錄層級(jí)關(guān)系一樣。
# 創(chuàng)建子cgroup cgroup-cpu
$ mkdir cgroup-cpu
$ cd cgroup-cpu
# 創(chuàng)建cgroup-cpu的子cgroup
$ mkdir cgroup-cpu-1
刪除
刪除也很簡(jiǎn)單,刪除對(duì)應(yīng)目錄即可。
注意:是刪除目錄 rmdir,而不是遞歸刪除目錄下的所有文件。
如果有多層 cgroup 則需要先刪除子 cgroup,否則會(huì)報(bào)錯(cuò):
$ rmdir cgroup-cpu
# 如果cgroup中有進(jìn)程正在本限制,也會(huì)出現(xiàn)這個(gè)錯(cuò)誤,需要先停掉對(duì)應(yīng)進(jìn)程,或者把進(jìn)程移動(dòng)到另外的 cgroup 中(比如父cgroup)
rmdir: failed to remove 'cgroup-cpu': Device or resource busy
先刪除子 cgroup 就可以了:
$ rmdir cg1
$ cd ../
$ rmdir cgroup-cpu
也可以借助 libcgroup 工具來(lái)創(chuàng)建或刪除。
使用 libcgroup 工具前,請(qǐng)先安裝 libcgroup 和 libcgroup-tools 數(shù)據(jù)包
redhat 系統(tǒng)安裝:
$ yum install libcgroup
$ yum install libcgroup-tools
ubuntu 系統(tǒng)安裝:
$ apt-get install cgroup-bin
# 如果提示cgroup-bin找不到,可以用 cgroup-tools 替換
$ apt-get install cgroup-tools
具體語(yǔ)法:
# controllers就是subsystem
# path可以用相對(duì)路徑或者絕對(duì)路徑
$ cgdelete controllers:path
例如:
$ cgcreate cpu:./mycgroup
$ cgdelete cpu:./mycgroup
添加進(jìn)程
創(chuàng)建新的 cgroup 后,就可以往里面添加進(jìn)程了。注意下面幾點(diǎn):
- 在一顆 cgroup 樹里面,一個(gè)進(jìn)程必須要屬于一個(gè) cgroup。
- 所以不能憑空從一個(gè) cgroup 里面刪除一個(gè)進(jìn)程,只能將一個(gè)進(jìn)程從一個(gè) cgroup 移到另一個(gè) cgroup
- 新創(chuàng)建的子進(jìn)程將會(huì)自動(dòng)加入父進(jìn)程所在的 cgroup。
- 這也就是 tasks 和 cgroup.proc 的區(qū)別。
- 從一個(gè) cgroup 移動(dòng)一個(gè)進(jìn)程到另一個(gè) cgroup 時(shí),只要有目的 cgroup 的寫入權(quán)限就可以了,系統(tǒng)不會(huì)檢查源 cgroup 里的權(quán)限。
- 用戶只能操作屬于自己的進(jìn)程,不能操作其他用戶的進(jìn)程,root 賬號(hào)除外。
#--------------------------第一個(gè)shell窗口----------------------
#創(chuàng)建一個(gè)新的cgroup
dev@ubuntu:~/cgroup/demo$ sudo mkdir test
dev@ubuntu:~/cgroup/demo$ cd test
#將當(dāng)前bash加入到上面新創(chuàng)建的cgroup中
dev@ubuntu:~/cgroup/demo/test$ echo $$
1421
dev@ubuntu:~/cgroup/demo/test$ sudo sh -c 'echo 1421 > cgroup.procs'
#注意:一次只能往這個(gè)文件中寫一個(gè)進(jìn)程ID,如果需要寫多個(gè)的話,需要多次調(diào)用這個(gè)命令
#--------------------------第二個(gè)shell窗口----------------------
#重新打開(kāi)一個(gè)shell窗口,避免第一個(gè)shell里面運(yùn)行的命令影響輸出結(jié)果
#這時(shí)可以看到cgroup.procs里面包含了上面的第一個(gè)shell進(jìn)程
dev@ubuntu:~/cgroup/demo/test$ cat cgroup.procs
1421
#--------------------------第一個(gè)shell窗口----------------------
#回到第一個(gè)窗口,隨便運(yùn)行一個(gè)命令,比如 top
dev@ubuntu:~/cgroup/demo/test$ top
#這里省略輸出內(nèi)容
#--------------------------第二個(gè)shell窗口----------------------
#這時(shí)再在第二個(gè)窗口查看,發(fā)現(xiàn)top進(jìn)程自動(dòng)加入了它的父進(jìn)程(1421)所在的cgroup
dev@ubuntu:~/cgroup/demo/test$ cat cgroup.procs
1421
16515
dev@ubuntu:~/cgroup/demo/test$ ps -ef|grep top
dev 16515 1421 0 04:02 pts/0 00:00:00 top
dev@ubuntu:~/cgroup/demo/test$
#在一顆cgroup樹里面,一個(gè)進(jìn)程必須要屬于一個(gè)cgroup,
#所以我們不能憑空從一個(gè)cgroup里面刪除一個(gè)進(jìn)程,只能將一個(gè)進(jìn)程從一個(gè)cgroup移到另一個(gè)cgroup,
#這里我們將1421移動(dòng)到root cgroup
dev@ubuntu:~/cgroup/demo/test$ sudo sh -c 'echo 1421 > ../cgroup.procs'
dev@ubuntu:~/cgroup/demo/test$ cat cgroup.procs
16515
#移動(dòng)1421到另一個(gè)cgroup之后,它的子進(jìn)程不會(huì)隨著移動(dòng)
#--------------------------第一個(gè)shell窗口----------------------
##回到第一個(gè)shell窗口,進(jìn)行清理工作
#先用ctrl+c退出top命令
dev@ubuntu:~/cgroup/demo/test$ cd ..
#然后刪除創(chuàng)建的cgroup
dev@ubuntu:~/cgroup/demo$ sudo rmdir test
cgroup.procs vs tasks
#創(chuàng)建兩個(gè)新的cgroup用于演示
dev@ubuntu:~/cgroup/demo$ sudo mkdir c1 c2
#為了便于操作,先給root賬號(hào)設(shè)置一個(gè)密碼,然后切換到root賬號(hào)
dev@ubuntu:~/cgroup/demo$ sudo passwd root
dev@ubuntu:~/cgroup/demo$ su root
root@ubuntu:/home/dev/cgroup/demo#
#系統(tǒng)中找一個(gè)有多個(gè)線程的進(jìn)程
root@ubuntu:/home/dev/cgroup/demo# ps -efL|grep /lib/systemd/systemd-timesyncd
systemd+ 610 1 610 0 2 01:52 ? 00:00:00 /lib/systemd/systemd-timesyncd
systemd+ 610 1 616 0 2 01:52 ? 00:00:00 /lib/systemd/systemd-timesyncd
#進(jìn)程610有兩個(gè)線程,分別是610和616
#將616加入c1/cgroup.procs
root@ubuntu:/home/dev/cgroup/demo# echo 616 > c1/cgroup.procs
#由于cgroup.procs存放的是進(jìn)程ID,所以這里看到的是616所屬的進(jìn)程ID(610)
root@ubuntu:/home/dev/cgroup/demo# cat c1/cgroup.procs
610
#從tasks中的內(nèi)容可以看出,雖然只往cgroup.procs中加了線程616,
#但系統(tǒng)已經(jīng)將這個(gè)線程所屬的進(jìn)程的所有線程都加入到了tasks中,
#說(shuō)明現(xiàn)在整個(gè)進(jìn)程的所有線程已經(jīng)處于c1中了
root@ubuntu:/home/dev/cgroup/demo# cat c1/tasks
610
616
#將616加入c2/tasks中
root@ubuntu:/home/dev/cgroup/demo# echo 616 > c2/tasks
#這時(shí)我們看到雖然在c1/cgroup.procs和c2/cgroup.procs里面都有610,
#但c1/tasks和c2/tasks中包含了不同的線程,說(shuō)明這個(gè)進(jìn)程的兩個(gè)線程分別屬于不同的cgroup
root@ubuntu:/home/dev/cgroup/demo# cat c1/cgroup.procs
610
root@ubuntu:/home/dev/cgroup/demo# cat c1/tasks
610
root@ubuntu:/home/dev/cgroup/demo# cat c2/cgroup.procs
610
root@ubuntu:/home/dev/cgroup/demo# cat c2/tasks
616
#通過(guò)tasks,我們可以實(shí)現(xiàn)線程級(jí)別的管理,但通常情況下不會(huì)這么用,
#并且在cgroup V2以后,將不再支持該功能,只能以進(jìn)程為單位來(lái)配置cgroup
#清理
root@ubuntu:/home/dev/cgroup/demo# echo 610 > ./cgroup.procs
root@ubuntu:/home/dev/cgroup/demo# rmdir c1
root@ubuntu:/home/dev/cgroup/demo# rmdir c2
root@ubuntu:/home/dev/cgroup/demo# exit
exit
結(jié)論:將線程 ID 加到 cgroup1 的 cgroup.procs 時(shí),會(huì)把線程對(duì)應(yīng)進(jìn)程 ID 加入 cgroup.procs 且還會(huì)把當(dāng)前進(jìn)程下的全部線程 ID 加入到
tasks 中。
這里看起來(lái),進(jìn)程和線程好像效果是一樣的。
區(qū)別來(lái)了,如果此時(shí)把某個(gè)線程 ID 移動(dòng)到另外的 cgroup2 的 tasks 中,會(huì)自動(dòng)把 線程 ID 對(duì)應(yīng)的進(jìn)程 ID 加入到 cgroup2 的
cgroup.procs 中,且只把對(duì)應(yīng)線程加入 tasks 中。
此時(shí) cgroup1 和 cgroup2 的 cgroup.procs 都包含了同一個(gè)進(jìn)程 ID,但是二者的 tasks 中卻包含了不同的線程 ID。
這樣就實(shí)現(xiàn)了線程粒度的控制。但通常情況下不會(huì)這么用,并且在 cgroup V2 以后,將不再支持該功能,只能以進(jìn)程為單位來(lái)配置
cgroup。
3.如何使用 Go 和 Cgroups 交互
其實(shí)挺簡(jiǎn)單的,就是用 Go 翻譯了一遍上面的命令。
后續(xù)則是按照這個(gè)流程實(shí)現(xiàn)自己的 docker。
具體代碼如下:
// cGroups cGroups初體驗(yàn)
func cGroups() {
// /proc/self/exe是一個(gè)符號(hào)鏈接,代表當(dāng)前程序的絕對(duì)路徑
if os.Args[0] == "/proc/self/exe" {
// 第一個(gè)參數(shù)就是當(dāng)前執(zhí)行的文件名,所以只有fork出的容器進(jìn)程才會(huì)進(jìn)入該分支
fmt.Printf("容器進(jìn)程內(nèi)部 PID %d\n", syscall.Getpid())
// 需要先在宿主機(jī)上安裝 stress 比如 apt-get install stress
cmd := exec.Command("sh", "-c", `stress --vm-bytes 200m --vm-keep -m 1`)
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
} else {
// 主進(jìn)程會(huì)走這個(gè)分支
cmd := exec.Command("/proc/self/exe")
cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS | syscall.CLONE_NEWPID}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
fmt.Println(err)
os.Exit(1)
}
// 得到 fork 出來(lái)的進(jìn)程在外部namespace 的 pid
fmt.Println("fork 進(jìn)程 PID:", cmd.Process.Pid)
// 在默認(rèn)的 memory cgroup 下創(chuàng)建子目錄,即創(chuàng)建一個(gè)子 cgroup
err := os.Mkdir(filepath.Join(cgroupMemoryHierarchyMount, "testmemorylimit"), 0755)
if err != nil {
fmt.Println(err)
}
// 將容器加入到這個(gè) cgroup 中,即將進(jìn)程PID加入到cgroup下的 cgroup.procs 文件中
err = ioutil.WriteFile(filepath.Join(cgroupMemoryHierarchyMount, "testmemorylimit", "cgroup.procs"),
[]byte(strconv.Itoa(cmd.Process.Pid)), 0644)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// 限制進(jìn)程的內(nèi)存使用,往 memory.limit_in_bytes 文件中寫入數(shù)據(jù)
err = ioutil.WriteFile(filepath.Join(cgroupMemoryHierarchyMount, "testmemorylimit", "memory.limit_in_bytes"),
[]byte("100m"), 0644)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
cmd.Process.Wait()
}
}
首先是一個(gè) if 判斷,區(qū)分主進(jìn)程和子進(jìn)程,分別執(zhí)行不同邏輯。
- 主進(jìn)程:fork 出子進(jìn)程,并創(chuàng)建 cgroup,然后將子進(jìn)程加入該 cgrouop
- 子進(jìn)程:執(zhí)行 stress 命令,以消耗內(nèi)存,便于查看 memory cgroup 的效果
運(yùn)行并測(cè)試:
lixd ~/projects/docker/mydocker main $ go build main.go
lixd ~/projects/docker/mydocker main $ sudo ./main
fork 進(jìn)程 PID: 21827
當(dāng)前進(jìn)程 pid 1
stress: info: [7] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
根據(jù)輸出可以知道,我們 fork 出的進(jìn)程,pid 為 21827。
通過(guò)pstree -pl查看進(jìn)程關(guān)系:
$pstree -pl
init(1)─┬─init(8)───init(9)───fsnotifier-wsl(10)
├─init(12)───init(13)─┬─exe(20618)─┬─sh(20623)───stress(20624)───stress(20625)
│ │ ├─{exe}(20619)
│ │ ├─{exe}(20620)
│ │ ├─{exe}(20621)
│ │ └─{exe}(20622)
└─zsh(14)───sudo(21821)───main(21822)─┬─exe(21827)─┬─sh(21832)───stress(21833)───stress(21834)
可以看到 21827 進(jìn)程 最終啟動(dòng)了一個(gè) 21834 的 stress 進(jìn)程。
top查看以下內(nèi)存占用:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
21834 root 20 0 208664 101564 272 D 35.2 1.3 0:14.38 stress
可以看到 RES 101564,也就是剛好 100M,說(shuō)明我們的 cgroup 是有效果的。
4. 小結(jié)
本文主要介紹了
- 1)Docker 是如何使用 cgroups 的;
- 2) hierarchy 和 cgroup 相關(guān)的操作,如創(chuàng)建刪除等;
- 3)最后則是簡(jiǎn)單演示了如何使用 Go 和 cgroups 進(jìn)行交互。
至此,cgroups 的相關(guān)內(nèi)容就告一段落了,加上本文一共包括 3 篇文章:
初探 Linux Cgroups:資源控制的奇妙世界
深入剖析 Linux Cgroups 子系統(tǒng):資源精細(xì)管理
包括以下內(nèi)容:
- 1)cgroups 怎么實(shí)現(xiàn)資源控制的
- 2)相關(guān) subsystem 演示
- 3)docker 怎么使用 cgroups 的
- 4)go 怎么操作 cgroups
后續(xù)可以使用 go 實(shí)現(xiàn) docker 的時(shí)候,資源控制就會(huì)使用 go 和 cgroups 交互來(lái)實(shí)現(xiàn)。
如果你對(duì)云原生技術(shù)充滿好奇,想要深入了解更多相關(guān)的文章和資訊,歡迎關(guān)注微信公眾號(hào)。
搜索公眾號(hào)【探索云原生】即可訂閱
5.參考
cgroups(7) — Linux manual page
Linux Cgroup 系列(02):創(chuàng)建并管理 cgroup
cgroup 源碼分析 6——cgroup 中默認(rèn)控制文件的內(nèi)核實(shí)現(xiàn)分析
總結(jié)
以上是生活随笔為你收集整理的Docker 与 Linux Cgroups:资源隔离的魔法之旅的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 深入了解 ReadDirectoryCh
- 下一篇: Sunshine + Moonlight