• 在Linux系统启动Docker时如何限制硬件资源
  • 发布于 2个月前
  • 210 热度
    0 评论
背景
自从 docker 这一类容器化工具出来之后,我们想对应用做一些资源限制的时候已经是非常的简单了,在启动容器的时候加一些限制的参数就可以了。在不同的操作系统下面,资源限制这一块还是有一些不一样的地方。linux 一般就是 cgroup v1 或 cgroup v2,windows 则是 kernel 32。在 dotnet/crank 中,对这两种操作系统都有实现,下面主要来看看Linux的一些实现细节。

Linux 下的资源限制
crank 是依赖于 cgroup-tools,所以需要在操作系统上面先进行安装才能使用。由于 cgroup v1 和 v2 之间有一些差异,所以它的实现也是根据不同版本的语义进行了区分。区分 v1 v2 用的是下面的命令:
stat -fc %T /sys/fs/cgroup/

# cgroup v1 返回 tmpfs
# cgroup v2 返回 cgroup2fs
创建 cgroup 和 执行命令在 v1 和 v2 是一样的,所以这一块不需要区分。
cgcreate -g memory,cpu,cpuset:benchmark-1-1
# 具体的限制语义设置。。。
cgexec -g memory,cpu,cpuset:benchmark-1-1 /tmp/publised/webapp
cgcreate 就是用来创建 cgroup 的一个命令,可以看到 crank 这块主要是针对了 memroy,cpu,cpuset 这三个做了限制。

下面就分别看看这三个限制的具体内容。

内存
先来看看 v1 的内存限制:
# 限制 1G 的内存
cgset -r memory.limit_in_bytes=1073741824 benchmark-1-1
# 不限制内存
cgset -r memory.limit_in_bytes=-1 benchmark-1-1
v2 的语义发生了变化,与 memory.limit_in_bytes 相对应的是 memory.max,所以与之对应的是
cgset -r memory.max=1073741824 benchmark-1-1
# 不限制内存
cgset -r memory.max=max benchmark-1-1
参考:
https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/memory.html

https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files


cpu
先来看看 v1 cpu 的限制:
The amount of CPU available for the process, e.g., "0.5". For a 12 cores machines this value would result in 50% out of 1200% total available CPU time.
# 限制 50% 的 cpu,其中 cfs_period_us = 10000 是为了和docekr对齐
cgset -r cpu.cfs_period_us=100000 benchmark-1-1
cgset -r cpu.cfs_quota_us=50000 benchmark-1-1

# 不限制 cpu
cgset -r cpu.cfs_quota_us=-1 benchmark-1-1
v2 这边的语义变化的还是比较大的,把 cfs_period_us 和 cfs_quota_us 整合到 max 上面了。
cgset -r cpu.max="50000 100000" benchmark-1-1
cgset -r cpu.max="max 100000" benchmark-1-1
参考:
https://docs.kernel.org/scheduler/sched-bwc.html

https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#cpu-interface-files


cpuset
crank 这块用的的关于cpuset 的语义没有不一样的地方,都是 cpuset.cpus 和 cpuset.mems
# 限制使用 0和1 两个cpu
cgset -r cpuset.cpus=0-1 benchmark-1-1
cgset -r cpuset.mems=0 benchmark-1-1

########

# 不限制cpu,8个cpu都可以使用 (8c机器)
cgset -r cpuset.cpus=0-7 benchmark-1-1
cgset -r cpuset.mems=0 benchmark-1-1
有一个需要注意的是, cpuset.mems 的取值问题,在 v1 时,是从 /sys/fs/cgroup/cpuset/cpuset.mems 中取, v2 则是 /sys/fs/cgroup/{controller}/cpuset.mems 或 /sys/fs/cgroup/{controller}/cpuset.mems.effective。

这个主要是由于 v1 和 v2 的层级发生了变化。
cpuset 还可以在 NUMA 架构的机器上面发挥一定的作用。

参考:
https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cpusets.html
https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#cpuset-interface-files
来看一个 crank 具体的例子:
crank --config ./hello.benchmarks.yml --profile local --scenario hello \
          --application.cpuSet "0" \
          --application.cpuLimitRatio 0.5 \
          --application.memoryLimitInBytes 1073741824
Agent 的具体输出
# 堆代码 duidaima.com
[18:53:09 INF] [/] chmod +x /tmp/benchmarks-agent/benchmarks-server-1/ancl3ltc.4a1/src/hello/published/hello
[18:53:09 INF] [/] cgcreate -g memory,cpu,cpuset:benchmarks-1-4
[18:53:09 INF] [/] cgset -r memory.max=1073741824 benchmarks-1-4
[18:53:09 INF] [/] cgset -r cpu.max="50000 100000" benchmarks-1-4
[18:53:09 INF] [/] cgset -r cpuset.cpus=0 benchmarks-1-4
[18:53:09 INF] [/] cgset -r cpuset.mems=0 benchmarks-1-4
[18:53:09 INF] [/] Invoking executable: cgexec
[18:53:09 INF] [/]   Arguments: -g memory,cpu,cpuset:benchmarks-1-4 /tmp/benchmarks-agent/benchmarks-server-1/ancl3ltc.4a1/src/hello/published/hello
[18:53:09 INF] [/]   Working directory: /tmp/benchmarks-agent/benchmarks-server-1/ancl3ltc.4a1/src/hello/published
除了上面提到的语义之外,还有硬盘,网络相关的也可以进行限制。如果在没有安装 cgroup-tools 的前提下,需要通过 echo xx -> yyy 的方式来设置。
下面再来看2个 docker 限制内存和cpu的例子。

docker 示例
这里启动两个 nginx 容器,一个限制 200M 内存和 50000 的 cpu-quota 以及 100000 的 cpu-period,另一个不做任何限制。
# 8614aabd3e57  v1
# 0a7e61dd556f  v2
docker run -d -m 200M --cpu-quota=50000 --cpu-period=100000 --name ng-cg0 nginx
# 118bc903caee  v1
# ce356350e956  v2
docker run -d --name ng-cg1 nginx
然后分别看看 cgroup 的限制情况

cgroup v1
先是做了限制的容器:
cd /sys/fs/cgroup/memory/docker
cat 8614aabd3e57*****/memory.limit_in_bytes
# output is 209715200

cd /sys/fs/cgroup/cpu/docker
cat 8614aabd3e57*****/cpu.cfs_quota_us 
# output is 50000
cat 8614aabd3e57*****/cpu.cfs_period_us 
# output is 100000
然后是没做限制的容器:
cd /sys/fs/cgroup/memory/docker
cat 118bc903caee*****/memory.limit_in_bytes 
# output is 9223372036854771712

cd /sys/fs/cgroup/cpu/docker
cat 118bc903caee*****/cpu.cfs_quota_us 
# output is -1
cat 118bc903caee*****/cpu.cfs_period_us 
# output is 100000
cgroup v1 的 cgroup 控制文件在 /sys/fs/cgroup/<控制器>/docker/<longid>/ 或 /sys/fs/cgroup/<控制器>/system.slice/docker-<longid>.scope/ 下面。

cgroup v2
先是做了限制的容器:
cd /sys/fs/cgroup/system.slice/docker-0a7e61dd556f*****.scope
cat memory.max
# output is 209715200

cat cpu.max
# output is 50000 100000
然后是没做限制的容器:
cd /sys/fs/cgroup/system.slice/docker-ce356350e956*****.scope
cat memory.max
# output is max

cat cpu.max
# output is max 100000
cgroup v1 的 cgroup 控制文件在 /sys/fs/cgroup/system.slice/docker-<longid>.scope/ 或 /sys/fs/cgroup/docker/<longid/> 下面。可以看到是做了不同版本的区分已经符合 cgroup 预期行为的。

写在最后
这篇是从 dotnet/crank 中用到 cgroup 知识的一个小结,也衍生出了一些 cgroup 相关的知识。
下面 3 个文件就是实现的具体细节,想了解详细的可以参考下面的链接:
https://github.com/dotnet/crank/blob/main/src/Microsoft.Crank.Agent/CGroup.cs https://github.com/dotnet/crank/blob/main/src/Microsoft.Crank.Agent/CGroupV1.cs https://github.com/dotnet/crank/blob/main/src/Microsoft.Crank.Agent/CGroupV2.cs

后面有空再写写 windows 下面的资源限制。
用户评论