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