在实际的虚拟化环境使用过程中,由于一台物理机中开通多个虚拟机,如果不对虚拟机做限制,会造成虚拟机之间相互争抢物理机资源。
Linux中对资源控制这块已经做了一些工作,本文就是讲解Linux 对CPU资源的控制。
CPU调度策略
Linux中,每个进程能够占用CPU多久的时间,以及什么时候能够占用CPU是和系统的调度策略密切相关的。
Linux系统中有多种调度策略,各种调度策略有其适用的场景。
这里对调度策略不做过多说明,支持的调度策略如下:
/*
* Scheduling policies
*/
#define SCHED_NORMAL 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE 5
/* Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */
#define SCHED_RESET_ON_FORK 0x40000000
不过在查看Linux内核说明文档时,还有如下一句话:
"Any time not allocated to a realtime group will be used to run normal priority
tasks (SCHED_OTHER). Any allocated run time not used will also be picked up by
SCHED_OTHER"
大致意思是如果没有使用分配的时间,使用的调度策略就是SCHED_OTHER,也就是还有一种默认策略是SCHED_OTHER。
Linux 系统也提供了修改调度策略的命令和系统调用接口 -- 命令chrt。
查看一个虚拟机进程pid:
#ps axjf | grep 00dd
1 8647 8646 8646 ? -1 Rl 107 6823:04 /usr/libexec/qemu-kvm -name instance-000000dd ... ...
通过chrt命令可以看到其使用的调度策略:
#chrt -p 8647
pid 8647's current scheduling policy: SCHED_OTHER
pid 8647's current scheduling priority: 0
可以将其修改成FIFO策略,优先级为10:
#chrt -p -f 10 8647
再次查看结果如下:
#chrt -p 8647
pid 8647's current scheduling policy: SCHED_FIFO
pid 8647's current scheduling priority: 10
说明:chrt的其他用法可以查看help。
进程的CPU资源控制
内核中提供全局资源控制,也提供有针对某个进程的局部资源控制方法cgroup。
全局资源控制
主要有两个参数决定:sched_rt_period_us 和 sched_rt_runtime_us。
通过sysctl接口可以查看:
#sysctl -n kernel.sched_rt_period_us --> 进程调度的单位CPU时间 1s
1000000
#sysctl -n kernel.sched_rt_runtime_us --> 进程在1s 内实际占用的cpu时间 0.95s
950000
默认设置说明进程在运行时并不是完全占用CPU的,每1秒中有0.05秒的时间可以给其它进程运行。
这样既不会对实时进程的响应时间造成太大的影响,也避免了实时进程卡住时导致整个系统无响应。
可以通过sysctl命令修改上述两个值,如:
#sysctl -w kernel.sched_rt_runtime_us=900000 设置为1s内进程实际占用的cpu时间为0.9s
局部资源控制
全局控制是针对所有进程的,局部控制是针对某个进程的,这里用到了linux的cgroup子系统。
如果想在 cgroup 中对 sched_rt_period_us 和 sched_rt_runtime_us 进行控制, 需要内核编译选项 CONFIG_RT_GROUP_SCHED=y
以centos6系统为例,如果安装了libcgroup安装包,那么系统中会有一个cgconfig服务,此服务启动后,会自动挂载cgroup各个组件。
内容如下:
cgroup /cgroup/cpuset cgroup rw,relatime,cpuset 0 0
cgroup /cgroup/cpu cgroup rw,relatime,cpu 0 0
cgroup /cgroup/cpuacct cgroup rw,relatime,cpuacct 0 0
cgroup /cgroup/memory cgroup rw,relatime,memory 0 0
cgroup /cgroup/devices cgroup rw,relatime,devices 0 0
cgroup /cgroup/freezer cgroup rw,relatime,freezer 0 0
cgroup /cgroup/net_cls cgroup rw,relatime,net_cls 0 0
cgroup /cgroup/blkio cgroup rw,relatime,blkio 0 0
这里只以/cgroup/cpu为例说明,此目录结构如下:
#/cgroup/cpu/
cgroup.event_control cpu.cfs_period_us cpu.rt_period_us cpu.shares release_agent
cgroup.procs cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat notify_on_release tasks
可以看到包含了cpu.rt_period_us和cpu.rt_runtime_us,其内容分别为:
#cat /cgroup/cpu/cpu.rt_period_us
1000000
#cat /cgroup/cpu/cpu.rt_runtime_us
950000
通过配置上述参数,就可以对/cgroup/cpu/tasks中记录的进程做限制,当然在该目录下还可以创建新的子目录结构,只限制某些进程。
cpu利用率控制
控制进程的资源使用率可以通过/cgroup/cpu/cpu.shares(默认值1024)来实现。
比如:
1. 有两个虚拟机instance-000000dd和instance-000000df,两个虚拟机cpu配置一样,内部跑stress(测试工具)将cpu跑到100%;
2. 在/cgroup/cpu/目录下创建目录instance-000000dd和instance-000000df,并将各自的进程号输入到/cgroup/cpu/instance-000000d*/tasks中;
3. 设置各自目录下的cpu.shares值;
3. 使用top命令查看两个虚拟机进程占用的物理机cpu百分比
当两个虚拟机的cpu.shares值均是默认值1024时,可以看到两个虚拟机占用的物理cpu百分比一样。
但是如果将instance-000000dd/cpu.shares设置为1024,instance-000000df/cpu.shares设置为512,可以看到instance-000000df占用的cpu为instance-000000dd的一半。
上述两个虚拟机,如果虚拟机instance-000000df不忙的时候,可以看到虚拟机instance-000000dd可以尽情的使用物理机的cpu资源。这种限制比较灵活,但是也有其缺点,因为还是会有竞争出现。
如果配合kernel scheduler里的cfs bandwidth control,可以做到对cpu资源的硬限制,即不管别的虚拟机是否繁忙,被限制的虚拟机消耗的cpu最多只能到达其被限制的水位。
有兴趣的可以看看cfs bandwidth control的用法。
cpu绑定
上面的例子中,虽然能够控制每个组的CPU的总体占用率,但是不能控制某个组的进程固定在某个物理CPU上运行。
要想将进程绑定到某个固定的CPU上, 需要使用cgroup的另一个组件 -- cpuset 子系统。
比如:
1. 在/cgroup/cpuset/下创建目录instance-000000dd,并将虚拟机instance-000000dd的进程号输入到instance-000000dd/tasks
2. 修改instance-000000dd/cpuset.cpus的值
#echo 0 > /cgroup/cpuset/instance-000000dd/cpuset.cpus --> 将该虚拟机进程绑定到cpu0
#echo 0-7 > /cgroup/cpuset/instance-000000dd/cpuset.cpus --> 将该虚拟机进程绑定到cpu0-7
3. 使用top命令查看虚拟机进程在物理机的哪个cpu运行,可以看到只会在绑定的cpu上调度。