柔中带刚,刚中带柔,淫荡中富含柔和,刚猛中荡漾风骚,无坚不摧,无孔不入!
全部博文(1669)
分类: 云计算
2014-05-30 10:18:24
warden的资源隔离,看到了一篇讲的很详细的文章,地址(http://blog.csdn.net/k_james/article/details/8523934),就直接转载了。
cloudfoundry多租户的核心就是资源隔离,假设应用之间没有做到相互隔离,随便一个应用都可以占满cpu,跑满内存,写满磁盘甚至耗尽带宽。那这个PAAS就没法用了。
分为四大块,cpu,memory,disk,bandwidth 。 看源码得知,分别用到了Linux的cgroup 、quota以及tc(traffic control)。
看下warden工程的目录,其中图所示这几个文件就是负责资源隔离与监控的代码了。
cgroup.rb主要是命令行info时读取cgroup中container的memory和cpu信息
mem_limit.rb 是负责配置内存限制,写入cgroup中,并提供oom,就是内存溢出时的行为。
net.rb ,负责container的网络I/O,通过调用 net_rate.sh来配置tc(traffic control)的。
quato.rb,负责container的磁盘份额。
下面会进一步讲到代码。
warden memory 限制
最常用的就是内存限制了。warden的内存限制与监控,反馈,委托给linux 的cgroup。关于cgroup的概念仅用法参看:
它是control group,就是控制组,是个树形结构,每个root管理着自己下面所有的分支,而且分支共享着root的资源。由各个子系统控制与监控这些组群。最新linux内核(3.2.6)提供了丰富的cgroup
子系统:cpu,cpuset,cpuacct,memory,devices,blkio,net_cls,freezer
cgroup的系统目录位于 /sys/fs/cgroup,可以看到cgroup下有四个子系统:cpu,memory,devices,cpuacct,可能与linux 内核版本有关。内核3.2.0
这一节讲memory,重点看一下memory目录:
-rw-r--r-- 1 root root 0 Dec 21 01:05 cgroup.clone_children
--w--w--w- 1 root root 0 Dec 21 01:05 cgroup.event_control
-rw-r--r-- 1 root root 0 Dec 21 01:05 cgroup.procs
drwxr-xr-x 2 root root 0 Dec 21 01:17 instance-16haaer1lpc/
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.failcnt
--w------- 1 root root 0 Dec 21 01:05 memory.force_empty
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.memsw.failcnt
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.memsw.limit_in_bytes
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.memsw.max_usage_in_bytes
-r--r--r-- 1 root root 0 Dec 21 01:05 memory.memsw.usage_in_bytes
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 Dec 21 01:05 memory.numa_stat
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.oom_control
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 Dec 21 01:05 memory.stat
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.swappiness
-r--r--r-- 1 root root 0 Dec 21 01:05 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 Dec 21 01:05 memory.use_hierarchy
-rw-r--r-- 1 root root 0 Dec 21 01:05 notify_on_release
-rw-r--r-- 1 root root 0 Dec 21 01:05 release_agent
-rw-r--r-- 1 root root 0 Dec 21 01:05 tasks
看到一堆文件,前面几个是cgroup的控制文件,然后是instance-16haaer1lpc,新创建的容器,已经加入到memory的控制组群了。后面是一些memory的参数,最后一个tasks,就是存放要监控的进程IDID
重点看两个文件,也是跟warden配置相关的,一个是memory.limit_in_byes,限制最大内存。一个是memory.memsw.limit_in_bytes,连同swap也一起限制。
vagrant@precise64:/sys/fs/cgroup/memory$ cat memory.limit_in_bytes
9223372036854775807
vagrant@precise64:/sys/fs/cgroup/memory$ cat memory.memsw.limit_in_bytes
9223372036854775807
单位是字节,这个值9223372036854775807刚好是long型最大值,所以默认是没有限制的。可以理解为无限大,你可以使用vm上的所有内存资源。
memory目录下的这些参数可以理解为树的root资源,有个分支,就是instance-16haaer1lpc,此目录下的各文件参数与root一样,默认是无限制的。我们可以做个限制。限制两个limit参数。
root@precise64:/sys/fs/cgroup/memory/instance-16haaer1lpc# echo 100M >> memory.limit_in_bytes
root@precise64:/sys/fs/cgroup/memory/instance-16haaer1lpc# cat memory.limit_in_bytes
104857600
会自动转化为字节,这样warden容器16haaer1lpc最大只能用这么大的内存了,memory.memsw.limit_in_bytes一个道理。
我们大概知道了,warden其实是将container委托给cgroup来管理,监控,以及反馈的。 那就是创建container时,warden负责把container挂到cgroup的memory组群的内存树上,这样用户通过warden提供的命令行修改memory时, 也是warden负责修改cgroup组群上的相应container的memory.limit_in_bytes和memory.memsw.limit_in_bytes。具体我们来看一下命令行。
先查下默认的值,一堆memory参数,重点看memory_stat.hierarchical_memory_limit 和 memory_stat.hierarchical_memsw_limit,是不是long最大值9223372036854775807
info --handle 16haaer1lpc
warden> limit_memory --help
command: limit_memory
description: Set or get the memory limit for the container.
usage: limit_memory [options]
[options] can be one of the following:
--handle
[--limit_in_bytes
warden> limit_memory --handle 16haaer1lpc --limit_in_bytes 104857600
limit_in_bytes : 104857600
操作前先--help看下用法。现在 info --handle 16haaer1lpc 发现两个值变了。
warden> info --handle 16haaer1lpc
state : active
host_ip : 10.254.0.1
container_ip : 10.254.0.2
container_path : /opt/warden/warden/data/containers/16haaer1lpc
...
memory_stat.hierarchical_memory_limit : 104857600
memory_stat.hierarchical_memsw_limit : 104857600
....
这时候查看 /sys/fs/cgroup/memory/instance-16haaer1lpc 目录下,两个文件的值同时被修改了,说明warden通过命令行,只修改这两个值。
root@precise64:/sys/fs/cgroup/memory/instance-16haaer1lpc# cat memory.limit_in_bytes
104857600
root@precise64:/sys/fs/cgroup/memory/instance-16haaer1lpc# cat memory.memsw.limit_in_bytes
104857600
mem_limit.rb中的do_limit_memory方法有这么两句,就是往cgroup中写值了。
["memory.limit_in_bytes", "memory.memsw.limit_in_bytes"].each do |path|
File.open(File.join(cgroup_path(:memory), path), 'w') do |f|
f.write(request.limit_in_bytes.to_s)
end
end
没有测试过应用,有兴趣的可以测试下,把进程号放进对应的taks文件中就可以。
cpu 相互隔离
先抛出一个问题:cpu隔离什么呢。 使用时间? 指定哪一个cpu核? 独占某个cpu? 限制cpu使用比例?
说下几个cgroup的cpu相关子系统的功能:
cpu子系统负责分配进程间的cpu的使用比例
cpuset子系统可以让某个进程使用哪些cpu和内存资源。比如指定8核cpu的哪几核等。
cpuacct子系统统计cpu使用情况。
看看 /sys/fs/cgroup中,只有cpu和cpuacct子系统。可以知道warden起码只用到了这两个子系统。再看看warden命令行的help命令,查看指令发现,哇,没有limit_cpu这一项。没有设置入口。
同时info --handle 16haaer1lpc时,又有cpu的信息。说明:1.无法设置cpu,2.cpu的信息是从cpuacct是取得的。可以查看cgroup.rb中的do_info方法:
def do_info(request, response)
super(request, response)
begin
fields = read_memory_stats
response.memory_stat = Protocol::InfoResponse::MemoryStat.new(fields)
rescue => e
raise WardenError.new("Failed getting memory usage: #{e}")
end
begin
fields = read_cpu_stats
response.cpu_stat = Protocol::InfoResponse::CpuStat.new(fields)
rescue => e
raise WardenError.new("Failed getting cpu stats: #{e}")
end
nil
end
这么说难道cpu没有做到资源隔离,一个无限创建线程的程序就可以跑满cpu。事实是这样的吗?来验证一下:
1. 本地vcap环境,是没有warden的,一跑程序,果然超过cpu 100%。
2. 自己也没架起cf-release集群,扔到cloudfoundry.com上试试。url测试,一会儿就报错了,不像本地,浏览器无限等待。同时在部署在cloundfoundry.com上的其他应用不受影响。
结论:
1. 本地跑满cpu,是因为没有warden,没有container,没有交给cgroup管理。
2. cloudfoundry.com应用了warden,代码里没看到有cpu设置。但貌似可以做到隔离,其他应用不受影响。
简单把container交给cgroup,而不做设置,就可以做到应用之间不相互受影响吗,答案是肯定的,由cgroup的cpu子系统的分配机制相关。刚才提到cpu子系统是负责分配进程间的cpu使用比例。
看下 /sys/fs/cgroup/cpu目录
-rw-r--r-- 1 root root 0 Dec 21 01:05 cgroup.clone_children
--w--w--w- 1 root root 0 Dec 21 01:05 cgroup.event_control
-rw-r--r-- 1 root root 0 Dec 21 01:05 cgroup.procs
-rw-r--r-- 1 root root 0 Dec 21 01:05 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Dec 21 01:05 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Dec 21 01:05 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Dec 21 01:05 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Dec 21 01:05 cpu.shares
-r--r--r-- 1 root root 0 Dec 21 01:05 cpu.stat
drwxr-xr-x 2 root root 0 Dec 21 01:17 instance-16haaer1lpc/
-rw-r--r-- 1 root root 0 Dec 21 01:05 notify_on_release
-rw-r--r-- 1 root root 0 Dec 21 01:05 release_agent
-rw-r--r-- 1 root root 0 Dec 21 01:05 tasks
同样有instance-16haaer1lpc这一目录。cpu子系统最重要看cpu.shares。里面的默认值是1024。只是一个值。看instance-16haaer1lpc目录下的cpu.shares,也是1024。
说明目前cpu子系统只管理着一个进程,没人跟你抢。如果再来一个container,可以看到cpu.shares也是1024。那么两个container的cpu.shares的比例就是1:1,他们分别最多可以占用cpu的50%的使用率
如果container继续增加,他们就以同等比例分享cpu资源了。这样就可以做到不影响其他应用。所以cpu资源交给cgroup之后,其实不用做什么配置,所以就可以理解warden没有配置cpu命令的原因了。
Disk 资源限制
接着说磁盘配额,warden使用linux的quota特性来限制磁盘使用量的。quota可以限制硬盘某一分区下用户组或用户的磁盘使用量,并进行监控。关于quota的概念和使用可以上网查查。这里只做简单说明。
命令行查看如何设置disk:
warden> limit_disk --help
command: limit_disk
description: set or get the disk limit for the container.
usage: limit_disk [options]
[options] can be one of the following:
--handle
[--block_limit
[--block
[--block_soft
[--block_hard
[--inode_limit
[--inode
[--inode_soft
[--inode_hard
[--byte_limit
[--byte
[--byte_soft
[--byte_hard
好多可选项,查看quota.rb的do_limit_disk方法,发现quota要设置好多参数,而必选入参其实只有byte_limit。单位是byte
说明下参数:
block是块,就是与byte有个换算关系,所以只需要设置byte_limit,inode我们一般配置不了,也控制不了。
soft值就是超过了有警告,并被要求在一定时间内返回soft值以下,hard就是硬指标,不能超过。一般soft
测试一下。
首先查看当前container的磁盘使用量,才16K,其实就是看vcap下的磁盘使用情况。
root@precise64:/opt/warden/warden/data/containers/16haaer1lpc/tmp/rootfs/home# ll
total 12
drwxr-xr-x 3 root root 4096 Dec 21 01:17 ./
drwxr-xr-x 6 root root 4096 Dec 21 01:17 ../
drwxr-xr-x 2 10000 10000 4096 Dec 21 01:17 vcap/
root@precise64:/opt/warden/warden/data/containers/16haaer1lpc/tmp/rootfs/home# cd vcap
root@precise64:/opt/warden/warden/data/containers/16haaer1lpc/tmp/rootfs/home/vcap# du -sh
16K .
注意到为什么要ll来查看目录,vcap的用户组是10000,文章刚开始就提到过,这个有说法。quota正是根据用户组来控制磁盘的,不同的container有不同的用户组,再创建一个container,用户组就是10001,往下累加。查看下当前所有用户的磁盘使用情况。
/dev/mapper/precise64-root 分区下所有用户的磁盘使用情况。值为0说明没有限制。看到10000用户使用了264M,没有限制最大值。
warden>limit_disk --handle 16haaer1lpc 102457600
设置100M 然后再查看 repquota -auvs。接着copy_in一个大于100m的文件,就会报错。这里就不再演示了。
BankWidth 带宽限制
命令行设置会调用net.rb的do_limit_bandwidth方法。
def do_limit_bandwidth(request, response)
sh File.join(container_path, "net_rate.sh"), :env => {
"BURST" => request.burst,
"RATE" => request.rate * 8, # Bytes to bits
}
response.rate = request.rate
response.burst = request.burst
end
它调用net_rate.sh ,传入RATE和BURST参数。
看下net_rate.sh
# clear rule if exist
# delete root egress tc qdisc
tc qdisc del dev ${network_host_iface} root 2> /dev/null || true
# delete root ingress tc qdisc
tc qdisc del dev ${network_host_iface} ingress 2> /dev/null || true
# set inbound(outside -> eth0 -> w-
# rate is the bandwidth
# burst is the burst size
# latency is the maxium time the packet wait to enqueue while no token left
tc qdisc add dev ${network_host_iface} root tbf rate ${RATE}bit burst ${BURST} latency 25ms
# set outbound(w-
tc qdisc add dev ${network_host_iface} ingress handle ffff:
# use u32 filter with target(0.0.0.0) mask (0) to filter all the ingress packets
tc filter add dev ${network_host_iface} parent ffff: protocol ip prio 1 u32 match ip src 0.0.0.0/0 police rate ${RATE}bit burst ${BURST} drop flowid :1
大概意思是先删掉先前的设置,再创建新的,利用tc 的 tbf 过滤器,就是令牌桶。RATE指定速率,BURST 指定了最大能有多少个桶。tc就是监控create container时创建的虚拟网卡。
每个输入输出数据都要经过这一tc 队列,每个请求数据通过一个桶,桶的速率是rate。最大有burst/rate个桶。可以允许超过burst限制,但不能太多。