このブログを検索

2011年6月24日金曜日

[Postfix]特定のドメインのみ受信しそれ以外は別アドレスに転送する

特定のドメインのみ受信し、それ以外は別アドレスに転送する設定をします。
IPフィルタ、ドメイン偽装などはひとまず触れません。

ケータイのドメインのみ受信し、それ以外はスパム候補として別アドレスに転送する設定をしました。
今回は、少しややこしいのですが、このフィルタリングをしたメールをメールボックスおよびalias転送をします。
システムの構成や細かな要件によっては他にもやり方はあると思います。

今回の構成は次の通り。
 |送信元|
   ↓
 |Postfix|
<フィルタリング>   ⇒info@doubt.spam
  ↓  ↓ 
 MBox info@alias.to

まず、許可ドメインのアクセステーブルを作成します。
※ファイル名は任意です

# vi /etc/allow_domains
indexweb.co.jp  OK
docomo.ne.jp    OK
vodafone.ne.jp  OK
disney.ne.jp    OK
softbank.ne.jp  OK
    :

# /usr/sbin/postmap /etc/allow_domains 

# ls -l /etc/allow_domains*
-rw-r--r-- 1 root root   283  6月 23 18:39 /etc/allow_domains
-rw-r--r-- 1 root root 12288  6月 23 18:39 /etc/allow_domains.db

次にNGドメインのテーブルを正規表現で記述します。
今回はregexpで作りましたが環境や流量によってはpcreでもいいかもしれません。
また、正規表現を工夫して特定地域ドメインだけをリダイレクトすることもできると思います。
※これもファイル名は任意です

# vi /etc/redirect_domains
/^.+@.+$/ REDIRECT info@doubt.spam

# /usr/sbin/postmap /etc/redirect_domains

# ls -l /etc/redirect_domains*
-rw-r--r-- 1 root root    41  6月 23 18:51 /etc/redirect_domains
-rw-r--r-- 1 root root 12288  6月 23 18:51 /etc/redirect_domains.db

最後にmain.cfに次のように記述してreloadします。
smtpd_sender_restrictionsの判定は、alias判定の前に行われるので、aliasesには影響を与えません。
smtpd_sender_restrictions = hash:/etc/allow_domains,check_sender_access regexp:/etc/redirect_domains

# /usr/sbin/postfix check
# /etc/rc.d/init.d/postfix reload

特定地域ドメインだけをリダイレクトし、それ以外は拒否するのであれば、smtpd_sender_restrictionsの最後に", reject"と追加するといいかと思います。(すみません、これは検証してません)

2011年6月22日水曜日

[syslog]syslog-ngでApacheログを集約してみた

syslog-ngを使ってApacheのaccess_logを1箇所に集約してみました。
発端は、PHPやApacheのerror_logを、リリース直後に1箇所でtailしたい要望があったためです。
また、access_logの集計に使っている商用ソフトウェアを廃止することによるコスト削減も狙っていました。

結論から言えば、かなり残念な結果に。

Webサーバ2台で検証環境ですらかなりのCPUを消費するので、Webサーバが10台以上ある本番環境では使いものにならないように思います。


ボクの感想としては2つ。
・I/Oがボトルネックになると予測していたのでディスクアレイも検証したのに残念。
・ベンチか設定が間違ってるだけと信じたいですが、そもそも1GB超/1台/日のaccess_logを処理するのはやはり無理か・・・。

ちなみに、Webサーバで内蔵ディスクにTCP出力しているのは、syslogサーバの取りこぼしに備えてです。
access_logの精度を求めるなら内蔵ディスク、概算やerror_logのエラー検知ならsyslogサーバ、と使い分けるつもりでした。


詳細は次の通り。

まず、負荷試験の構成。
|負荷試験サーバ|
   ↓
|Webサーバ x 2|
     ⇒内蔵ディスクにもログ出力
   ↓
|syslogサーバ|
     ⇒内蔵ディスク or ディスクアレイ

・WebサーバのApacheは、access_log、error_logともにsyslog出力
・Webサーバのsyslogは、TCPで内蔵ディスクにファイル出力
・Webサーバのsyslogは、TCP/UDPでsyslogサーバに転送
・syslogサーバは、内蔵ディスクやディスクアレイにファイル出力

検証には次のような機器を使いました。
(Webサーバ)
CPU: Xeon E5640(Quad)
内蔵ディスクRAID: 5

(syslogサーバ)
CPU: Xeon E5502(Dual)
内蔵ディスク: SATA2, RAID5
ディスクアレイ: SATA2, RAID5, SCSI

設定内容は次のように、logger経由でsyslog転送しました。
(Webサーバ)
$ cat /etc/httpd/conf/extra/httpd-vhost.conf

    CustomLog      "| /bin/logger -p local3.info -t aplog001i" combinedmob_uniq
    ErrorLog      "| /bin/logger -p local3.info -t aperr001i"

$ cat /etc/syslog-ng/syslog-ng.conf
# Apache log
destination     d_aplog001i  { file("/home/www/sss01test/running/logs/apache/mobile/i/access_log" owner(root) group(root) perm(0644) template("$MSGONLY\n") template_escape(no)); };
filter          f_aplog001i  { facility(local3) and program("aplog001i"); };

log   { source(s_sys); filter(f_aplog001i); destination(d_aplog001i); };
log   { source(s_sys); filter(f_aplog001i); destination(d_ulog_sss01); };

(syslogサーバ)
$ cat /etc/syslog-ng/syslog-ng.conf
destination d_aplog001i { file("/var/log/httpd-ng/access_log.aplog001i_stb" owner(root) group(root) perm(0644)); };
#destination d_aplog001i { file("/mnt/das/testsyslog/access_log.aplog001i_stb" owner(root) group(root) perm(0644)); };
log { source(t_server); filter(f_host_stb); filter(f_aplog001i); destination(d_aplog001i); };

負荷試験はシンプルにApache Benchを使い、TCPやUDPの違い、出力ストレージの違いを調べました。
グラフは、各サーバのCPU使用率(user, sys)です。

CPU使用率はUDPの方が5%増しくらいですが、処理時間はUDPの方が倍くらい速いです。
UDPでの取りこぼし率は0.00194%でしたが、もしかしたら台数が増えるとどこかのタイミングで急増するんでしょうか。
ストレージの差がもっと出るかと思いましたが、たぶんCPUがボトルネックで違いが現れないのだと思います。

機会があればAmazonなどで追試をしてみたいと思います。

上から(c200, n200000, TCP, syslogは内蔵ディスクへ出力)、(c200, n200000, UDP, syslogはディスクアレイへ出力)

2011年6月9日木曜日

[コマンド]wgetを標準出力に表示しeAcceleratorの使用状況を確認する

サンプルPHPは次の通りで、ピークPHPメモリやeAcceleratorメモリ状況をレポートするもの。
これをDocumentRootに設置しておきます。
余談ですが、私はCloudForecastでこのPHPを定期監視しています。
 $einfo_val) {
  if ($einfo_key != "version" && $einfo_key != "logo") {
    echo $einfo_key . ":" . $einfo_val . "\n";
  }
}

$resusage = getrusage();
echo "ru_oublock:" . $resusage["ru_oublock"] . "\n";
echo "ru_inblock:" . $resusage["ru_inblock"] . "\n";
echo "ru_minflt:" . $resusage["ru_minflt"] . "\n";
echo "ru_majflt:" . $resusage["ru_majflt"] . "\n";
echo "ru_nvcsw:" . $resusage["ru_nvcsw"] . "\n";
echo "ru_nivcsw:" . $resusage["ru_nivcsw"] . "\n";
echo "ru_nswap:" . $resusage["ru_nswap"] . "\n";
echo "ru_utime:" . $resusage["ru_utime.tv_usec"] . "\n";
echo "ru_stime:" . $resusage["ru_stime.tv_usec"] . "\n";
?>

このwgetし、さらにgrepします。
-qで問合せやダウンロード表示を抑制し、-O -で標準出力に表示します。
--headerでHostを指定しているのは、単に私の環境がVirtualHostで管理しているためなくてもOKです。
hostnameコマンドは、各Webサーバに配布したスクリプトで記述したことがあるためこれもなくてOKです。
[t-itoh@snwadm0 eaccel96]$ wget -q -O - --header='Host: zzzz.webwebweb.co.jp' http://`hostname -s`/phpstats.php | grep ^memory[A-Z]
memorySize:134217672
memoryAvailable:44576
memoryAllocated:134173096

2011年4月22日金曜日

[Apache]Combined Log Formatの分間アクセス数を整形表示する

Apacheのログ形式がcombinedの場合で、分間アクセス数を整形表示します。
niceはつけてもつけなくても。
なお、この例ではリクエストドメインでの判定もしたいのでHostリクエストヘッダを追加しています。
$ nice -n 19 zcat /usr/local/apache/logs/access_log.Apr.16.gz | egrep 'soft|ez-soft|j-soft|w-soft|mytown' | cut -d' ' -f4 | cut -d: -f1-3 | sed -e "s/^\[//g" | sort | uniq -c
      8 16/Apr/2011:02:05
      7 16/Apr/2011:02:06

sortが必要なのは次の理由から。
高負荷時は、各リクエストの出力順番がズレる可能性があるためです。
・Combined Log Formatの時刻(%t)は、リクエストを受け取った時刻
・ログ出力されるのは、レスポンスを返したタイミング

長期間を調べる時は、10分ごとで。
$ nice -n 19 zcat /usr/local/apache/logs/access_log.Apr.15.gz | tail -5000 | egrep 'soft|ez-soft|j-soft|w-soft|mytown' | cut -d' ' -f4 | cut -d: -f1-3 | sed -e "s/.$//g" | sed -e "s/^\[//g" | sort | uniq -c
   2959 14/Apr/2011:13:2
   1889 14/Apr/2011:13:3

2011年4月3日日曜日

[MySQL]Maatkitを使ってサイズの大きいテーブルを探す

Maatkitで好きなコマンドの1つ、mk-find。
findコマンド互換のオプションなのでわかりやすい。

例えば、サイズが500K以上のテーブルを探す。
$ mk-find --tablesize +500k -hsssdbmra1 -ucheck --ask-pass
実行例は、次の通り。
Enter password: 
`area001i`.`t01_area_item_hist1`
`area001i`.`t01_area_temp_prev1`
`area001i`.`t01_area_user1`
`area001i`.`t01_area_user_item1`
`area001i`.`t01_area_user_stage1`

2011年3月8日火曜日

[MySQL]分離レベルを設定する

my.cnfのmysqldセクションに記述し、再起動すると反映されます。
set global transactionすることもできるようです。
分離レベルの詳細は公式InnoDB と TRANSACTION ISOLATION LEVELを参照してください。
[mysqld]
transaction-isolation=READ-COMMITTED
設定すると次のように表示されます。
mysql> show variables like 'tx_isolation';
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tx_isolation  | READ-COMMITTED |
+---------------+----------------+

2011年3月4日金曜日

[MySQL]スローログクエリを調査する

パッケージに同梱されているmysqldumpslowはスローログクエリを調査するのに便利です。
オプションを付けないと、次のようにリテラル値が表示されなかったり実行順にされます。
$ mysqldumpslow /var/lib/mysql/sssdbmy01-slow.log

Reading mysql slow query log from /var/lib/mysql/sssdbmy01-slow.log
Count: 2  Time=6.96s (13s)  Lock=0.00s (0s)  Rows=5844505.0 (11689010), backup[backup]@localhost
  SELECT /*!N SQL_NO_CACHE */ * FROM `t01_quiz0_user_item1`

Count: 2  Time=3.31s (6s)  Lock=0.00s (0s)  Rows=2649981.0 (5299962), backup[backup]@localhost
  SELECT /*!N SQL_NO_CACHE */ * FROM `t01_quiz0_user_area1`
               :
よく使っているオプションは、-a(リテラル値の表示)、-g(パターンマッチング)、-s(ソート)です。
次の例は、リテラル値の表示、カラムitem_qtyで検索、クエリタイムの降順ソートです。
$ mysqldumpslow -a -g item_qty -s t /var/lib/mysql/sssdbmy01-slow.log 

Reading mysql slow query log from /var/lib/mysql/sssdbmy01-slow.log
Count: 1  Time=12.49s (12s)  Lock=0.00s (0s)  Rows=7.0 (7), quiz0001iuser[quiz0001iuser]@sssdev1.sss.examples.co.jp
  select item_qty,count(user_id) item_qty from t01_quiz0_user_item1 where item_qty<>99
  group by item_qty
  order by item_qty

Count: 1  Time=2.02s (2s)  Lock=0.00s (0s)  Rows=7.0 (7), quiz0001iuser[quiz0001iuser]@sssdev1.sss.examples.co.jp
  select distinct item_qty from t01_quiz0_user_item1

2011年3月2日水曜日

Linuxのメモリ割り当て状況を確認する

$ LANG=C sar -R
実行結果は次の通り。
campgが負の値になるとキャッシュがあまり効いていません。
frmpg/s  ・・・  解放されたメモリのページ数/s
bufpg/s  ・・・  割り当てられたバッファのページ数/s
campg/s  ・・・  キャッシュしたページ数/s

00:00:01      frmpg/s   bufpg/s   campg/s
00:01:01      -604.87      0.17      0.08
00:02:01       312.58      0.13      0.05
00:03:01        -3.60      0.22      0.18
00:04:01      -192.49      0.27      0.10
00:05:01        22.62     -1.25    -40.25
00:06:01         6.82      0.23      0.00

2011年2月24日木曜日

[PHP]バージョンを見えないようにする

PHPの場合、Apacheのバージョンを見えなくするをした上で、さらにPHPの設定を変更します。

php.iniを次のように変更します。
expose_php = Off

●変更前
$ telnet sssdev1 80
Trying 192.xxx.xxx.xxx...
Connected to sssdev1.sss.indexweb.co.jp (192.xxx.xxx.xxx).
Escape character is '^]'.
GET /pinfo.php HTTP/1.1
Host: ssssfuka.indexweb.co.jp

HTTP/1.1 200 OK
Date: Thu, 24 Feb 2011 03:09:46 GMT
Server: Apache
ココ→★X-Powered-By: PHP/5.2.17
Transfer-Encoding: chunked
Content-Type: text/html
        :
●設定後
$ telnet sssdev1 80
Trying 192.xxx.xxx.xxx...
Connected to sssdev1.sss.indexweb.co.jp (192.xxx.xxx.xxx).
Escape character is '^]'.
GET /pinfo.php HTTP/1.1
Host: ssssfuka.indexweb.co.jp

HTTP/1.1 200 OK
Date: Thu, 24 Feb 2011 03:11:17 GMT
Server: Apache
★(表示されない)
Transfer-Encoding: chunked
Content-Type: text/html
        :

[Apache]バージョンを見えないようにする

Apacheのバージョンが見えるとセキュリティ上、問題があります。
例えば、そのバージョンにあるセキュリティホールを使われてしまうリスクがあるためです。
詳細はこちら。
http://httpd.apache.org/docs/2.2/ja/mod/core.html#servertokens

httpd.confに次のように記述します。
ServerTokens ProductOnly
●変更前
$ telnet sssdev1 80
Trying 172.xxx.xxx.xxx...
Connected to sssdev1.sss.indexweb.co.jp (172.xxx.xxx.xxx).
Escape character is '^]'.
GET / HTTP/1.1

HTTP/1.1 400 Bad Request
Date: Thu, 24 Feb 2011 01:08:00 GMT
ココ→★Server: Apache/2.2.1x (Unix) DAV/2 PHP/5.2.1x
Content-Length: 226
Connection: close
Content-Type: text/html; charset=iso-8859-1
       :
●設定後
$ telnet sssdev1 80
Trying 172.xxx.xxx.xxx...
Connected to sssdev1.sss.indexweb.co.jp (172.xxx.xxx.xxx).
Escape character is '^]'.
GET / HTTP/1.1

HTTP/1.1 400 Bad Request
Date: Thu, 24 Feb 2011 01:08:57 GMT
ココ→★Server: Apache
Content-Length: 226
Connection: close
Content-Type: text/html; charset=iso-8859-1
       :

2011年2月23日水曜日

SSL証明書をopensslコマンドで確認する

opensslコマンドで接続先の証明書を取得することができます。
$ openssl s_client -connect xx.xx.xxx.xxx:443 -showcerts

-showcertsは、ベリサインなどの中間証明書も取得したい場合に使います。
ベリサインだけではないでしょうが、CAはときどき証明書の構成や暗号ロジックを変更することがあります。
実行例は次の通り。
CONNECTED(00000003)
depth=2 /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority - G2/OU=(c) 1998 VeriSign, Inc. - For authorized use only/OU=VeriSign Trust Network
verify return:1
depth=1 /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)09/CN=VeriSign Class 3 Secure Server 1024-bit CA - G2
verify return:1
depth=0 /C=JP/ST=Tokyo/L=Taishido, Setagaya-ku/O=Index Corporation/OU=Technical Support Dept./CN=ssl-ssss.indexweb.co.jp
verify return:1
---
Certificate chain
 0 s:/C=JP/ST=Tokyo/L=Taishido, Setagaya-ku/O=Index Corporation/OU=Technical Support Dept./CN=ssl-ssss.indexweb.co.jp
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)09/CN=VeriSign Class 3 Secure Server 1024-bit CA - G2
-----BEGIN CERTIFICATE-----   ★接続先の証明書
MIIeWZccbcYGaWibaGiqywYoY9B7krqf1Jf92VXf4danbGKQHKIg9W0baqUFADCB
             :
+zcHSBcs/TAf0SUFoYTaaYS/pzeJUhO=
-----END CERTIFICATE-----
 1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)09/CN=VeriSign Class 3 Secure Server 1024-bit CA - G2
   i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority - G2/OU=(c) 1998 VeriSign, Inc. - For authorized use only/OU=VeriSign Trust Network
-----BEGIN CERTIFICATE-----   ★ベリサインの中間証明書
MIIfRdccbrwGaWibaGiqzwzyZf3KeK/Wf+X7KrvLGdanbGKQHKIg9W0baqufadCB
             :
54aOnkweIl2+DoyLE39vHw==
-----END CERTIFICATE-----
---
Server certificate
subject=/C=JP/ST=Tokyo/L=Taishido, Setagaya-ku/O=Index Corporation/OU=Technical Support Dept./CN=ssl-ssss.indexweb.co.jp
issuer=/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)09/CN=VeriSign Class 3 Secure Server 1024-bit CA - G2
---
No client certificate CA names sent
---
SSL handshake has read 2844 bytes and written 316 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 1024 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : AES256-SHA
    Session-ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    Session-ID-ctx: 
    Master-Key: XXXXXXXXXXXXXXXXXXXXXXX
    Key-Arg   : None
    Krb5 Principal: None
    Start Time: 1298440296
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
取得した証明書の内容を確認するのもopensslコマンドを使います。
コモンネームだけ知りたければgrepすると簡単です。
$ openssl asn1parse -i -in test.crt | grep -A1 commonName
  185:d=5  hl=2 l=   3 prim:      OBJECT            :commonName
  190:d=5  hl=2 l=  47 prim:      PRINTABLESTRING   :VeriSign Class 3 Secure Server 1024-bit CA - G2
--
  401:d=5  hl=2 l=   3 prim:      OBJECT            :commonName
  406:d=5  hl=2 l=  23 prim:      T61STRING         :ssl-ssss.indexweb.co.jp

2011年2月15日火曜日

[CloudForecast]RRDファイルへDSを追加する

RRDファイルへのData Sourceを追加する
基本的にRRDファイルはDS(Data Source)の追加ができません。
※そのため、CactiではRRD管理をグラフ単位にしています。
追加は、RRDファイルからdumpしたXMLファイルを修正してRRDにrestoreします。

1.RRDファイルのXML出力
$ rrdtool dump MONSSS_perfstat-xxxxxx.rrd xml/new.xml
2.XMLへDS追加
このperlスクリプトが便利です。
http://osdir.com/ml/db.rrdtool.user/2003-08/msg00116.html
perl rrdaddds.pl xml/new.xml DS名 > xml/new2.xml
3.DSタイプの修正
前述のスクリプトは強制的にDSタイプをGAUGEに設定します。
それ以外のDSタイプにする場合は、viなどエディタで修正します。
4.RRDファイルにリストア
$ rrdtool restore xml/new2.xml MONSSS_perfstat-xxxxxx.rrd

[MySQL]InnoDBのステータスを確認する

InnoDBのステータスを確認します。
この例では、InnoDB Pluginを使っているので、I/Oスレッド(read/write)をそれぞれ4に増やしてます。
$ grep _io_threads /etc/my.cnf
innodb_read_io_threads=4
innodb_write_io_threads=4

$ mysql -u root -p
mysql> SHOW ENGINE INNODB STATUS;
| Type   | Name | Status| InnoDB |      | 
=====================================
110215 22:32:10 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 8 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 20844 1_second, 20844 sleeps, 1732 10_second, 3540 background, 3540 flush
srv_master_thread log flush and writes: 20914
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 832, signal count 830
Mutex spin waits 234, rounds 6018, OS waits 63
RW-shared spins 763, OS waits 763; RW-excl spins 0, OS waits 6
Spin rounds per wait: 25.72 mutex, 30.00 RW-shared, 180.00 RW-excl
------------
TRANSACTIONS
------------
Trx id counter 417D3
Purge done for trx's n:o < 417C3 undo n:o < 0
History list length 7
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started, process no 3139, OS thread id 1242859840
MySQL thread id 5425, query id 65466 localhost root
SHOW ENGINE INNODB STATUS
---TRANSACTION 417D2, not started, process no 3139, OS thread id 1103010112
MySQL thread id 5422, query id 65451 Has read all relay log; waiting for the slave I/O thread to update it
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (read thread)
I/O thread 4 state: waiting for i/o request (read thread)
I/O thread 5 state: waiting for i/o request (read thread)
I/O thread 6 state: waiting for i/o request (write thread)
I/O thread 7 state: waiting for i/o request (write thread)
I/O thread 8 state: waiting for i/o request (write thread)
I/O thread 9 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
523 OS file reads, 9909 OS file writes, 8774 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2,
0 inserts, 0 merged recs, 0 merges
Hash table size 6375023, node heap has 4 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 177701418
Log flushed up to   177701418
Last checkpoint at  177701418
0 pending log writes, 0 pending chkp writes
5225 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 3293577216; in additional pool allocated 0
Dictionary memory allocated 607926
Buffer pool size   196607
Free buffers       194357
Database pages     2246
Old database pages 832
Modified db pages  0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 486, created 4563, written 7861
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s
LRU len: 2246, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Main thread process no. 3139, id 1211124032, state: waiting for server activity
Number of rows inserted 514325, updated 8978, deleted 109, read 9934992
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

2011年2月10日木曜日

[MySQL]Maatkitによりレプリケーションの整合性をチェックする

何かしらの事情でマスタとスレーブに差異が出ることがあります。
マスタ、スレーブともにテーブルロックして比較するのが正攻法ですが、サービスに必ず影響が出ます。
そこで、Maatkitでチェックします。

1.しくみ
テーブルのチェックサム情報をマスタにinsertします。
ステートメントベースでスレーブに転送されるので、スレーブ側に差異があればマスタと異なるチェックサムが記録されます。

2.チェックサム記録用テーブルの作成
SQL> use mysql;
SQL> CREATE TABLE checksum (
     db         char(64)     NOT NULL,
     tbl        char(64)     NOT NULL,
     chunk      int          NOT NULL,
     boundaries char(100)    NOT NULL,
     this_crc   char(40)     NOT NULL,
     this_cnt   int          NOT NULL,
     master_crc char(40)         NULL,
     master_cnt int              NULL,
     ts         timestamp    NOT NULL,
     PRIMARY KEY (db, tbl, chunk)
  );

3.スレーブ→マスタ権限追加

4.チェックサムの記録
スレーブから実行します。
$ mk-table-checksum --replicate=mysql.checksum <マスタ名> -uroot -pxxxxxxxxxxxx -S/usr/local/db_data/mysql/mysql.sock 

DATABASE TABLE                     CHUNK HOST    ENGINE      COUNT         CHECKSUM TIME WAIT STAT  LAG
mysql    columns_priv                  0 sssdb12 MyISAM          0             NULL    0 NULL NULL NULL
mysql    db                            0 sssdb12 MyISAM         24         7bed06ed    0 NULL NULL NULL
                                        (略)
DATABASE  TABLE                 CHUNK HOST    ENGINE      COUNT         CHECKSUM TIME WAIT STAT  LAG
quest001i tbl_quest_item_hist1      0 sssdb12 InnoDB        298         61b07870    0 NULL NULL NULL
quest001i tbl_quest_user1           0 sssdb12 InnoDB     250027         333f3110    1 NULL NULL NULL
quest001i tbl_quest_user_area1      0 sssdb12 InnoDB          5         29fa5071    0 NULL NULL NULL

5.マスタとスレーブの比較
スレーブから実行します。
$ mk-table-checksum --replicate-check=1 --replicate=mysql.checksum <マスタ名> -uroot -pxxxxxxxxxxxx -S/usr/local/db_data/mysql/mysql.sock 

Differences on P=3306,h=sssdb13.smc.indexweb.co.jp
DB        TBL                   CHUNK CNT_DIFF CRC_DIFF BOUNDARIES
quest001i tbl_quest_item_hist1      0        0        1 1=1
quest001i tbl_quest_user1           0       -2        1 1=1
quest001i tbl_quest_user_area1      0        0        1 1=1

[MySQL]readonlyスレーブなのにduplicateエラーが出る

readonlyスレーブなのにDuplicateエラーが発生する時の暫定的な対処。
本来は根本原因を探るべき。
例えば次のようなエラーが発生し、SQLスレッドが停止したとします。
110209 21:08:45 [ERROR] Slave SQL: Error 'Duplicate entry '49-2147483647-49' for key 'PRIMARY'' on query. Default database: 'quest001i'. Query: 'INSERT INTO
        tbl_quest_user_stage1 
        (quest_id, user_id, stage_id, finish_flg, last_access_dt, finish_dt, delete_flg, regist_dt, update_dt)
VALUES
        (0x31, 0x3139323831393731, 0x31, 0x30, str_to_date(NULL,'%Y/%m/%d %T'), str_to_date(NULL,'%Y/%m/%d %T'), 0x30, str_to_date(0x323031312F30322F30392032313A30383A3233,'%Y/%m/%d %T'), str_to_date(0x323031312F30322F30392032313A30383A3233,'%Y/%m/%d %T'))', Error_code: 1062
110209 21:08:45 [ERROR] Error running query, slave SQL thread aborted. Fix the problem, and restart the slave SQL thread with "SLAVE START". We stopped at log 'mysql-bin.000021' position 935073

手動でエラーをスキップしてSQLスレッドを再開するのは次の通り。
SQL> set global SQL_SLAVE_SKIP_COUNTER=1;
SQL> start slave sql thread;

スキップすべきエラーが大量にある場合はMaatkitが便利です。
mk-slave-restartをバックグラウンド実行すると、特定のエラーをスキップしスレーブを再開します。
mk-slave-restart -uroot -pxxxxxxxxxxxx -S/usr/local/db_data/mysql/mysql.sock --error-numbers=1062 --verbose > skipslave.err 2>&1 &


追記 2011/02/10
こんなことしなくても、my.cnfに書けばいいようです。
当然、MySQL再起動は必要ですが。
slave-skip-errors=1062
すべてスキップしたければ
slave-skip-errors=all

2011年2月9日水曜日

[MySQL]マスタをほぼ止めずにスレーブにまるごとコピーする

0.前提
単純なマスタ-スレーブ構成の場合。
マスタはreadonlyになります。

1.マスタをreadonlyにし、最新バイナリログのpositionを確認する
$ mysql -u root -p
master> flush tables with read lock;
master> show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000016 |  72777285 |
| mysql-bin.000017 |    220724 |
| mysql-bin.000018 |    792503 |
| mysql-bin.000019 |       149 |
| mysql-bin.000020 |   4768160 |
| mysql-bin.000021 |       106 |★
+------------------+-----------+

2.マスタからdumpし、スレーブにコピーする
$ mysqldump -uroot -pxxxxxxxxxxxx -hsssdb12 --master-data=2 --flush-logs --single-transaction --a
ll-databases --default-character-set=sjis > dump-m_sssdb12_20110209.sql
$ scp dump-m_sssdb12_20110209.sql sssdb13:/tmp

3.マスタをロックを解放する
master> unlock tables;

4.マスタのデータをスレーブにコピーする
$ mysql -uroot -pxxxxxxxxxxxx < dump-m_sssdb12_20110209.sql
5.スレーブ設定を再設定する MASTER_LOG_FILEおよびMASTER_LOG_POSは確認した最新positionを設定する
slave> reset slave;
slave> CHANGE MASTER TO
  MASTER_HOST='sssdb12',
  MASTER_USER='rep1user',
  MASTER_PASSWORD='rep1xxxxxxx',
  MASTER_LOG_FILE='mysql-bin.000021',
  MASTER_LOG_POS=106;
slave> start slave;
6.スレーブのステータスを確認する
slave> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: sssdb12
                  Master_User: rep1user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000021
          Read_Master_Log_Pos: 66865
               Relay_Log_File: apsssdev01-relay-bin.000430
                Relay_Log_Pos: 67010
        Relay_Master_Log_File: mysql-bin.000021
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 66865
              Relay_Log_Space: 67170
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 

2011年2月6日日曜日

[CloudForecast]監視項目を追加する

担当案件にてCloudForecastというCactiのようなツールを仮運用中です。
http://blog.nomadscafe.jp/2010/05/cloudforecast.html

担当案件のOracle(RAC)は、Sybaseからの移行、開発体制などの事情によりセグメントレベルの負荷情報が必要になります。
負荷も高く、STATSPACKをレベル7実行するとサービス障害になるため、CloudForecastを導入しました。
Cactiの閲覧性能よりも監視項目の追加を重視したため、Cactiプラグインは選びませんでした。

ここでは同じ案件のサブシステムで使っているMySQLサーバの追加手順を紹介します。

0.インストール
省略します。
長野さんのサイト参照。

1.監視サーバの追加
1-1.監視対象サーバに監視アカウントを作成
grant select,process
on *.* to 'monitor'@'devdev.testtest.co.jp'
identified by 'monitor';

※process権限はInnoDBのために必要

1-2.監視アカウントの設定
$ vi /usr/local/src/compiles/cloudforecast/cloudforecast.yaml
config:以降に追記

# MySQLを監視する場合のuser名とパスワード
 MySQL:
   user: monitor
   password: "monitor"

1-3.サーバリストへの追加
$ vi/usr/local/src/compiles/cloudforecast/server_list.yaml
servers:以降に追記
   label: MySQL Servers
   hosts:
     - 172.99.99.156 snwdbmra1 mysql master
     - 172.99.99.157 snwdbmra2 mysql slave

2.CloudForecast再起動
再起動するrcスクリプトを作っておくと楽です。
ps auxw | grep cloudを実行し、cloudforecast_radarおよびcloudforecast_webをkill

# ./cloudforecast_radar -c cloudforecast.yaml -i server_list.yaml > radar.log 2>&1 &O
# ./cloudforecast_web -p 80 -c cloudforecast.yaml -l server_list.yaml > web.log 2>&1 &

3.例