一般的なサーバ管理者さんは、こんなことをせずにもっとスマートに対応しているんだろうか…。という話。
#概要
サーバは普段ZabbixやCloudWatch(AWS)で監視していますが、調査の際はより詳細な情報を知りたいことがあります。
例えば「昨日の深夜2時に負荷が高かったけど、その当時の ps
の結果をみたい」など。
Zabbixやsysstatをインストールしておけば過去の状況は調べられますが、より詳細に知るためにシェルスクリプトで各コマンドの実行結果を記録しています。
基本的な方針は
*/5 * * * * root uptime > /var/log/command/uptime/`date +'\%Y\%m\%d'`/`date +'\%H\%M'`.log
こんなCronを登録しておくと5分ごとに uptime
の結果が /var/log/command/uptime/
内に保存されるので、あとから cat
で参照したりプログラムでグラフ化したりする…という流れです。
直接コマンドを1つ1つ登録すると管理しづらいので、実際はシェルスクリプトを作成しています。(後述)
ログの閲覧は専用のPHPプログラムを作っていて、JavaScriptでZabbix風にグラフ表示しています。
具体的な設定内容を以下に。
#ログ保存用ディレクトリを作成
まずは以下のようにして、ログ保存用ディレクトリを作成しておきます。rootで作業していますが、sudo
での作業ももちろん有効です。
PHPでログを閲覧するためにapache権限で読み込めるようにしていますが、不要なら権限も不要です。
# cd /var/log
# mkdir command
# chown :apache command
# chmod g+s command
# cd command
# mkdir uptime
# mkdir mpstat_10_5
# mkdir free
# mkdir vmstat_10_5
# mkdir df
# mkdir df_i
# mkdir cat_proc_net_dev
# mkdir netstat_s_connection_numerics
# mkdir ps_aux_stat_r_count
# mkdir ps_aux_sort_-pcpu
# mkdir netstat_an_80_443_count
# mkdir mysql_show_status
# mkdir mysql_strage
# mkdir mysql_show_processlist
#シェルスクリプトを作成
作成したディレクトリに、uptime
や mpstat
などの結果を記録していきます。記録するためのスクリプトを作成します。
# cd /root
# vi monitor_record.sh
# chmod 744 monitor_record.sh
monitor_record.sh
の内容は以下です。コマンドの実行結果をひたすらファイルに保存しているだけです。
#!/bin/bash
SAVE_PATH=/var/log/command
MYSQL_HOST=localhost
MYSQL_USER=root
MYSQL_PASSWORD=1234
now_date=`date +'%Y%m%d'`
now_time=`date +'%H%M'`
#Load Average
dir_name=${SAVE_PATH}/uptime/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
uptime > $file_name
#CUP Utilization
dir_name=${SAVE_PATH}/mpstat_10_5/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
mpstat 10 5 > $file_name
#Memory
dir_name=${SAVE_PATH}/free/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
free > $file_name
#Memory
dir_name=${SAVE_PATH}/vmstat_10_5/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
vmstat 10 5 > $file_name
#Disk Space
dir_name=${SAVE_PATH}/df/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
df > $file_name
#Disk inode Space
dir_name=${SAVE_PATH}/df_i/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
df -i > $file_name
#Traffic
dir_name=${SAVE_PATH}/cat_proc_net_dev/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
cat /proc/net/dev > $file_name
#Number of Users Connected
dir_name=${SAVE_PATH}/netstat_s_connection_numerics/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
netstat -s | grep 'connections established' | awk '{print $1}' > $file_name
#Number of Running Process
dir_name=${SAVE_PATH}/ps_aux_stat_r_count/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
ps aux | awk '{print $8}' | grep '^[DR]' -c > $file_name
#Process
dir_name=${SAVE_PATH}/ps_aux_sort_-pcpu/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
ps aux --sort=-pcpu > $file_name
#Apache Connected
dir_name=${SAVE_PATH}/netstat_an_80_443_count/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
netstat -an | egrep ':(80|443) ' | wc -l > $file_name
#MySQL Status
dir_name=${SAVE_PATH}/mysql_show_status/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
/usr/bin/mysql -s -N -h $MYSQL_HOST -u $MYSQL_USER -p"${MYSQL_PASSWORD}" -e "SHOW STATUS;" > $file_name
#MySQL Strage
dir_name=${SAVE_PATH}/mysql_strage/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
/usr/bin/mysql -s -N -h $MYSQL_HOST -u $MYSQL_USER -p"${MYSQL_PASSWORD}" -e "SELECT table_schema AS DB, SUM(data_length) / 1024 / 1024 AS MB FROM information_schema.tables GROUP BY table_schema ORDER BY SUM(data_length + index_length) DESC;" > $file_name
#MySQL Process
dir_name=${SAVE_PATH}/mysql_show_processlist/${now_date}/
file_name=${dir_name}${now_time}.log
if [ ! -e $dir_name ]; then
mkdir $dir_name
fi
/usr/bin/mysql -s -N -h $MYSQL_HOST -u $MYSQL_USER -p"${MYSQL_PASSWORD}" -e "SHOW PROCESSLIST;" > $file_name
スクリプトの冒頭にある
MYSQL_HOST=localhost
MYSQL_USER=root
MYSQL_PASSWORD=1234
この部分は、MySQLの接続先・ユーザ名・パスワードを設定します。
自分が管理しているサーバはすべてにMySQLがインストールされていますが、MySQLを使わないサーバならログ記録処理も含めて不要です。
続いて、ログ削除用のスクリプトも作成します。
# vi monitor_remove.sh
# chmod 744 monitor_remove.sh
monitor_remove.sh
の内容は以下です。10日より古いファイルを、ディレクトリごと削除します。
#!/bin/bash
SAVE_PATH=/var/log/command
target_date=`date +'%Y%m%d' --date '10 days ago'`
#Remove
rm -rf ${SAVE_PATH}/cat_proc_net_dev/${target_date}/
rm -rf ${SAVE_PATH}/df/${target_date}/
rm -rf ${SAVE_PATH}/df_i/${target_date}/
rm -rf ${SAVE_PATH}/free/${target_date}/
rm -rf ${SAVE_PATH}/mpstat_10_5/${target_date}/
rm -rf ${SAVE_PATH}/mysql_show_processlist/${target_date}/
rm -rf ${SAVE_PATH}/mysql_show_status/${target_date}/
rm -rf ${SAVE_PATH}/mysql_strage/${target_date}/
rm -rf ${SAVE_PATH}/netstat_an_80_443_count/${target_date}/
rm -rf ${SAVE_PATH}/netstat_s_connection_numerics/${target_date}/
rm -rf ${SAVE_PATH}/ps_aux_sort_-pcpu/${target_date}/
rm -rf ${SAVE_PATH}/ps_aux_stat_r_count/${target_date}/
rm -rf ${SAVE_PATH}/uptime/${target_date}/
rm -rf ${SAVE_PATH}/vmstat_10_5/${target_date}/
#Cronに登録
作成したスクリプトをCronに登録し、5分ごとにログを記録するようにします。
ファイルの削除は1日1回行うようにします。
# vi /etc/crontab
*/5 * * * * root /root/monitor_record.sh
00 00 * * * root /root/monitor_remove.sh
コマンド実行時に異常があってもメール通知したくない場合、以下のように >> /dev/null 2>&1
を追加します。
*/5 * * * * root /root/monitor_record.sh >> /dev/null 2>&1
00 00 * * * root /root/monitor_remove.sh >> /dev/null 2>&1
以上の手順で記録されたログを、PHPで作成したログ閲覧ツールで日々確認しています。
#あとがき
…こんな仕組みでいつでもサーバの過去の状態を調べられるようにしていますが、
「いやいや、そんなことしなくても○○を使えば簡単なのに」
などあれば教えていただけると、とってもありがたいです。(切実)
記録している内容は、自分がサーバについて何かしら調査する場合に調べること全般です。
「こういう調査も必要だから、こんな情報も記録しておくべきでは」
というツッコミは歓迎です。