Webサーバのみならず、サーバ運用において「どのプロセスがCPU/メモリを喰っているか」というボトルネックの把握は重要です。

それを把握するためのスクリプトのご紹介です。

なぜボトルネックの把握が重要なのか

以下の3点が主な理由です:

  1. リソースの最適化と安定運用
     高負荷プロセスを特定することで、不要な消費を抑え、他のサービスへの影響を防げます。
  2. 障害予防と早期対応
     異常なリソース使用は障害の前兆であることが多く、早期発見によりダウンタイムを回避できます。
  3. 攻撃予兆への対応
     DDoS/執拗な攻撃などはリソース寮にダイレクトに現れます。

把握するためのシェルスクリプト

といっても、topwコマンドなどでは煩雑な情報が多いため、シンプルに

  1. CPUを多く使っているプロセス
  2. メモリを多く使っているプロセス

に絞り込みを行います。というのも、プロセスの暴走は先に示したとおり、CPU/メモリを多く使うからです。

それをより分かりやすく視覚化するスクリプト例が以下の通り。

top-procs.sh等の名前で、任意の場所に作成します。

#!/bin/bash

#================================================================
# System Resource Monitor (Refined by Riza)
#================================================================

# --- Colors ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color

# --- Defaults ---
TOP_N=5
MODE="all" # all, cpu, mem

# --- Usage ---
usage() {
    echo -e "${CYAN}Usage: $(basename "$0") [-c] [-m] [-n NUM] [-h]${NC}"
    echo "  -c      : CPU使用率順で表示"
    echo "  -m      : メモリ使用率順で表示"
    echo "  -n NUM  : 表示する行数を指定 (Default: ${TOP_N})"
    echo "  -h      : ヘルプ表示"
    exit 0
}

# --- Argument Parsing (getopts is standard) ---
while getopts "cmn:h" opt; do
  case $opt in
    c) MODE="cpu" ;;
    m) MODE="mem" ;;
    n) TOP_N="$OPTARG" ;;
    h) usage ;;
    \?) usage ;;
  esac
done

# --- Core Function ---
# Arguments: 
# 1: Sort Key (e.g., -pcpu or -pmem)
# 2: Title
# 3: Format String for ps
show_top() {
    local sort_key="$1"
    local title="$2"

    echo -e "\n${BOLD}${CYAN}--- ${title} (Top ${TOP_N}) ---${NC}"

    # Header format specifically tailored for clarity
    printf "${YELLOW}%-6s %-6s %-8s %-20s %s${NC}\n" "%CPU" "%MEM" "PID" "USER" "COMMAND"
    echo "---------------------------------------------------------"

    # ps command explanation:
    # -e : Select all processes
    # -o : User-defined format
    # --sort : Internal sorting (descending with -)
    # head : Limit output

    ps -e -o pcpu,pmem,pid,user,args --sort="${sort_key}" | \
    head -n "$((TOP_N + 1))" | tail -n "$TOP_N" | \
    awk '{
        # Highlighting logic
        cpu=$1; mem=$2;

        # Colorize CPU if > 50% or MEM > 50% (Arbitrary threshold for visual aid)
        color="'"${NC}"'";
        if (cpu > 50.0 || mem > 50.0) color="'"${RED}"'";
        else if (cpu > 10.0 || mem > 10.0) color="'"${GREEN}"'";

        # Reconstruct the line with printf for alignment
        # $1=CPU, $2=MEM, $3=PID, $4=USER, $5...=COMMAND

        # Capture the full command (args) which starts from $5
        cmd=""; for(i=5;i<=NF;i++) cmd=cmd" "$i;

        printf "%s%-6s %-6s %-8s %-20s %s%s\n", color, $1, $2, $3, $4, cmd, "'"${NC}"'"
    }'
}

# --- Main Logic ---

case $MODE in
    cpu)
        show_top "-pcpu" "CPU Consumers"
        ;;
    mem)
        show_top "-pmem" "Memory Consumers"
        ;;
    all)
        show_top "-pcpu" "CPU Consumers"
        show_top "-pmem" "Memory Consumers"
        ;;
esac

echo ""

以前の改良点

  1. ps --sort の活用: これが最大の改良点よです。ps aux | sort -k ... はデータをテキストとして処理するから遅いのですが、ps --sort=-pcpuはカーネルから情報を取得する段階でソートします。
  2. -n オプションの追加: ./top-procs.sh -n 10 と打てばトップ10が見られるようにしています。
  3. しきい値による色付け: awk の中で、CPUやメモリを激しく消費しているプロセス(例えば50%以上)を赤色で表示するようにしています。
./top-procs.sh

を実行することで、

--- CPU使用率 (上位 5 プロセス) ---
 %CPU    PID  COMMAND
-----------------------------------------
 52.10%  12345  ruby_app_server: /var/www/webapp1 (production)
  9.40%   1086  /usr/sbin/database_server [...]
  3.80%  42162  /usr/sbin/web_server -k start
  1.50%  42161  /usr/sbin/web_server -k start
  0.90%   7978  nodejs_process /path/to/nodejs_app/server.js

--- メモリ使用率 (上位 5 プロセス) ---
 %MEM    PID  COMMAND
-----------------------------------------
 13.10%   1984  /usr/bin/java -Xms256m -Xmx256m [...] search_engine -Des.path.home=/usr/share/search_engine [...]
 10.00%   1086  /usr/sbin/database_server [...]
  7.50%  12345  ruby_app_server: /var/www/webapp1 (production)
  3.90%  78630  ruby_app_server: /var/www/webapp2 (production)
  3.80%  76583  ruby_app_server: /var/www/webapp3 (production)

が出てきます。

この例では、rubyアプリが圧倒的にCPUを消費し、ElasticSearchがメモリを食っているというのが分かります。

そして、

  • -a / 引数無し : CPUとメモリの両方を表示
  • -c : CPU情報のみを表示
  • -m : メモリ情報のみを表示
  • -h : これら引数やスクリプトの内容を表示

と、目的に合わせた柔軟な表示も可能にしています。

ついでにコマンド化

こういった障害発生時のボトルネック判定時、いちいちスクリプトの場所を探すという悠長なことはできません。

なので、余裕がある(つまりこのスクリプトを作成した直後です)状況で、

sudo ln -sf /path/to/script/top-procs.sh /usr/local/bin/top-procs

として、どこからでもコマンドを呼び出せるようにします。(スクリプトの場所は自分がこれを保存した絶対パスを指定してください)

which top-procs

/usr/local/bin/top-procs

と表示されればコマンド化は完了。こうすることにより、どのユーザーでもこのコマンド一発で上記のボトルネック判定が可能になります。