SQL Serverのslowqueryを監視する

前回覚えたSQL Serverの条件分岐を使って、nagios/icinga用のSlowQuery監視スクリプトを書いてみた。

前提として、こちらを参考に、SlowQuery取得の設定が必要。

NRPE経由で監視する場合は、MAX_PACKETBUFFER_LENGTH等の値の変更をしたら幸せになれます。

[bash]
#!/bin/bash
CMDNAME=`basename $0`
while getopts h:u:p:c:w: OPT
do
case $OPT in
"h" ) FLG_H="TRUE" ; VALUE_H="$OPTARG" ;;
"u" ) FLG_U="TRUE" ; VALUE_U="$OPTARG" ;;
"p" ) FLG_P="TRUE" ; VALUE_P="$OPTARG" ;;
"w" ) FLG_W="TRUE" ; VALUE_W="$OPTARG" ;;
"c" ) FLG_C="TRUE" ; VALUE_C="$OPTARG" ;;
* ) echo "Usage: $CMDNAME [-h HOST_NAME] [-u User] [-p PASS] [-c warnning] [-c critical]" 1>&2
exit 1 ;;
esac
done
_HOST=$VALUE_H
_USER=$VALUE_U
_PASS=$VALUE_P
_WARN=$VALUE_W
_CRIT=$VALUE_C
# 何分前の以降に出たSLOW LOGを対象とする
_TIMESTAMP=’00:05′
#echo "$_HOST, $_USER, $_PASS, $_WARN, $_CRIT"
export ODBCINI=/etc/odbc.ini
export ODBCSYSINI=/etc
export FREETDSCONF=/etc/freetds/freetds.conf
# ここからT-SQL処理
TDSVER=8.0 /usr/local/freetds/bin/tsql -H $_HOST -p 1433 -U $_USER -P $_PASS << _EOF > /tmp/${_HOST}_slowlog.tmp
DECLARE @VERSION_FULL nvarchar(max);
DECLARE @VERSION int;
SET @VERSION_FULL = (SELECT convert (nvarchar(max),SERVERPROPERTY(‘productversion’)));
SET @VERSION = substring(@VERSION_FULL, 1, 2)
DECLARE @DATE VARCHAR(16);
SET @DATE = (select CONVERT(VARCHAR,GETDATE()-‘$_TIMESTAMP’,120));
IF @VERSION = 10
BEGIN
SELECT REPLACE((
SELECT
DATEADD(hour,9 , CAST(event_data as XML).value(‘(/event/@timestamp)[1]’, ‘datetime’)) AS timestamp,
DB_NAME(CAST(event_data as XML).value(‘(/event/action[@name="database_id"]/value)[1]’, ‘sysname’)) AS database_name,
CAST(event_data as XML).value(‘(/event/action[@name="sql_text"]/value)[1]’, ‘nvarchar(max)’) AS sql_text,
CAST(event_data as XML).value(‘(/event/data[@name="duration"]/value)[1]’, ‘bigint’) AS duration,
CAST(event_data as XML).value(‘(/event/data[@name="cpu"]/value)[1]’, ‘bigint’) AS cpu_time
FOR XML PATH (”)), ‘ ‘, ‘ ‘) AS RESULT
from sys.fn_xe_file_target_read_file(
‘C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\Highload_Query*.xel’,
‘C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\Highload_Query*.xem’ ,
NULL,
NULL)
where DATEADD(hour,9 , CAST(event_data as XML).value(‘(/event/@timestamp)[1]’, ‘datetime’)) >= @DATE
END
ELSE
BEGIN
IF @VERSION = 11
BEGIN
SELECT
DATEADD(hour,9 , CAST(event_data as XML).value(‘(/event/@timestamp)[1]’, ‘datetime’)) AS timestamp,
DB_NAME(CAST(event_data as XML).value(‘(/event/action[@name="database_id"]/value)[1]’, ‘sysname’)) AS database_name,
CAST(event_data as XML).value(‘(/event/data[@name="batch_text"]/value)[1]’, ‘nvarchar(max)’) AS batch_textt,
CAST(event_data as XML).value(‘(/event/data[@name="duration"]/value)[1]’, ‘bigint’) AS duration,
CAST(event_data as XML).value(‘(/event/data[@name="cpu_time"]/value)[1]’, ‘bigint’) AS cpu_time
from sys.fn_xe_file_target_read_file(
‘C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Log\Highload_Query*.xel’,
NULL,
NULL,
NULL)
where DATEADD(hour,9 , CAST(event_data as XML).value(‘(/event/@timestamp)[1]’, ‘datetime’)) >= @DATE;
END;
GO
quit
_EOF
IFS_BACKUP=$IFS
IFS=$’\n’
_TIME=(`cat /tmp/${_HOST}_slowlog.tmp | sed -n -e ‘s/.*\(.*\)<\/timestamp>.*/"\1"/p’`)
_DBNAME=(`cat /tmp/${_HOST}_slowlog.tmp | sed -n -e ‘s/.*\(.*\)<\/database_name>.*/"\1"/p’`)
_CPUTIME=(`cat /tmp/${_HOST}_slowlog.tmp |sed -n -e ‘s/.*\(.*\)<\/cpu_time>.*/"\1"/p’`)
_DURATION=(`cat /tmp/${_HOST}_slowlog.tmp | sed -n -e ‘s/.*\(.*\)<\/duration>.*/"\1"/p’`)
_QUERY=(`cat /tmp/${_HOST}_slowlog.tmp | tr ‘\n’ ‘ ‘| sed -e ‘s/<\/cpu_time>/<\/cpu_time>\n/g’| sed -n -e ‘s/.*\(.*\)<\/sql_text>.*/"\1"\n/p’`)
func_message (){
for (( i=0; $i<`echo ${#_TIME[@]}`; i++ ));
do
echo "———-"
echo "TIME: ${_TIME[$i]}"
echo "DBNAME: ${_DBNAME[$i]}"
echo "CPU_TIME: ${_CPUTIME[$i]} ms"
echo "DURATION: ${_DURATION[$i]} us"
echo "${_QUERY[$i]}"
done
IFS=$IFS_BACKUP
}
# check
if [ $_CRIT -le `echo ${#_TIME[@]}` ]
then
echo "CRIT: SlowQuery is `echo ${#_TIME[@]}` | SlowQuerys=`echo ${#_TIME[@]}`;$_WARN;$_CRIT"
func_message
exit 2
elif [ $_WARN -le `echo ${#_TIME[@]}` ]
then
echo "WARN: SlowQuery is `echo ${#_TIME[@]}` | SlowQuerys=`echo ${#_TIME[@]}`;$_WARN;$_CRIT"
func_message
exit 1
elif [ $_WARN -gt `echo ${#_TIME[@]}` ]
then
echo "OK: SlowQuery is `echo ${#_TIME[@]}` | SlowQuerys=`echo ${#_TIME[@]}`;$_WARN;$_CRIT"
exit 0
else
echo "status unknown"
exit 3
fi
[/bash]

使い方
./check_sqlserver_slowquery -h ホスト名orIPアドレス -u ユーザー名 -p パスワード -w ワーニング閾値 -c クリティカル閾値

閾値以下の場合は
OK: SlowQuery is 0 | SlowQuerys=0;3;4
とか表示される。

閾値以上の場合は
CRIT: SlowQuery is 6 | SlowQuerys=6;3;4
———-
TIME: “2016-01-08T10:42:00.427”
DBNAME: “hoge”
CPU_TIME: “3932” ms
DURATION: “4011259” us
“~~遅いクエリ~~”
———-
TIME: “2016-01-08T10:42:07.283”
DBNAME: “hoge”
CPU_TIME: “4431” ms
DURATION: “4481414” us
“~~遅いクエリ~~”

等が表示されます。

javeって何よ?

29日の18:00ぐらいから、CentOS7のVPSサーバーに繋がらなくなった。
再起動しても、5分ぐらい経つとSSHでの接続もタイムアウトになる。

プロセスを見てみると、/tmp/javeってのがCPUリソース食いまくってるのみたい。

[root@jhhk-family bin]# ps -auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
(中略)
root 513 67.8 0.0 107076 1064 ? Ssl 18:46 2:52 /tmp/jave

netstatはこんな感じ

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name Timer
cp 0 55 182.163.72.51:59867 23.228.105.6:25000 ESTABLISHED 17759/jave on (0.20/0/0)

pkillしても、すぐに上がってくるからKernel関係のプロセス?と思ったけど、ググッてもそんな情報無いし、とりあえず止めてもサービスには影響は無かったのでcrondで毎分pkill投げてる。


botとかで踏まれてる可能性もあるから、iptablesは固くした。
時間見てちゃんと調べよう。

14:27追記
以下の事をやられていたので、追加で対策。
1)/root/.ssh/の公開鍵のtimestampが変わっている
  作業用でrootログインを許可して鍵認証していたけど、authorized_keyの
timestampが昨日になっていた。
SSHのログインを禁止して、authorized_keyを削除

2)/tmp/色々のパーミッション変更
不正な実行ファイルは/tmp/javeだけど、/tmp下に関連しそうなファイルがあった。
  ———- 1 root root 73 12月 30 13:36 conf.n
———- 1 root root 5 12月 30 13:36 gates.lod
———- 1 root root 1223123 12月 30 14:32 jave
———- 1 root root 5 12月 30 13:36 moni.lod
javeと同様、単純に消しちゃうと再作成された為、全てのパーミッションを取り除く。
※ 上記の不正なファイル関連は、ファイルの存在のみ見ているみたいだったので、
パーミッション変更後にpkill /tmp/javeとすると、2度とjaveは起動しなくなった。

でも、root取られていたので他にも悪さしている箇所があるかも。
ログも信用出来なくなったので、年明けに再構築しないと、、、

  

SQL Serverのversionによって処理を分けたい場合

SQL Serverのversionによって処理を分けたい場合
もうちょっと細かいversionで分けられると思うけど、
今はSQL Server 2008 か 2012 か 2014 かぐらいで分けれれば問題ない。

[code]declare @VERSION_FULL nvarchar(max);
declare @VERSION int;
SET @VERSION_FULL = (SELECT convert (nvarchar(max),SERVERPROPERTY(‘productversion’)));
SET @VERSION = substring(@VERSION_FULL, 1, 2)
IF @VERSION = 10
— SQL SERVER 2008での処理
BEGIN
PRINT ‘2008’;
END
ELSE
BEGIN
IF @VERSION = 11
— SQL SERVER 2012での処理
PRINT ‘2012’;
ELSE
— SQL SERVER 2014での処理
PRINT ‘2014’;
END ;
GO
[/code]

redisデータベースの複製

rdbファイルの複製ではなく、db10 → db18とかに複製する方法。

[code]source_host=localhost
source_port=xxxx
source_db=10
target_host=localhost
target_port=xxxx
target_db=18
redis-cli -p $source_port -n $source_db keys \* | while read key; do echo "Copying $key"; \
redis-cli –raw -h $source_host -p $source_port -n $source_db DUMP "$key" | \
head -c -1|redis-cli -x -h $target_host -p $target_port -n $target_db RESTORE "$key" 0; done
[/code]

openstackで固定IPアドレスを振る

openstackのネットワークを管理するneutronについて
色んなサイトで「仮想ホストにDHCPでIPアドレスを割り当て、floatingIPを振って外部に公開する~」みたいな情報は見たけど、
実運用だとサーバーに固定でIPアドレスを振りたい。

10.0.0.1 ・・・ 仮想L3ゲートウェイ
10.0.0.2 ・・・ 仮想DHCP、DNS
10.0.0.3 ・・・ 仮想WEBサーバ(1)
10.0.0.4 ・・・ 仮想DBサーバ(1)
10.0.0.5 ・・・ 仮想DBサーバ(2)
10.0.0.6 ・・・ 仮想WEBサーバ(2)
とか、気持ち的に嫌なので。

仮想サーバーの/etc/sysconfig/network-scripts/ifcfg-eht0とかに直接IPアドレスを書いても、
neutronでネットワークポート(それにIPアドレスが紐づく)を管理してる為、残念ながらこのやり方では認識されない。

数時間調べて、コマンド的には以下で変更出来た。
[bash]
#DHCPで割り当てられたネットワークポートの削除
#仮想サブネット上でDHCPが無い場合は、この手順は不要
nova interface-detach 仮想ホスト名 Port ID

#ポートを作る
neutron port-create ネットワーク名 –fixed-ip ip_address=xxx.xxx.xxx.xxx

#作ったポートを、仮想ホストに割り当てる
nova interface-attach –port-id ポートID 仮想ホスト名
[/bash]

ただ、この状態だと対象の仮想サーバーに、新規にネットワークインターフェースが追加されることになるので、
/etc/udev/rules.d/70-persistent-net.rulesを修正する必要がある。

awkで”後ろからn番目”を指定する

今日知ったこと。
awkはよく使うけど、後ろからn番目を指定する方法があることを知ったのでメモ。

cat access.log
[10/Jun/2015:23:01:04 +0900] "POST /hoge?a=12345 HTTP/1.1" 200 2597 2832 "-" "Mozilla/5.0 (Linux; U; Android 2.3.7; ja-jp; SonyEricssonSO-03D Build/6.0.A.5.14)" "yyy.yyy.yyy.yyy"  "0.000"
[10/Jun/2015:23:02:04 +0900] "GET /hoge.jpg HTTP/1.1" 200 2597 2832 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12F70 Safari/600.1.4)" "xxx.xxx.xxx.xxx"  "0.000"

ここから、IPアドレスの部分”yyy.yyy.yyy.yyy”を抜きたい場合とか、

awk '{print $(NF - 2)}' access.log

とすれば良い。

ただ、CentOS6系では使えて、CentOS7系では

コマンドライン:1: (FILENAME=a.txt FNR=1) 致命的: フィールド -2 へのアクセスの試みです

とエラーが出る。
後で調べておこう。

FTPログインの手間を減らす

覚え書き。
cron等でユーザー認証があるFTPに接続を行う場合に、ユーザー認証の応答を自動化する方法。
~/.netrcを作成し、以下の内容を記載する。
[bash]
<script class="brush:bash;" type="syntaxhighlighter" title="~/.netrc"><![CDATA[
machine FTPホスト
login ユーザー名
password パスワード
[/bash]

BeckyのMIMEタイプ追加

特殊な環境かもしれないけど、需要があるかもしれないのでメモ

メールソフト「Becky」からの送信メールに、【日本語のファイル名】を添付して送信すると、
受信環境によっては添付ファイルを見る事が出来ない事象の対応。

私が遭遇したのは、Backy⇒zimbra宛のメール、且つ、zimbraをiPhoneとかのIOS端末で使った場合。

【原因】
 ①Becky!の問題
  ⇒添付ファイルの形式を示す「Content Type」ヘッダーが「application/octet-stream」(不明確なバイナリデータとして認識)
   として捉えられてしまう。

  ⇒添付ファイルを示すヘッダーは「name」だけど、ISO-2022-JPでエンコードされている為、「テスト.pptx」が
   「”=?ISO-2022-JP?B?GyRCJUYlOSVIJUchPCU/GyhCLnBwdHg=?=”」
   と解釈され、拡張子での判別も出来ない。

【対策】
 取りあえず、becky側のMIMEタイプを追加すると問題が解決する。
 ※セキュリティ的には、あまり宜しくないので自己責任で。

  レジストリキーに、以下を追加
  【ワード2003/2007】
  ・application/msword
  ・application/vnd.openxmlformats-officedocument.wordprocessingml.document
  【エクセル2003/2007】
  ・application/vnd.ms-excel
  ・application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  【パワーポイント2003/2007】
  ・application/vnd.ms-powerpoin
  ・application/vnd.openxmlformats-officedocument.presentationml.presentation
  【PDF】
  ・application/pdf

logwatchのhttpエラーをサマリー化する

サーバーのステータスをレポートしてくれるlogwatchは、とても便利なツールだけど
デフォルトの設定だと、攻撃を受けた内容がエラーコード毎に表示されるので、とても見辛くなる。
(たまに1MBとか巨大なサイズになる)

 404 Not Found
 /game/img/01_b.jpg: 1 Time(s)
 /game/img/02_b.jpg: 1 Time(s)
 /game/img/05_b.jpg: 1 Time(s)
      ・
      ・
      ・

リソースは食うけど、403 Forbbidenと 404 Nof Found がログに大量に記録されても
セキュリティリスクは無いので、エラーのlogwatchで発生件数だけカウントするように修正。

【環境】
CentOS5.8 logwatch-7.3-9.el5_6(yumからインストールしたもの)

/usr/share/logwatch/default.conf/services/http.confをvimなどで開き、以下の2行を追加する。

 $http_rc_detail_rep-403 = 10
 $http_rc_detail_rep-404 = 20

設定後、logwatchのhttpdのレポートが以下のようにまとめられる。

 Requests with error response codes
    403 Forbidden SUMMARY – 5 URLs, total: 11 Time(s)
    404 Not Found SUMMARY – 252 URLs, total: 1110 Time(s)

他の40xや50xのエラーは未対応だけど、取りあえずこれで様子見。