Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9250815
  • 博文数量: 1669
  • 博客积分: 16831
  • 博客等级: 上将
  • 技术积分: 12594
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-25 07:23
个人简介

柔中带刚,刚中带柔,淫荡中富含柔和,刚猛中荡漾风骚,无坚不摧,无孔不入!

文章分类

全部博文(1669)

文章存档

2023年(4)

2022年(1)

2021年(10)

2020年(24)

2019年(4)

2018年(19)

2017年(66)

2016年(60)

2015年(49)

2014年(201)

2013年(221)

2012年(638)

2011年(372)

分类: 云计算

2014-05-30 10:18:24

warden 资源隔离源码研究

分类: CloudFoundry 68人阅读 评论(0) 收藏 举报

warden的资源隔离,看到了一篇讲的很详细的文章,地址(http://blog.csdn.net/k_james/article/details/8523934),就直接转载了。

 

         cloudfoundry多租户的核心就是资源隔离,假设应用之间没有做到相互隔离,随便一个应用都可以占满cpu,跑满内存,写满磁盘甚至耗尽带宽。那这个PAAS就没法用了。

分为四大块,cpu,memory,disk,bandwidth 。 看源码得知,分别用到了Linuxcgroup quota以及tc(traffic control)

          看下warden工程的目录,其中图所示这几个文件就是负责资源隔离与监控的代码了。

         cgroup.rb主要是命令行info时读取cgroupcontainermemorycpu信息
         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挂到cgroupmemory组群的内存树上,这样用户通过warden提供的命令行修改memory时, 也是warden负责修改cgroup组群上的相应containermemory.limit_in_bytesmemory.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  (string)  # required

    [--limit_in_bytes  (uint64)]  # optional

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使用比例?

说下几个cgroupcpu相关子系统的功能:

cpu子系统负责分配进程间的cpu的使用比例
cpuset子系统可以让某个进程使用哪些cpu和内存资源。比如指定8cpu的哪几核等。
cpuacct子系统统计cpu使用情况。

看看 /sys/fs/cgroup中,只有cpucpuacct子系统。可以知道warden起码只用到了这两个子系统。再看看warden命令行的help命令,查看指令发现,哇,没有limit_cpu这一项。没有设置入口。

同时info --handle 16haaer1lpc时,又有cpu的信息。说明:1.无法设置cpu2.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,而不做设置,就可以做到应用之间不相互受影响吗,答案是肯定的,由cgroupcpu子系统的分配机制相关。刚才提到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。那么两个containercpu.shares的比例就是11,他们分别最多可以占用cpu50%的使用率

如果container继续增加,他们就以同等比例分享cpu资源了。这样就可以做到不影响其他应用。所以cpu资源交给cgroup之后,其实不用做什么配置,所以就可以理解warden没有配置cpu命令的原因了。

 

Disk 资源限制

 

接着说磁盘配额,warden使用linuxquota特性来限制磁盘使用量的。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  (string)  # required

    [--block_limit  (uint32)]  # optional

    [--block  (uint64)]  # optional

    [--block_soft  (uint64)]  # optional

    [--block_hard  (uint64)]  # optional

    [--inode_limit  (uint32)]  # optional

    [--inode  (uint64)]  # optional

    [--inode_soft  (uint64)]  # optional

    [--inode_hard  (uint64)]  # optional

    [--byte_limit  (uint32)]  # optional

    [--byte  (uint64)]  # optional

    [--byte_soft  (uint64)]  # optional

    [--byte_hard  (uint64)]  # optional

 

 

好多可选项,查看quota.rbdo_limit_disk方法,发现quota要设置好多参数,而必选入参其实只有byte_limit。单位是byte

说明下参数:

block是块,就是与byte有个换算关系,所以只需要设置byte_limitinode我们一般配置不了,也控制不了。
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.rbdo_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 ,传入RATEBURST参数。

看下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--0 -> w--1) rule with tc's tbf(token bucket filter) qdisc

# 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--1 -> w--0 -> eth0 -> outside)  rule

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限制,但不能太多。

阅读(874) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~