linux资源限制的原理,Docker技术原理之Linux Cgroups(资源限制)

0.前言

首先要知道一个运行的容器,其实就是一个受到隔离和资源限制的Linux进程——对,它就是一个进程。前面我们讨论了Docker容器实现隔离用到的技术Linux namespace,本篇我们来讨论容器实现资源限制的技术 Linux CGroups。

1.关于Linux CGroups

Linux Cgroups的全称是Linux Control Groups。它最主要的作用,就是限制一个进程组能够使用的资源上限,包括CPU、内存、磁盘、网络带宽等等。此外,还能够对进程进行优先级设置,以及将进程挂起和恢复等操作。

在Linux中,Cgroups给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下。在我的centos服务器下,用mount指令把它们展示出来:

//CentOS Linux release 7.5.1804

$ mount -t cgroup

cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)

cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)

cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)

cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)

cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)

cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)

cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)

cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)

cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)

cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)

可以看到,在/sys/fs/cgroup下面有很多诸如cpuset、cpu、 memory这样的子目录,也叫子系统。这些都是我这台机器当前可以被Cgroups进行限制的资源种类。而在子系统对应的资源种类下,你就可以看到该类资源具体可以被限制的方法。比如,对CPU子系统来说,我们就可以看到如下几个配置文件:

$ ls /sys/fs/cgroup/cpu

cgroup.clone_children cgroup.sane_behavior cpu.rt_period_us cpu.stat cpuacct.usage_percpu system.slice

cgroup.event_control cpu.cfs_period_us cpu.rt_runtime_us cpuacct.stat notify_on_release tasks

cgroup.procs cpu.cfs_quota_us cpu.shares cpuacct.usage release_agent user.slice

2.举个例子(CPU限制)

1) 配置你的控制组

$ cd /sys/fs/cgroup/cpu

$ mkdir testlimit

$ ls testlimit/

cgroup.clone_children cgroup.procs cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat cpuacct.usage notify_on_release

cgroup.event_control cpu.cfs_period_us cpu.rt_period_us cpu.shares cpuacct.stat cpuacct.usage_percpu tasks

$ cat /sys/fs/cgroup/cpu/testlimit/cpu.cfs_quota_us

-1

$ cat /sys/fs/cgroup/cpu/testlimit/cpu.cfs_period_us

100000

你创建的这个目录testlimit就称为一个“控制组”。你会发现,操作系统会在你新创建的目录下,自动生成该子系统对应的资源限制文件。可以看到testlimit控制组里的CPU quota还没有任何限制(即:-1),CPU period则是默认的100000us

配置一个只能使用30%cpu的限制,即长度为cfs_period的一段时间内,只能被分配到总量为cfs_quota的CPU时间。

$ echo 30000 > /sys/fs/cgroup/cpu/testlimit/cpu.cfs_quota_us

至此我们的testlimit控制组就配置好了,它限制进程在100000us里只能使用30000us的cpu时间。只是目前没有将它应用于任何进程。

2) 执行脚本

$ while : ; do : ; done &

[1] 4477

该脚本执行了一个无限循环,可以把cpu吃到100%,可以看到它在后台的进程id是4477,后面限制的时候我们会用到。

通过top查看cpu使用情况:

%Cpu0 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

3) 使用cgroups限制该进程的cpu

执行如下命令,使用刚才配置好的testlimit控制组,限制上面4477号进程的cpu:

$ echo 4477 > /sys/fs/cgroup/cpu/testlimit/tasks

再次通过top查看cpu使用情况:

%Cpu0 : 30.1 us, 3.0 sy, 0.0 ni, 65.5 id, 1.0 wa, 0.0 hi, 0.3 si, 0.0 st

可以看到使用刚才创建的testlimit控制组,将cpu被限制到了30%左右。

4) 启动一个容器加上CPU时钟周期限制

接下来,我们启动一个容器,并加上cpu限制,然后看看cgroups对应的目录里有没有该容器的限制。

$ docker run -td --cpu-period 100000 --cpu-quota 200000 busybox /bin/sh -c "while : ; do : ; done"

c3e3fb30f3cbdcc707dff9f5937018c0ac6b07002d80656760026111c569ca4f

//查看该容器的进程id: 26430

$ ps -x |grep '/bin/sh'

26430 pts/0 Rs+ 20:52 /bin/sh -c while : ; do : ; done

//查看cgroups

$ cat /sys/fs/cgroup/cpu/docker/c3e3fb30f3cbdcc707dff9f5937018c0ac6b07002d80656760026111c569ca4f/cpu.cfs_period_us

100000

$ cat /sys/fs/cgroup/cpu/docker/c3e3fb30f3cbdcc707dff9f5937018c0ac6b07002d80656760026111c569ca4f/cpu.cfs_quota_us

200000

$ cat /sys/fs/cgroup/cpu/docker/c3e3fb30f3cbdcc707dff9f5937018c0ac6b07002d80656760026111c569ca4f/tasks

26430

$ top

%Cpu0 : 50.8 us, 49.2 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

如上所示,可以通过启动容器时返回的容器ID在cgroups中找到对应的限制。

3.对照Docker源码

// New creates and initializes a new containerd server

func New(ctx context.Context, config *Config) (*Server, error) {

//...

if err := apply(ctx, config); err != nil {

return nil, err

}

//...

}

// apply sets config settings on the server process

func apply(ctx context.Context, config *Config) error {

if config.OOMScore != 0 {

log.G(ctx).Debugf("changing OOM score to %d", config.OOMScore)

if err := sys.SetOOMScore(os.Getpid(), config.OOMScore); err != nil {

log.G(ctx).WithError(err).Errorf("failed to change OOM score to %d", config.OOMScore)

}

}

if config.Cgroup.Path != "" {

cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(config.Cgroup.Path))

if err != nil {

if err != cgroups.ErrCgroupDeleted {

return err

}

if cg, err = cgroups.New(cgroups.V1, cgroups.StaticPath(config.Cgroup.Path), &specs.LinuxResources{}); err != nil {

return err

}

}

if err := cg.Add(cgroups.Process{

Pid: os.Getpid(),

}); err != nil {

return err

}

}

return nil

}

在上面代码中,创建容器时会调用apply接口,里面的cgroups.Load调用就会去加载cgroups,cg.Add把创建的容器进程加入到cgroups task中。

4.下一代Linux Cgroups

在Kernel 3.16后,引入了一个叫__DEVEL__sane_behavior的特性(还在开发试验阶段),它可以把所有子系统都挂载到根层级下,只有叶子节点可以存在tasks,非叶子节点只进行资源控制。

The unified control group hierarchy in 3.16

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值