web-dev-qa-db-ja.com

CPU /メモリ使用率が高くなりすぎたときにプロセスを自動的に強制終了するBashスクリプト

CPUやメモリの使用率が80%に達した場合にプロセスを強制終了するスクリプトを作成しました。これが発生すると、強制終了されたプロセスのリストが作成されます。それを改善するために私は何ができますか?

while [ 1 ];
do 
echo
echo checking for run-away process ...

CPU_USAGE=$(uptime | cut -d"," -f4 | cut -d":" -f2 | cut -d" " -f2 | sed -e "s/\.//g")
CPU_USAGE_THRESHOLD=800
PROCESS=$(ps aux r)
TOPPROCESS=$(ps -eo pid -eo pcpu -eo command | sort -k 2 -r | grep -v PID | head -n 1)

if [ $CPU_USAGE -gt $CPU_USAGE_THRESHOLD] ; then
  kill -9 $(ps -eo pid | sort -k 1 -r | grep -v PID | head -n 1) #original
  kill -9 $(ps -eo pcpu | sort -k 1 -r | grep -v %CPU | head -n 1)
  kill -9 $TOPPROCESS
  echo system overloading!
  echo Top-most process killed $TOPPROCESS
      echo CPU USAGE is at $CPU_LOAD

else
    fi
    exit 0
    sleep 1;
    done
11
Ketan Patel

私は推測解決したい問題は、ボックスで実行中のプロセスが時々誤動作し、永久にコアをペギングしていることです。

最初にしたいことは、狂ったプログラムを修正することです。それは断然最良の解決策です。私はそれが不可能であると仮定します。そうしないと、箱が修理されるまで箱を動かし続けるためのクイッククルージが必要です。

少なくとも、問題のある1つのプログラムのみにヒットするようにスクリプトを制限したいとします。権限によってこのようにスクリプトが制限された場合に最適です(たとえば、スクリプトはユーザーXとして実行され、Xとして実行される他の唯一のものはプログラムです)。

_ulimit -t_のようなものを使用して、プログラムが使用できる合計CPU時間を制限することをお勧めします。同様に、すべてのメモリを消費する場合は、_ulimit -v_を確認します。カーネルはこれらの制限を強制します。詳細については、bashマンページ(シェル組み込み)とsetrlimit(2)マンページを参照してください。

問題がamokを実行しているプロセスではなく、実行しているプロセスが多すぎる場合は、何らかの形式のロックを実装して、X以上が実行されないようにします(または、これは慣れ親しんでいるはずです_ulimit -u_)。また、これらのプロセスのスケジューラの優先度を変更することを検討することもできます(Niceまたはreniceを使用)。または、より徹底的に、_sched_setscheduler_を使用してポリシーを_SCHED_IDLE_に変更することもできます。 。

さらに制御が必要な場合は、制御グループ(cgroups)を見てください。実行しているカーネルに応じて、プロセスのグループ全体が一緒に消費するCPU時間、メモリ、I/Oなどの量を実際に制限できます。コントロールグループは非常に柔軟です。壊れやすいクルージがなくても、あなたがしようとしていることは何でもできるでしょう。 Arch Linux Wikiには cgroupsの紹介 があり、LWNには Neil Brownのcgroupsシリーズ があるので一読に値します。

11
derobert

問題:

  • 数値フィールドを並べ替えるときは、-nオプションを使用することをお勧めします:sort -nrk 2。そうしないと、%CPUの値が5.0の行は、値が12.0の行よりも高くなります。
  • psの実装によっては、--no-headersオプションを使用してgrep -vを削除することもできます。これにより、PIDを含むコマンドを破棄できなくなります。
  • echo CPU USAGE is at $CPU_LOADではなく、echo CPU USAGE is at $CPU_USAGEを意味しているようです。
  • デバッグ中に挿入したexit 0を削除するのを忘れたようです(?)。

スタイル:

  • CPU_USAGE_THRESHOLD=800行をファイルの先頭に移動することをお勧めします。これは、最も情報量が多く、スクリプトが安定した後でも変更される可能性が最も高いためです。
  • -eオプションを繰り返しています。ps -eo pid -eo pcpu -eo commandps -eo pid -o pcpu -o commandと同じです(ps -eo pid,pcpu,commandも同様)。
  • 空のelse句があります。これは常に処理する必要があるかのように見えますが、何らかの理由でそうではありませんでした。
3
Yurim

ほとんどのCPU /メモリを使用しているプロセスを強制終了すると、問題が発生します。マシンの現在の状態を確認してください(ここでは現在、firefox、systemd(ini​​t)、Xorg、gnome-terminal、カーネルスレッドのセット、xemacs、どれも必須ではありません)。 LinuxのOOM-killerを調整する方法を見てください(例 here )。

また、「プロセスが使用するメモリ」は曖昧な概念であることに注意してください。共有ライブラリ、実行可能ファイル、さらにはデータ領域の一部さえ存在するからです。各ユーザーに使用済みスペースのごく一部を請求することでいくつかの数字を思い付くことができますが、それを追加しても実際には「使用メモリ」は得られません(プロセスがなくなると「メモリ解放」が少なくなります)。後ろに)。

2
vonbrand

CPU使用率がYY秒間XX%を超える場合、またはZZ秒を超えて実行されているプロセスを強制終了する場合、配列にリストされた一部のプロセスを強制終了するスクリプト kill-process を作成しました。

  • ファイルの先頭にXX、YY、ZZを設定できます。
  • チェックプロセスには、psまたはtopを使用できます。
  • チェックするが殺さないために、予行演習モードもあります。
  • 最後に、いくつかのプロセスが強制終了された場合、スクリプトは電子メールを送信します。

注:これがGithub上の私のリポジトリです: https://github.com/padosoft/kill-process

これがスクリーンショットです:

ss#1

参考文献

スクリプトの重要な部分(トップコマンドのコードの要約):

#!/usr/bin/env bash

#max cpu % load
MAX_CPU=90
#max execution time for CPU percentage > MAX_CPU (in seconds 7200s=2h)
MAX_SEC=1800
#sort by cpu
SORTBY=9

#define a processes command name to check
declare -a KILLLIST
KILLLIST=("/usr/sbin/Apache2" "/usr/bin/php5-cgi")

#iterate for each process to check in list
for PROCESS_TOCHECK in ${KILLLIST[*]}
do

    #retrive pid with top command order by SORTBY
    PID=$(top -bcSH -n 1 | grep $PROCESS_TOCHECK | sort -k $SORTBY -r | head -n 1 | awk '{print $1}')

    CPU=$(top -p $PID -bcSH -n 1 | grep $PROCESS_TOCHECK | sort -k $SORTBY -r | head -n 1 | awk '{print $9}')
    TIME_STR=$(top -p $PID -bcSH -n 1 | grep $PROCESS_TOCHECK | sort -k $SORTBY -r | head -n 1 | awk '{print $11}')

    # Decode the top CPU time format [dd-]hh:mm.ss.
    TIME_SEC=0
    IFS="-:" read c1 c2 c3 c4 <<< "$TIME_STR"

    #with top command time format is hh:mm.ss, so truncare seconds in c2
    c2=${c2%%.*}

    if [ -n "$c4" ]
    then
      TIME_SEC=$((10#$c4+60*(10#$c3+60*(10#$c2+24*10#$c1))))
    Elif [ -n "$c3" ]
    then
      if [ "$CMD" = "ps" ]; then
        TIME_SEC=$((10#$c3+60*(10#$c2+60*10#$c1)))
      else
        TIME_SEC=$(((10#$c3*24)*60*60)+60*(10#$c2+60*10#$c1))             
      fi   
    else
      if [ "$CMD" = "ps" ]; then
        TIME_SEC=$((10#0+(10#$c2+60*10#$c1)))
      else
        TIME_SEC=$((10#0+60*(10#$c2+60*10#$c1)))
      fi
    fi

    #check if need to kill process
    if [ $CPU -gt $MAX_CPU ] && [ $TIME_SEC -gt $MAX_SEC ]; then
        kill -15 $PID
    fi

done
bash killprocess.sh [dry|kill|--help] [top|ps] [cpu|time]
1