cgroups I - usage

| 标签 linux 

0x00 what is cgroup?

cgroups 是 Linux 内核的一个功能, 用来限制, 控制与分离一个进程组群的资源(如 CPU, 内存, 网络, 磁盘输入输出等).

0x01 The begin of cgroups

这个项目最早是由 Google 的工程师在 2006 年发起 (主要是 Paul Menage 和 Rohit Seth), 最早的名称为进程容器 (process containers). 在 2007 年时, 因为在 Linux 内核中, 容器 (container) 这个名词有许多不同的意义, 为避免混乱, 被重命名为 cgroup, 并且被合并到 2.6.24 版的内核中去 1.

0x02 feature

cgroup 的一个设计目标是为不同的应用情况提供统一的接口, 从控制单一进程 (像 nice) 到操作系统层虚拟化 (像 opeNVZ,Linux-VServer,LXC).
cgroups 提供 2

  • 资源限制:组可以被设置不超过设定的内存限制;这也包括虚拟内存.
  • 优先化:一些组可能会得到大量的 CPU[5] 或磁盘输入输出通量.
  • 报告:用来衡量系统确实把多少资源用到适合的目的上.
  • 分离:为组分离命名空间, 这样一个组不会看到另一个组的进程、网络连接和文件.
  • 控制:冻结组或检查点和重启动.

0x03 the position of cgroups in kernel

可以在下图看到其所在:

architectures

0x10 The struct of cgroups

0x11 term

  • 任务 (Tasks):就是系统的一个进程.
  • 控制组 (Control Group):一组按照某种标准划分的进程, 比如官方文档中的 Professor 和 Student, 或是 WWW 和 System 之类的, 其表示了某进程组.Cgroups 中的资源控制都是以控制组为单位实现. 一个进程可以加入到某个控制组. 而资源的限制是定义在这个组上. 简单点说,cgroup 的呈现就是一个目录带一系列的可配置文件.
  • 层级 (Hierarchy):控制组可以组织成 hierarchical 的形式, 既一颗控制组的树 (目录结构). 控制组树上的子节点继承父结点的属性. 简单点说,hierarchy 就是在一个或多个子系统上的 cgroups 目录树.
  • 子系统 (Subsystem):一个子系统就是一个资源控制器, 比如 CPU 子系统就是控制 CPU 时间分配的一个控制器. 子系统必须附加到一个层级上才能起作用, 一个子系统附加到某个层级以后, 这个层级上的所有控制族群都受到这个子系统的控制.Cgroup 的子系统可以有很多, 也在不断增加中.

0x12 resource mangement

引用这个图片, 尝试解释一下 cgroup 的资源划分的结构. 不同颜色代表不同group对资源的划分, 不过这个设计存在一些缺陷已经遭到Tejun Heo吐槽, 某个任务归类方式的多样性导致了多个Hierarchy的正交, 导致了进程管理的复杂. 多个子系统的之间的交叉使用导致了管理的复杂, 不过在一些 cgroup 套件里面使用配置文件转移一部这个问题的复杂度.

cgroups2

0x13 subsystem

  • blkio — 这个子系统为块设备设定输入 / 输出限制, 比如物理设备(磁盘, 固态硬盘,USB 等等).
  • cpu — 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问.
  • cpuacct — 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告.
  • cpuset — 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点.
  • devices — 这个子系统可允许或者拒绝 cgroup 中的任务访问设备.
  • freezer — 这个子系统挂起或者恢复 cgroup 中的任务.
  • memory — 这个子系统设定 cgroup 中任务使用的内存限制, 并自动生成​​内存资源使用报告.
  • net_cls — 这个子系统使用等级识别符(classid)标记网络数据包, 可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包.
  • net_prio — 这个子系统用来设计网络流量的优先级
  • hugetlb — 这个子系统主要针对于 HugeTLB 系统进行限制, 这是一个大页文件系统.

0x20 Base usage in CLI

0x21 installation

在使用systemd的系统里面hierarchy由其启动时自动创建, 在rhel6系列中需要yum install libcgroup, 如果是Ubuntu系列的话较新的版本是自带了.
查看 cgroup 文件系统的挂载:

# 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/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
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/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls)

查看子系统:

# lssubsys
cpuset
cpu,cpuacct
memory
devices
freezer
net_cls
blkio
perf_event
hugetlb

0x22 configuration

Linux 中把cgroups实现成了文件系统, 所以对文件系统里面的特定文件进行操作就可以完成对cgroup的配置.

demo3:

int main(void)
{
    int i = 0;
    for(;;) i++;
    return 0;
}

配置: 首先在某个子系统下面建立一个目录, 其目录里面会自动创建与其相关的文件 (文件名表示其意义); 其次置具体参数到文件名; 然后把要限制的进程 PID 放入task的文件.

# mkdir /sys/fs/cgroup/cpu/eleme
# ps uax | grep deadloop
    root       4260 59.0  0.0   4164   352 pts/0    RN   23:12   3:03 ./deadloop
# echo "2000" >> /sys/fs/cgroup/cpu/eleme/cpu.cfs_quota_us
# echo "4260" >> /sys/fs/cgroup/cpu/eleme/tasks

0x23 confirmation

配置前:

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
4434 root      25   5    4164    356    280 R  92.0  0.0   0:04.18 deadloop

配置后:

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
4434 root      25   5    4164    356    280 R   2.0  0.0   1:14.91 deadloop

0x30 netcls usage

之所以单独记录了 netcls 的使用, 是因为netcls相当于cgroup中其他的控制组使用起来不是很方便 (需要外部工具配合).
在 cgroup 中, 通过 netcls 子系统使用识别符 (classid) 标记网络数据包, 标记完成过后一般是以下两个操作.

  • 可用流量控制器 (tc) 从不同的cgroup中关联不同的优先级到数据包, 这个场景多数是用于QOS相关.
  • 也可以用iptables来对这些具有这些特征 (-m cgroup) 流量做一些具体的操作, 这个操作幅度比QOS的要大, 根据iptables的不同table提供的-J有很多动作可以做.

0x32 Basic Queuing technology

在描述Linux QOS之前需要想象最基本的队列技术 - 普通队列 (FIFO), 而后对其有了点改进诞生了由 3 个队列一起工作 (pfifo), 由多个队列一起工作的 (Stochastic Fair Queuing) 和拿着令牌才能走包的 (token bucket filter)4.
需要补充说明的是虽然 linux 也支持基于字节的队列技术bfifo, 但是bfifo在特性支持上要远逊色于pfifo.

pfifo

接口默认队列技术的 pfifo_fast

pfifo(基于 packet 的 fifo) 默认是使用三个队列, 能提供基本的优先级功能.

SFQ

看上去公平的 sfq

sfq 的公平是由 hash 与轮序算法保证的. 更多信息 这里

tbf

拿着令牌才放行的 tbf

0x33 hierarchy Queuing technology

即使这样多数情况依然不够!
比如”A,B,C,D 四个服务, 其中 A 是延迟敏感的视频会议,B 是吞吐量大的 bt 下载,C,D 普通的 web 流量”, 上面提供的这些功能或多或少只能满足一部分服务. 这个时候一个层次化的划分队列被开发出来了, 虽然 linux 也实现了其它的有类队列规则, 但是他们远不如其中的 HTB(Hierarchical Token Bucket) 使用更加广泛 5.

HTB
htb 允许创建层次的队列结构与决定队列之间的关系 (父 - 子, 子 - 子).

一般的使用步骤如下:

  • 1: 匹配和标记流量: 将流量分类待使用, 利用包含一个或者多个匹配参数来选择数据包构成一个 class
  • 2: 创建策略到标记的流量上: 把具体的 class 放到特定的队列中并定义每个 class 携带的动作.
  • 3: 附加策略到到特定接口: 附加策略到全局接口 (全局进, 全局出, 全局进出), 特定接口或者父队列.

htb demo6:

# This line sets a HTB qdisc on the root of eth0, and it specifies that 
# the class 1:30 is used by default. It sets the name of the root as 1:, for future references.
tc qdisc add dev eth0 root handle 1: htb default 30

# This creates a class called 1:1, which is direct descendant of root (the parent is 1:),
# this class gets assigned also an HTB qdisc, and then it sets a max rate of 6mbits, with a burst of 15k
tc class add dev eth0 parent 1: classid 1:1 htb rate 6mbit burst 15k

# The previous class has this branches:

# Class 1:10, which has a rate of 5mbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit burst 15k

# Class 1:20, which has a rate of 3mbit
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 3mbit ceil 6mbit burst 15k

# Class 1:30, which has a rate of 1kbit. This one is the default class.
tc class add dev eth0 parent 1:1 classid 1:30 htb rate 1kbit ceil 6mbit burst 15k

# Martin Devera, author of HTB, then recommends SFQ for beneath these classes:
tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10

HTB 使用信息 这里, 更多理论信息 这里, 实现信息 这里.

0x34 netfilter & iptables

Netfilter, 在 Linux 内核中的一个软件框架, 用于管理网络数据包. 不仅具有网络地址转换(NAT)的功能, 也具备数据包内容修改、以及数据包过滤等防火墙功能.

以上内容引自 Wikipedia,netfilter 做为一个内核框架, 它可以在不同阶段将函数hook进网络栈, 框架本身并不处理数据包 7.

iptables, 一个运行在用户空间的应用软件, 通过控制 Linux 内核 netfilter 模块, 来管理网络数据包的流动与转送. 在大部分的 Linux 系统上面,iptables 是使用 /usr/sbin/iptables 来操作, 文件则放置在手册页(Man page[2])底下, 可以通过 man iptables 指令获取.

iptables做为一个用户态工具, 提供一些术语 (table,chain,match,target) 准确描述了一些网络管理, 这些术语离开iptables上下文可能意义不一样.

netflow

浏览器放大看

iptables默认提供四个table, 不同的table内置了不同的chain, 不同chain提供了不近相同的target.

  • filter: 用于应用过滤规则.
  • nat: 用于应用地址转化.
  • mangle: 用于修改分组数据.
  • raw: 想独立与 netfilter 链接跟踪子系统作用的规则应用于这.(可以在图里由raw表所在位置确认)

具体的的各个chain对数据包的处理流程可以参考上图, 数据包先进入raw表的preroute链, 而后进入manglepreroute链, 如此推理.

iptables demo8:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

把默认 HTTP 端口的数据包由 80 转向 8080 端口, 在路由决策前被处理, 而后进入mangleinput后, 又进入filterinput交与 socket.
-t参数后面认识是table,-A表示对后面的chain进行增加条目, 在往后一些事match规则,-j后面就是target.

0x35 net_cls demo

对前面的QOSiptables有了解, 加上在 2014netfilter支持了cgroup特性, 用户态需要安装新的 iptables, 而后可以matchcgroup相关的流量, 这个时候net_cls才算能和netfilter一起工作 9.

#!/bin/sh

INTERFACE=eno16777736

# configuring tc:
tc qdisc add dev $INTERFACE root handle 10: htb

# creating traffic class 10:1 and configure the rate with 40mbit
tc class add dev $INTERFACE parent 10: classid 10:1 htb rate 40mbit

# filter special traffic
tc filter add dev $INTERFACE parent 10: protocol ip prio 10 handle 1: cgroup

# create new Hierarchy
if [ ! -d /sys/fs/cgroup/net_cls/0 ]; then
    mkdir /sys/fs/cgroup/net_cls/0
fi

# send 0010:0001 to net_cls.classid
echo 0x00100001 >  /sys/fs/cgroup/net_cls/0/net_cls.classid

# use iptables to match cgroup traffic and drop it.
iptables -A OUTPUT -m cgroup --cgroup 0x00100001 -j DROP

reference


PREV     NEXT