サーバ運用のためのログ記録

一般的なサーバ管理者さんは、こんなことをせずにもっとスマートに対応しているんだろうか…。という話。

#概要

サーバは普段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で作成したログ閲覧ツールで日々確認しています。

#あとがき

…こんな仕組みでいつでもサーバの過去の状態を調べられるようにしていますが、
「いやいや、そんなことしなくても○○を使えば簡単なのに」
などあれば教えていただけると、とってもありがたいです。（切実）

記録している内容は、自分がサーバについて何かしら調査する場合に調べること全般です。
「こういう調査も必要だから、こんな情報も記録しておくべきでは」
というツッコミは歓迎です。