Cgroup和Namespace在測試中的使用(上)
很多時候需要測試程序在資源受限情況下的表現,普通的做法可能是不斷對系統加壓使能夠分配給目標程序的資源變少,換另一個思路思考,可以嘗試限制分配給目標程序的資源總數,使得機器狀態健康的情況下讓程序資源使用達到飽和。
作為一個正在做着容器項目的人,知道容器技術是依靠Cgroup和Namespace來實現的。在容器中,cpu和內存資源是使用Cgroup來控制,PID、IPC、網絡等資源是通過Namespace來划分。在程序沒有部署在容器的情況下,我們仍可以利用Cgoup和Namespace來構造場景完成一些異常測試,如利用Cgroup的資源控制功能做資源滿載的測試;利用Namespace的資源隔離特性做一些網絡異常測試而不影響其他程序的運行。
Cgroup介紹
Cgroup是進行分組化管理的Linux內核功能,具體的資源管理是通過子系統來完成的。可以理解為子系統就是資源控制器,每種子系統就是一個資源的分配器,比如cpu子系統是控制cpu時間分配的,使用方式如下
安裝(ubuntu)
#apt-get
install cgroup-bin
基本命令
cgclassify
cgclear
cgconfig.conf
cgconfigparser
cgcreate
cgdelete
cgexec
cgget
cgred.conf
cgrules.conf
cgrulesengd
cgset
lscgroup
lssubsys
子系統說明
可以使用lssubsys -a來列出系統支持多少種子系統,和:比如cpu是控制cpu時間片的,memory是控制內存使用的
#lssubsys -
a
cpuset
cpu,cpuacct
memory
devices
freezer
net_cls,net_prio
blkio
perf_event
hugetlb
主要的幾種子系統說明如下:
blkio 這個子系統設置限制每個塊設備的輸入輸出控制。例如:磁盤,光盤以及usb等等。
cpu 這個子系統使用調度程序為cgroup任務提供
cpu的訪問。
cpuacct 產生cgroup任務的
cpu資源報告。
cpuset 如果是多核心的
cpu,這個子系統會為cgroup任務分配單獨的
cpu和內存。
devices 允許或拒絕cgroup任務對設備的訪問。
freezer 暫停和恢復cgroup任務。
memory 設置每個cgroup的內存限制以及產生內存資源報告。
net_cls 標記每個網絡包以供cgroup方便使用。
ns 名稱空間子系統
perf_event: 增加了對每group的監測跟蹤的能力,即可以監測屬於某個特定的group的所有線程以及運行在特定
CPU上的線程
要為Cgroup分配限制的資源,首先要掛載子系統,然后才有控制組,比如想要對目標程序進行內存限制,那就需要掛載memory子系統
使用lssubsys -am來顯示已經掛載的子系統
#lssubsys -am
cpuset
/sys/fs
/cgroup/cpuset
cpu,cpuacct
/sys/fs
/cgroup/cpu,cpuacct
memory
/sys/fs
/cgroup/memory
devices
/sys/fs
/cgroup/devices
freezer
/sys/fs
/cgroup/freezer
net_cls,net_prio
/sys/fs
/cgroup/net_cls,net_prio
blkio
/sys/fs
/cgroup/blkio
perf_event
/sys/fs
/cgroup/perf_event
hugetlb
/sys/fs
/cgroup/hugetlb
可以手動掛載或者卸載子系統,如執行umount /sys/fs/cgroup/memory
,memory子系統就被卸載了,這時候手動執行# mount -t cgroup -o memory memory /sys/fs/cgroup/memory
就又掛載上了。
要確保需要的子系統都掛上了,不然創建控制組的時候會報錯 is not mounted
cgcreate: can't create cgroup /hzmali_test: Cgroup one
of
the needed subsystems
is
not mounted
如何創建control group(即需要資源管理的組)呢, 這里用cgcreate命令,當然也有其他方法, 如cgconfig.conf等
#cgcreate -
g
memory,cpu:/hzmali_test
這里有個重要特性:一個組可以同時做多個資源的限制,如這里我同時限制了memory和cpu,然后memory和cpu子系統目錄下會自動生成這個組的目錄和些文件,如memory
#/sys/fs/cgroup/memory/hzmali_test$ ls -lrt
-rw-r--r--
1 root root
0 Jul
26
20:
56 tasks
-rw-r--r--
1 root root
0 Jul
26
20:
56 notify_on_release
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.use_hierarchy
-r--r--r--
1 root root
0 Jul
26
20:
56 memory
.usage_in_bytes
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.swappiness
-r--r--r--
1 root root
0 Jul
26
20:
56 memory
.stat
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.soft_limit_in_bytes
----------
1 root root
0 Jul
26
20:
56 memory
.pressure_level
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.oom_control
-r--r--r--
1 root root
0 Jul
26
20:
56 memory
.numa_stat
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.move_charge_at_immigrate
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.max_usage_in_bytes
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.limit_in_bytes
-r--r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.usage_in_bytes
-r--r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.tcp
.usage_in_bytes
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.tcp
.max_usage_in_bytes
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.tcp
.limit_in_bytes
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.tcp
.failcnt
-r--r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.slabinfo
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.max_usage_in_bytes
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.limit_in_bytes
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.kmem
.failcnt
--w-------
1 root root
0 Jul
26
20:
56 memory
.force_empty
-rw-r--r--
1 root root
0 Jul
26
20:
56 memory
.failcnt
-rw-r--r--
1 root root
0 Jul
26
20:
56 cgroup
.procs
--w--w--w-
1 root root
0 Jul
26
20:
56 cgroup
.event_control
-rw-r--r--
1 root root
0 Jul
26
20:
56 cgroup.clone_children
文件很多,選幾個重要的講下:
- tasks 可以將想要限制資源的進程都加到這個文件中
- memory.max_usage_in_bytes內存的最大使用量,用來限制資源
-memory.soft_limit_in_bytes 和 memory.limit_in_bytes 的差異是,這個限制並不會阻止進程使用超過限額的內存,只是在系統內存不足時,會優先回收超過限額的進程占用的內存,使之向限定值靠攏。
- memory.oom_control
包含一個標志(0或1)來開啟或者關閉cgroup的OOM killer。如果開啟(1),任務如果嘗試申請內存超過允許,就會被系統OOM killer終止。OOM killer在每個使用cgroup內存子系統中都是默認開啟的。如果需要關閉,則可以向memory.oom_control文件寫入1:
# echo 1 > /sys/fs/cgroup/memory.oom_control
如果OOM killer關閉,那么進程嘗試申請的內存超過允許,那么它就會被暫停,直到額外的內存被釋放
- memory.mem.usage_in_bytes 當前進程內存用量,因為現在還沒有進程加到組里,就是0了
- memory.mem.failcnt顯示內存達到限制值的次數
Cgroup文檔
Cgroup的使用細節,子系統和參數設置都可以可以在https://www.kernel.org/doc/Documentation/cgroups/中找到,繼承等特性由於篇幅所限,可以看下文檔
Cgroup實戰
內存限制測試
用控制組限制目標程序內存使用為1000000 byte,當然,需要root執行
echo "1000000" >memory.limit_in_bytes
一般更推薦用cgset來設置數值
cgset -r memory.limit_
in_bytes=
1000000 hzmali_
test
然后構造一個吃內存的程序,每運行一次內存使用就大幅增加
x=
"hahaha"
while [ True ];
do
x=
$x
$x
$x
$x
$x
$x
$x
$x
$x
$x
sleep
1
done;
然后運行程序,並將進程pid寫入mem下面控制組的tasks中
#./memtest.sh &
[
1]
17638
# echo 17638 > /sys/fs/cgroup/memory/hzmali_test/tasks
使用cgclassify 可以將運行中的進程加到task中,如果控制組有多個資源的控制,使用命令會比echo方便很多
cgclassify -g mem:hzmali_
test
17638
然后這貨就在不斷占內存,由於沒有設置disable oom killing,所以最后會oom被kill掉
#
cat /sys/fs/cgroup/
memory/hzmali_test/
memory.usage_in_bytes
966656
#
cat /sys/fs/cgroup/
memory/hzmali_test/
memory.usage_in_bytes
978944
#
cat /sys/fs/cgroup/
memory/hzmali_test/
memory.usage_in_bytes
995328
#
[1]+ Killed ./memtest.
sh
%Cpu0 : 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
寫個死循環腳本cpu_test.sh跑一下
x=a
while [ True ];
do
x=
$x
done;
如果我不想把機器跑死,這里想要限制組里的進程的CPU使用,有2種做法
1.在cpu子系統中控制cpu調度的配額
先看下當前cpu分配情況
cat
/sys/fs
/cgroup/cpu
/hzmali_test/cpu.cfs_quota_us
-
1
cat
/sys/fs
/cgroup/cpu
/hzmali_test/cpu.cfs_period_us
100000
-1表示無限制,這里改為50000,即相對於cpu.cfs_period_us 來說為50000/100000約占1個核50%的cpu時間
#./cpu_test.sh &
[
1]
17709
# echo 17709 >/sys/fs/cgroup/cpu/hzmali_test/tasks
或者直接使用命令cgexec執行
cgexec -
g cpu:hzmali_test ./cpu_test.
sh
top了下基本上就是在50%的cpu占用
%Cpu0 :
50.5 us,
0.0 sy,
0.0 ni,
49.5 id,
0.0 wa,
0.0 hi,
0.0
si,
0.0
st
%Cpu1 :
0.0 us,
0.3 sy,
0.0 ni,
99.7 id,
0.0 wa,
0.0 hi,
0.0
si,
0.0
st
PID USER PR NI VIRT RES
SHR S %
CPU %MEM TIME+ COMMAND
17709 root
20
0
25368
2020
1764 R
50.2
0.1
1:
14.74 bash
2.在cpuset控制物理cpu的分配
當前使用了上面的方法后,我們發現進程的CPU使用都在Cpu0上,這次希望只用Cpu1來跑這個小程序
所以把控制組也加到cpuset
# cgcreate -g cpuset:/hzmali_test
看一下現在使用的cpu的設置
# cat
/sys/fs
/cgroup/cpuset
/hzmali_test/cpuset.cpus
0-
1
改為只用Cpu1,輸入以下命令
# echo
1 >
/sys/fs
/cgroup/cpuset
/hzmali_test/cpuset.cpus
# echo
17709 >
/sys/fs
/cgroup/cpuset
/hzmali_test/tasks
或用命令
# cgset -r cpuset.cpus='1' hzmali_test
# cgclassify -g cpu,cpuset:hzmali_test 17709
top一下,內存的使用從CPU0到CPU1了
%Cpu0 :
0.0 us,
0.0 sy,
0.0 ni,
99.7 id,
0.0 wa,
0.0 hi,
0.3
si,
0.0
st
%Cpu1 :
50.3 us,
0.0 sy,
0.0 ni,
49.7 id,
0.0 wa,
0.0 hi,
0.0
si,
0.0
st
PID USER PR NI VIRT RES
SHR S %
CPU %MEM TIME+ COMMAND
17709 root
20
0
25368
2108
2076 R
50.1
0.1
8:
56.78 bash
# dd
if=
/dev/sda of=
/dev/
null &
打開iotop看下IO速度
Total DISK
READ : 100.37
M/s |
Total DISK WRITE : 0.00 B/
s
Actual DISK
READ: 100.37
M/s | Actual DISK WRITE: 0.00 B/
s
TID PRIO USER DISK
READ DISK WRITE SWAPIN IO> COMMAND
18081 be/4 root 100.37
M/s 0.00 B/s 0.00 % 1.34 % dd
if=/dev/sda of=/dev/null
為了控制IO速度,在blkio上創建控制組
# cgcreate -g blkio:/hzmali_test
查看下硬盤號
設置硬盤號和對應的讀取速度限制,然后執行同樣的命令
# cgset -r blkio.throttle.read_bps_device="8:0 1000000" hzmali_test
# cgexec -g blkio:hzmali_test "dd if=/dev/sda of=/dev/null"
用iotop查看下,速度果然就降到1M以下
Total DISK
READ :
996.55 K
/s | Total DISK WRITE : 0.00 B/s
Actual DISK
READ:
996.55 K
/s | Actual DISK WRITE: 0.00 B/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
18188 be
/4 root 996.55 K/s
0.00 B
/s 0.00 % 99.99 % dd if=/dev
/sda of=/dev/
null