公司有一些云服務(wù)器,在華為云上,很多云服務(wù)器資源占用率不高,處于空閑狀態(tài)。我擔(dān)心領(lǐng)導(dǎo)檢測(cè)到這些資源空閑的云服務(wù)器,會(huì)要求我們降低配置,同時(shí)會(huì)降低云服務(wù)器的采購(gòu)預(yù)算。所以就想寫(xiě)一個(gè)shell腳本,模擬資源占用
思路
使用stress對(duì)內(nèi)存進(jìn)行壓測(cè),占用剩余內(nèi)存的80%,可以模擬CPU和內(nèi)存消耗
使用dd工具生成大文件,占用第二塊硬盤(pán)剩余空間的80%,可以模擬硬盤(pán)空間消耗和IO
腳本持續(xù)20分鐘,一旦檢測(cè)到內(nèi)存占用超過(guò)80就停止
不能壓測(cè)CPU,擔(dān)心CPU占用率過(guò)高導(dǎo)致應(yīng)用異?;蛘叻?wù)器重啟
相關(guān)工具
stress
stress 命令主要用來(lái)模擬系統(tǒng)負(fù)載較高時(shí)的場(chǎng)景,本文介紹其基本用法。文中 demo 的演示環(huán)境為 ubuntu 18.04。
可選參數(shù)
-c, --cpu N 產(chǎn)生 N 個(gè)進(jìn)程,每個(gè)進(jìn)程都反復(fù)不停的計(jì)算隨機(jī)數(shù)的平方根
-i, --io N 產(chǎn)生 N 個(gè)進(jìn)程,每個(gè)進(jìn)程反復(fù)調(diào)用 sync() 將內(nèi)存上的內(nèi)容寫(xiě)到硬盤(pán)上
-m, --vm N 產(chǎn)生 N 個(gè)進(jìn)程,每個(gè)進(jìn)程不斷分配和釋放內(nèi)存 –vm-bytes B 指定分配內(nèi)存的大小
–vm-stride B 不斷的給部分內(nèi)存賦值,讓 COW(Copy On Write)發(fā)生 –vm-hang N 指示每個(gè)消耗內(nèi)存的進(jìn)程在分配到內(nèi)存后轉(zhuǎn)入睡眠狀態(tài) N 秒,然后釋放內(nèi)存,一直重復(fù)執(zhí)行這個(gè)過(guò)程
–vm-keep 一直占用內(nèi)存,區(qū)別于不斷的釋放和重新分配(默認(rèn)是不斷釋放并重新分配內(nèi)存)
-d, --hadd N 產(chǎn)生 N 個(gè)不斷執(zhí)行 write 和 unlink 函數(shù)的進(jìn)程(創(chuàng)建文件,寫(xiě)入內(nèi)容,刪除文件)
–hadd-bytes B 指定文件大小
-t, --timeout N 在 N 秒后結(jié)束程序 –backoff N 等待N微妙后開(kāi)始運(yùn)行
-q, --quiet 程序在運(yùn)行的過(guò)程中不輸出信息
-n, --dry-run 輸出程序會(huì)做什么而并不實(shí)際執(zhí)行相關(guān)的操作
–version 顯示版本號(hào)
-v, --verbose 顯示詳細(xì)的信息
消耗 CPU 資源
stress 消耗 CPU 資源是通過(guò)調(diào)用 sqrt 函數(shù)計(jì)算由 rand 函數(shù)產(chǎn)生的隨機(jī)數(shù)的平方根實(shí)現(xiàn)。下面的命令會(huì)產(chǎn)生 4 個(gè)這樣的進(jìn)程不斷計(jì)算:
$ stress -c 4
使用 top 命令查看 CPU 的狀態(tài)如下(CPU 在用戶(hù)態(tài)滿(mǎn)負(fù)荷運(yùn)轉(zhuǎn)):
消耗內(nèi)存資源
下面的命令產(chǎn)生兩個(gè)子進(jìn)程,每個(gè)進(jìn)程分配 300M 內(nèi)存:
$ stress --vm 2 --vm-bytes 300M --vm-keep
父進(jìn)程處于睡眠狀態(tài),兩個(gè)子進(jìn)程負(fù)責(zé)資源消耗。
–vm-keep 一直占用內(nèi)存,區(qū)別于不斷的釋放和重新分配(默認(rèn)是不斷釋放并重新分配內(nèi)存)。 –vm-hang N 指示每個(gè)消耗內(nèi)存的進(jìn)程在分配到內(nèi)存后轉(zhuǎn)入睡眠狀態(tài) N 秒,然后釋放內(nèi)存,一直重復(fù)執(zhí)行這個(gè)過(guò)程。
–vm-keep 和 --vm-hang 都可以用來(lái)模擬只有少量?jī)?nèi)存的機(jī)器,但是指定它們時(shí) CPU 的使用情況是不一樣的。
$stress --vm 2 --vm-bytes 500M --vm-keep
一直在進(jìn)行默認(rèn)的 stride 操作,user 非常高(cpu 在用戶(hù)態(tài)忙碌)。
$ stress --vm 2 --vm-bytes 500M --vm-hang 5
上面這兩種狀態(tài)不斷切換,但整體上看 CPU 的負(fù)載并不高。
–vm-stride B 不斷的給部分內(nèi)存賦值,讓 COW(Copy On Write)發(fā)生。只要指定了內(nèi)存相關(guān)的選項(xiàng),這個(gè)操作就會(huì)執(zhí)行,只是大小為默認(rèn)的 4096。賦值內(nèi)存的比例由參數(shù)決定:
for (i = 0; i < bytes; i += stride) ptr[i] = ‘Z’; /* Ensure that COW happens. */ bytes 為消耗的總內(nèi)存大小,stride 為間隔。 該參數(shù)會(huì)影響 CPU 狀態(tài) us 和 sy:
$ stress --vm 2 --vm-bytes 500M --vm-stride 64 $ stress --vm 2 --vm-bytes 500M --vm-stride 1M
為什么會(huì)產(chǎn)生這樣的結(jié)果?原因是單獨(dú)的賦值和對(duì)比操作可以讓 CPU 在用戶(hù)態(tài)的負(fù)載占到 99% 以上。–vm-stride 值增大就意味著減少賦值和對(duì)比操作,這樣就增加了內(nèi)存的釋放和分配次數(shù)(cpu在內(nèi)核空間的負(fù)載)。 不指定 --vm-stride 選項(xiàng)就使用默認(rèn)值是 4096,CPU 負(fù)載情況居于前兩者之間:
$ stress --vm 2 --vm-bytes 500M
消耗 IO 資源 下面的命令產(chǎn)生 4 個(gè)進(jìn)程,每個(gè)進(jìn)程都反復(fù)調(diào)用 sync 函數(shù)將內(nèi)存上的內(nèi)容寫(xiě)到硬盤(pán)上:
$ stress -i 4
使用 top 命令查看 CPU 的狀態(tài)如下:
sy 升高,wa(iowait) 非常高。
壓測(cè)磁盤(pán)及 IO 下面的命令創(chuàng)建一個(gè)進(jìn)程不斷的在磁盤(pán)上創(chuàng)建 10M 大小的文件并寫(xiě)入內(nèi)容:
$ stress -d 1 --hdd-bytes 10M
使用 top 命令查看 CPU 的狀態(tài)如下(此時(shí)的 CPU 主要消耗在內(nèi)核態(tài)):
下面是 iostat 2 的輸出(同樣是高 iowait,瓶頸是寫(xiě)磁盤(pán)):
其它選項(xiàng)
–verbose 顯示 stress 程序運(yùn)行過(guò)程中的詳細(xì)信息:
–timeout N 在 N 秒后結(jié)束程序。
–quiet stress 程序運(yùn)行的過(guò)程中不輸出信息。
-n, --dry-run 輸出程序會(huì)做什么而并不實(shí)際執(zhí)行相關(guān)的操作:
–backoff N 讓新 fork 出來(lái)的進(jìn)程 sleep N 微秒再開(kāi)始運(yùn)行。
除了單獨(dú)指定某一類(lèi)的選項(xiàng),還可以同時(shí)執(zhí)行多個(gè)類(lèi)型的任務(wù),比如產(chǎn)生 3 個(gè) CPU 進(jìn)程、3 個(gè) IO 進(jìn)程、2 個(gè)10M 的 vm 進(jìn)程,并且每個(gè) vm 進(jìn)程中不循環(huán)分配釋放內(nèi)存:
$ stress --cpu 3 --io 3 --vm 2 --vm-bytes 10M --vm-keep
dd
dd 命令 用于復(fù)制文件并對(duì)原文件的內(nèi)容進(jìn)行轉(zhuǎn)換和格式化處理。dd 命令功能很強(qiáng)大的,對(duì)于一些比較底層的問(wèn)題,使用 dd 命令往往可以得到出人意料的效果。用的比較多的還是用 dd 來(lái)備份裸設(shè)備。但是不推薦,如果需要備份 oracle 裸設(shè)備,可以使用 rman 備份,或使用第三方軟件備份,使用 dd 的話,管理起來(lái)不太方便。需要的時(shí)候使用 dd 對(duì)物理磁盤(pán)操作,如果是文件系統(tǒng)的話還是使用 tar backup cpio 等其他命令更加方便。另外,使用 dd 對(duì)磁盤(pán)操作時(shí),最好使用塊設(shè)備文件。
語(yǔ)法
dd (選項(xiàng))
命令選項(xiàng)
bs=<字節(jié)數(shù)>:將ibs(輸入)與obs(輸出)設(shè)成指定的字節(jié)數(shù); cbs=<字節(jié)數(shù)>:轉(zhuǎn)換時(shí),每次只轉(zhuǎn)換指定的字節(jié)數(shù); conv=<關(guān)鍵字>:指定文件轉(zhuǎn)換的方式; count=<區(qū)塊數(shù)>:僅讀取指定的區(qū)塊數(shù); ibs=<字節(jié)數(shù)>:每次讀取的字節(jié)數(shù); obs=<字節(jié)數(shù)>:每次輸出的字節(jié)數(shù); of=<文件>:輸出到文件; seek=<區(qū)塊數(shù)>:一開(kāi)始輸出時(shí),跳過(guò)指定的區(qū)塊數(shù); skip=<區(qū)塊數(shù)>:一開(kāi)始讀取時(shí),跳過(guò)指定的區(qū)塊數(shù); --help:幫助; --version:顯示版本信息。
實(shí)例
> dd if=/dev/zero of=sun.txt bs=1M count=1 1+0 records in 1+0 records out 1048576 bytes (1.0 MB) copied, 0.006107 seconds, 172 MB/s [root@localhost text] 1.1M sun.txt
該命令創(chuàng)建了一個(gè) 1M 大小的文件 sun.txt,其中參數(shù)解釋?zhuān)?/span>
if?代表輸入文件。如果不指定 if,默認(rèn)就會(huì)從 stdin 中讀取輸入。
of?代表輸出文件。如果不指定 of,默認(rèn)就會(huì)將 stdout 作為默認(rèn)輸出。
bs?代表字節(jié)為單位的塊大小。
count?代表被復(fù)制的塊數(shù)。
/dev/zero?是一個(gè)字符設(shè)備,會(huì)不斷返回 0 值字節(jié)(?)。
塊大小可以使用的計(jì)量單位表
單元大小 | 代碼 |
字節(jié)(1B) | c |
字節(jié)(2B) | w |
塊(512B) | b |
千字節(jié)(1024B) | k |
兆字節(jié)(1024KB) | M |
吉字節(jié)(1024MB) | G |
以上命令可以看出 dd 命令來(lái)測(cè)試內(nèi)存操作速度:
> 1048576 bytes (1.0 MB) copied, 0.006107 seconds, 172 MB/s
生成隨機(jī)字符串
我們甚至可以使用 /dev/urandom 設(shè)備配合 dd 命令 來(lái)獲取隨機(jī)字符串
> dd if=/dev/urandom bs=1 count=15|base64 -w 0 15+0 records in 15+0 records out 15 bytes (15 B) copied, 0.000111993 s, 134 kB/s wFRAnlkXeBXmWs1MyGEs
常用案例匯總
1.將本地的/dev/hdb整盤(pán)備份到/dev/hdd #dd if=/dev/hdb of=/dev/hdd 2.將/dev/hdb全盤(pán)數(shù)據(jù)備份到指定路徑的image文件 #dd if=/dev/hdb of=/root/image 3.將備份文件恢復(fù)到指定盤(pán) #dd if=/root/image of=/dev/hdb 4.備份/dev/hdb全盤(pán)數(shù)據(jù),并利用gzip工具進(jìn)行壓縮,保存到指定路徑 #dd if=/dev/hdb | gzip > /root/image.gz 5.將壓縮的備份文件恢復(fù)到指定盤(pán) #gzip -dc /root/image.gz | dd of=/dev/hdb 6.備份與恢復(fù)MBR 備份磁盤(pán)開(kāi)始的512個(gè)字節(jié)大小的MBR信息到指定文件: #dd if=/dev/hda of=/root/image count=1 bs=512 count=1指僅拷貝一個(gè)塊;bs=512指塊大小為512個(gè)字節(jié)。 恢復(fù): #dd if=/root/image of=/dev/had 將備份的MBR信息寫(xiě)到磁盤(pán)開(kāi)始部分 7.備份軟盤(pán) #dd if=/dev/fd0 of=disk.img count=1 bs=1440k (即塊大小為1.44M) 8.拷貝內(nèi)存內(nèi)容到硬盤(pán) #dd if=/dev/mem of=/root/mem.bin bs=1024 (指定塊大小為1k) 9.拷貝光盤(pán)內(nèi)容到指定文件夾,并保存為cd.iso文件 #dd if=/dev/cdrom(hdc) of=/root/cd.iso 10.增加swap分區(qū)文件大小 第一步:創(chuàng)建一個(gè)大小為256M的文件: #dd if=/dev/zero of=/swapfile bs=1024 count=262144 第二步:把這個(gè)文件變成swap文件: #mkswap /swapfile 第三步:?jiǎn)⒂眠@個(gè)swap文件: #swapon /swapfile 第四步:編輯/etc/fstab文件,使在每次開(kāi)機(jī)時(shí)自動(dòng)加載swap文件: /swapfile swap swap default 0 0 11.銷(xiāo)毀磁盤(pán)數(shù)據(jù) #dd if=/dev/urandom of=/dev/hda1 注意:利用隨機(jī)的數(shù)據(jù)填充硬盤(pán),在某些必要的場(chǎng)合可以用來(lái)銷(xiāo)毀數(shù)據(jù)。 12.測(cè)試硬盤(pán)的讀寫(xiě)速度 #dd if=/dev/zero bs=1024 count=1000000 of=/root/1Gb.file #dd if=/root/1Gb.file bs=64k | dd of=/dev/null 通過(guò)以上兩個(gè)命令輸出的命令執(zhí)行時(shí)間,可以計(jì)算出硬盤(pán)的讀、寫(xiě)速度。 13.確定硬盤(pán)的最佳塊大?。?#dd if=/dev/zero bs=1024 count=1000000 of=/root/1Gb.file #dd if=/dev/zero bs=2048 count=500000 of=/root/1Gb.file #dd if=/dev/zero bs=4096 count=250000 of=/root/1Gb.file #dd if=/dev/zero bs=8192 count=125000 of=/root/1Gb.file 通過(guò)比較以上命令輸出中所顯示的命令執(zhí)行時(shí)間,即可確定系統(tǒng)最佳的塊大小。 14.修復(fù)硬盤(pán): #dd if=/dev/sda of=/dev/sda 或dd if=/dev/hda of=/dev/hda 當(dāng)硬盤(pán)較長(zhǎng)時(shí)間(一年以上)放置不使用后,磁盤(pán)上會(huì)產(chǎn)生magnetic flux point,當(dāng)磁頭讀到這些區(qū)域時(shí)會(huì)遇到困難,并可能導(dǎo)致I/O錯(cuò)誤。當(dāng)這種情況影響到硬盤(pán)的第一個(gè)扇區(qū)時(shí),可能導(dǎo)致硬盤(pán)報(bào)廢。上邊的命令有可能使這些數(shù) 據(jù)起死回生。并且這個(gè)過(guò)程是安全、高效的。 15.利用netcat遠(yuǎn)程備份 #dd if=/dev/hda bs=16065b | netcat < targethost-IP > 1234 在源主機(jī)上執(zhí)行此命令備份/dev/hda #netcat -l -p 1234 | dd of=/dev/hdc bs=16065b 在目的主機(jī)上執(zhí)行此命令來(lái)接收數(shù)據(jù)并寫(xiě)入/dev/hdc #netcat -l -p 1234 | bzip2 > partition.img #netcat -l -p 1234 | gzip > partition.img 以上兩條指令是目的主機(jī)指令的變化分別采用bzip2、gzip對(duì)數(shù)據(jù)進(jìn)行壓縮,并將備份文件保存在當(dāng)前目錄。 將一個(gè)很大的視頻文件中的第i個(gè)字節(jié)的值改成0x41(也就是大寫(xiě)字母A的ASCII值) echo A | dd of=bigfile seek=$i bs=1 count=1 conv=notrunc
腳本
編寫(xiě)的shell腳本如下:
#!/bin/bash #模擬cpu,IO,內(nèi)存使用 #ver 1.0 #每天凌晨1:00執(zhí)行 #持續(xù)20分鐘 #記錄執(zhí)行日志 script_log="script_log.log" mem_info=$(free -m | grep Mem) used_mem=$(echo $mem_info | awk '{print $3}') total_mem=$(echo $mem_info | awk '{print $2}') # 計(jì)算內(nèi)存利用率 mem_util=$(echo "scale=2; $used_mem / $total_mem * 100" | bc) #內(nèi)存利用率取整數(shù) mem_utilization=$(printf "%.0f" "$mem_util") if [ ! -f "$script_log" ]; then touch "$script_log" fi # 模擬內(nèi)存占用 function simulate_memory() { #安裝stress壓測(cè)工具 INSTALLED=$(rpm -qa | grep stress) if [ $? -eq 0 ];then echo "$(date +%Y-%m-%d-%H:%M) 已經(jīng)安裝了stress" >> $script_log else yum install stress -y sleep 5 fi #執(zhí)行內(nèi)存 if [ $mem_utilization -lt 80 ];then stress --io 1 --vm 1 --vm-bytes "$(awk '/MemFree/{printf "%d ", 0.7*$2}' /proc/meminfo)k" --vm-keep & echo "$(date +%Y-%m-%d-%H:%M) 執(zhí)行內(nèi)存壓測(cè)命令 " >> $script_log fi } #模擬file占用 function simulate_file() { #判斷第二硬盤(pán)是否存在 second_disk=$(lsblk | grep -w "vd[b-z]") if [ $? -eq 0 ];then echo "$(date +%Y-%m-%d-%H:%M) 第二塊硬盤(pán)存在" >> $script_log if [ -d "/data" ];then if [ -d "/data/second_disk_data_tmp" ];then echo "$(date +%Y-%m-%d-%H:%M) 臨時(shí)目錄/data/second_disk_data_tmp已存在" >> $script_log else mkdir -p /data/second_disk_data_tmp echo "$(date +%Y-%m-%d-%H:%M) 臨時(shí)目錄/data/second_disk_data_tmp不存在,已創(chuàng)建" >> $script_log fi fi else echo "$(date +%Y-%m-%d-%H:%M) 第二塊硬盤(pán)不存在" >> $script_log fi #執(zhí)行前先刪除臨時(shí)文件 if [ -f "/data/second_disk_data_tmp/data_tmp.txt" ];then rm -f /data/second_disk_data_tmp/data_tmp.txt echo "$(date +%Y-%m-%d-%H:%M) /data/second_disk_data_tmp/data_tmp.txt 文件存在,已刪除" else touch /data/second_disk_data_tmp/data_tmp.txt fi #第二塊盤(pán)磁盤(pán)利用率 second_disk_usage=$(df -h | egrep "vd[b-z]" | awk '{ print $5 }' | tr -d "%") #需要占用的磁盤(pán)空間大小 file_size=$(df -h | egrep "vd[b-z]" | awk '{ print $4*0.8 }' | tr -d "G"|bc) #去整數(shù) size_file=$(printf "%.0f" "$file_size") #計(jì)算需要生成的文件大小 #如果第二盤(pán)的空間小于80 if [ $second_disk_usage -lt 80 ];then #dd命令生成文件 time1=$(date +%s) echo "$(date +%Y-%m-%d-%H:%M) 開(kāi)始執(zhí)行生成臨時(shí)文件" >> $script_log dd if=/dev/zero of=/data/second_disk_data_tmp/data_tmp.txt bs=1G count=$size_file time2=$(date +%s) time3=$((time2-time1)) echo "$(date +%Y-%m-%d-%H:%M) 生成了臨時(shí)文件,路徑為/data/second_disk_data_tmp/data_tmp.txt,花費(fèi)時(shí)間$time3 秒" >> $script_log if [ $second_disk_usage -ge 80 ];then if [ -f "/data/second_disk_data_tmp/data_tmp.txt" ];then rm -rf /data/second_disk_data_tmp/data_tmp.txt echo "$(date +%Y-%m-%d-%H:%M) 第二盤(pán)硬盤(pán)利用率大于80%,已刪除臨時(shí)文件" >> $script_log fi fi fi } start_time=$(date +%s) simulate_memory simulate_file start_time=$(date +%s) #此刻的內(nèi)存利用率 while true;do #計(jì)算此時(shí)內(nèi)存利用率 mem_info_now=$(free -m | grep Mem) used_mem_now=$(echo $mem_info_now | awk '{print $3}') total_mem_now=$(echo $mem_info_now | awk '{print $2}') # 計(jì)算內(nèi)存利用率 mem_util_now=$(echo "scale=2; $used_mem_now / $total_mem_now * 100" | bc) mem_utilization_now=$(printf "%.0f" "$mem_util_now") current_time=$(date +%s) elapsed_time=$((current_time - start_time)) if [ $mem_utilization_now -ge 80 ];then for i in $(ps aux | grep "stress --io" | awk '{ print $2}');do kill -9 $i;done echo "$(date +%Y-%m-%d-%H:%M) 內(nèi)存利用率大于80%停止stress命令 " >> $script_log sleep 10 simulate_memory sleep 10 fi if [ $elapsed_time -ge 1200 ]; then break fi sleep 10 done for i in $(ps aux | grep "stress --io" | awk '{ print $2}');do kill -9 $i;done echo "$(date +%Y-%m-%d-%H:%M) 結(jié)束內(nèi)存壓測(cè)" >> $script_log end_time=$(date +%s) cost_time=$((end_time - start_time)) echo "$(date +%Y-%m-%d-%H:%M) 程序已經(jīng)運(yùn)行:$cost_time 秒 并結(jié)束" >> $script_log exit 0
?
?
執(zhí)行效果
cpu
內(nèi)存
IO
相關(guān)問(wèn)題
1 dd命令執(zhí)行時(shí)間過(guò)長(zhǎng),持續(xù)將近十幾分鐘
2 dd執(zhí)行過(guò)程中磁盤(pán)IO基本上被占滿(mǎn)
審核編輯:黃飛
?
評(píng)論
查看更多