ssh鍵認証ログイン時のホームディレクトリパーミッション制限を緩める

3 2月, 2015 (12:09) | ssh | By: hogehoge

ssh鍵認証でログインする場合、homeディレクトリのパーミッションが所有ユーザー以外に書き込み権限があるとアクセスが拒否される。

# ssh -i hoge_id_rsa hoge@hogehoge
Permission denied (publickey).

ログイン対象機器を確認
# ll /home/
drwxrwx--- 23 hoge hoge 4096 2月 2 14:29 hoge

ログを見てみる
# less /var/log/secure
sshd[24942]: Authentication refused: bad ownership or modes for directory /home/hoge

これを解除するにはsshd_configでStrictModesをnoに設定する。
# vim /etc/ssh/sshd_config
StrictModes no

マニュアルでは

$ man sshd_config
StrictModes
Specifies whether sshd should check file modes and ownership of the user’s files and home directory before accepting login. This is normally desirable because novices sometimes acci-dentally leave their directory or files world-writable. The default is “yes”.

とある。
ログイン時にユーザーディレクトリの権限を確認してする機能で、ホームディレクトリを誰でも書き込み可能な状態にして公開してしまうことを防ぐ目的がらしい。
鍵認証時にホームディレクトリのパーミッションを確認してくれなくなるので設定を投入した後の取り扱いは慎重にやろう。

link
ttp://teketeke55.hatenablog.com/entry/2012/02/02/165238

シェルスクリプト内での rsync に ssh 鍵を指定する

4 9月, 2014 (14:51) | ssh | By: hogehoge

rsync を ssh でするとき、普通のコマンドラインなら

$ rsync -e "ssh -i /path/to/ssh_key" -avz -p -o \
--delete remoteuser@hostname:/path/to/remote/file /path/to/local_save/file

てな感じにするけれど、
シェルスクリプト内だと、”ssh -i /path/to/ssh_key” の部分が指定を見てくれないのです。何かやり方があるのかな??

でも、環境変数 RSYNC_RSH に ssh の鍵指定をしてやればOK。
man page にこんなことが書いてあります。

You can also choose the remote shell program using the RSYNC_RSH
environment variable, which accepts the same range of values as
-e.

なので、こういう風にすればOK。

RSYNC_RSH="ssh -i /path/to/ssh_key"
# rsync -avz -p -o --delete remoteuser@hostname:/path/to/remote/file /path/to/local_save/file

ex)
# cat /var/spool/cron/backup
00 04 10,20,30 * * RSYNC_RSH="ssh -i /home/backup/.ssh/id_rsa";rsync -acvz --delete /home/backup/web_bk/ backup@xxx.xxx.xxxx.xxx:/home/backup/app/web_bk/ 1>/dev/null | mail -s "web_sync_rest" test@test.co.jp

ttp://bougaidenpa.org/wanatabe/archives/30

リソース監視スクリプト

21 5月, 2014 (13:25) | shell | By: hogehoge

設定スクリプト

# cat work/status_sh/st.sh
#!/bin/sh

LANG=C

TIME=`date +"%Y%m%d %H%M%S"`
DATE=`date +"%Y%m%d"`

DIR="/var/tmp/resource/"
HOSTNAME=`hostname`

for CMD in "uptime" "mpstat -P ALL" "iostat -x" "vmstat 1 5" "free" "df -P" "ps auxfww" "netstat -an" "netstat -s" "ipcs" "w"
do
  NAME=`echo $CMD | cut -d ' ' -f 1`
  FILE=${DIR}/${HOSTNAME}_${DATE}_${NAME}.log

  echo "===================="    >> $FILE
  echo $TIME >> $FILE
  $CMD       >> $FILE
done

# cat work/status_sh/ps.sh
#!/bin/bash
export PATH=/usr/local/bin:/bin:/usr/bin

#server_list='/home/more_work/work/status_sh/sv.list'
HOST="localhost"
DAY=`date +%Y%m%d`
MIN=`date +%H%M`
BASE_DIR=/var/www/html/check_servers/ps

cd $BASE_DIR || exit 1

#while read host;do
  mkdir $HOST 2>/dev/null
  mkdir $HOST/$DAY 2>/dev/null
  RET=`/bin/ps aux`
  echo -n $RET > $HOST/$DAY/${DAY}_${MIN}.txt
#  `ps aux` > $HOST/$DAY/${DAY}_${MIN}.txt
#done < $server_list
#wait
exit 0

# cat status_sh/sv.list
localhost

# cat httpd_st.sh
#!/bin/sh

LANG=C

TIME=`date +"%Y%m%d %H%M%S"`
DATE=`date +"%Y%m%d"`

DIR="/var/tmp/resource/"
FILE=${DIR}/${DATE}_httpd_st.log

echo "===================="    >> $FILE
echo $TIME >> $FILE
curl -s 'http://127.0.0.1/server-status?auto'       >> $FILE
echo "===================="    >> $FILE
curl 'http://mj-mj.mogame.jp/mjmj-balancer-manager' >> $FILE

※前提条件(confに以下を追記)

ProxyPass /mjmj-balancer-manager !

SetHandler balancer-manager
Order deny,allow
Deny from all
Allow from 127.0.0.1 211.129.99.160 58.5.162.32/28 211.13.211.135

■注意!
virtualhost毎に設定が必要。

シェルスクリプトが ‘> $logfile 2>&1’ だらけにならなくて済んだ話

24 4月, 2014 (12:22) | shell | By: hogehoge

ttp://memo.laughk.org/2014/04/24/shell_exec_log.html

今まではこんなことやってたわけです。

#!/bin/bash
LOGFILE=/tmp/script-log

command1 > $LOGFILE 2>&1
command2 > $LOGFILE 2>&1
...      > $LOGFILE 2>&1
...      > $LOGFILE 2>&1
...      > $LOGFILE 2>&1
...      > $LOGFILE 2>&1

まあ1,2行くらいならいいのですが、これが5行超えてくるともう編集するのも読むのも嫌になってきます。
この辺調べてみると、 exec コマンドによるプロセス置換で、出力を変更してあげると良いようです。

execで解決

#!/bin/bash
LOGFILE=/tmp/script-log

exec 1> >(cat >> $LOGFILE)
exec 2> >(cat >> $LOGFILE)

command1
command2
...
...
...

こんな感じで書けます。
さらに awk も使って以下のようにすると、ログの各行の先頭に
[YYYY-mm-dd HH:MM:SS] のようにタイムスタンプも付けられます。

#!/bin/bash
LOGFILE=/tmp/script-log

exec 1> >(awk '{print strftime("[%Y-%m-%d %H:%M:%S] "),$0 } { fflush() } ' >> $LOGFILE)
exec 2> >(awk '{print strftime("[%Y-%m-%d %H:%M:%S] "),$0 } { fflush() } ' >> $LOGFILE)

command1
command2
...
...
...

tee コマンドを使えば出力を保ちながらロギングとかもできそうです。
ただしこの出力の変更は bash の機能らしく、シバンを #!/bin/sh にすると
Syntax error: redirection unexpected が返って来てうまく処理が動かないので
#!/bin/bash と書く必要が有ります。

SSH公開鍵交換なしにパスワードなしでrsyncする方法

3 4月, 2014 (13:33) | ssh | By: hogehoge

ttp://nosa.cocolog-nifty.com/sanonosa/2014/03/rsync-c66f.html

他のサーバにrsyncしたいときがあります。その場合考えなければならないのが認証です。よく見られるのは送信先のSSH公開鍵を送信元に登録することで認証をスルーする方法です。しかしrsyncの場合、そんなことをせずともパスワードなしでrsyncできるということを最近知りましたので記録を残しておきます。

【送信先での設定】
まずはパスワードなしでrsyncするために、送信先サーバに下記の要領で/etc/rsyncd.confに追記します。rsyncdの再起動は不要です。
[USERDATA]
comment=user data
path=/home/user
read only=false
uid=user
gid=user
hosts allow=送信元サーバのIPやFQDN
hosts deny=*

【送信元でのrsync実行】
続いて送信元でのrsync実行となります。下記の要領でrsyncを実行するとパスワードが聞かれずrsyncが可能となります。
rsync -av /home/user/data user@DESTHOST::USERDATA

ご参考までに下記は通常のパスワードが聞かれるrsyncの実行方法です。
rsync -av /home/user/data user@DESTHOST:/home/user

cp するときに上書きされるファイルがあったらバックアップを取る

27 3月, 2014 (12:19) | backup, file・directory | By: hogehoge

ttp://a4p.me/archives/1274

cp コマンドの -b オプションを使うと上書きするファイルがあったとき自動でバックアップを取ってくれる。
$ touch aaa
$ touch bbb
$ cp -b ./aaa ./bbb
$ ls
aaa bbb bbb~

そのままだとバックアップファイルは ~ がつくが、–suffix オプションで指定できる。

$ cp -b --suffix=.`date +%Y%m%d` ./aaa ./bbb
$ ls
aaa bbb bbb.20140325 bbb~

bashrc に alias を書いておくと便利。
alias cp='cp -ivb --suffix=.`date +%Y%m%d%H%M%S`'

Apacheログから”internal dummy connection”を消す

17 1月, 2014 (11:26) | log | By: hogehoge

Apacheのアクセスログに、こんな行がダラダラと出てきてる。(ログタイプを combined にした例です)
::1 - - [08/Jan/2013:17:31:10 +0900] "OPTIONS * HTTP/1.0" 200 -
     "-" "Apache/2.2.15 (CentOS) (internal dummy connection)"

上記ログ「internal dummy connection」は、
Apache自身から自身への通信を行なうApache2.2以上でのバージョンの仕様。
インストールされているApacheは2.2.3(httpd -V で確認)、仕様どおりの動作ということだ。

?この通信は、何の為に行なわれているのか?

ApacheにはWebアクセスを待ち受けている子プロセスが存在し
その子プロセスに対して「起きろ!」と指示を送る為の通信を行なっている。
それが「internal dummy connection」。

参考:ttp://wiki.apache.org/httpd/InternalDummyConnection

■httpd.conf編集 (ログを出力させない)

# Mark requests from the loop-back interface (internal dummy connection)
SetEnvIf Remote_Addr “::1″ dontlog
SetEnvIf User-Agent “.*internal dummy connection.*" dontlog
 
CustomLog logs/access_log combined env=!dontlog

■対応策
1.PROTOCOL指定した除外
RewriteCond %{THE_REQUEST} HTTP/1\.0

ttp://wordpress.org/support/topic/do-search-engines-use-server-protocol-10-to-crawl-our-website

2.HTTP TRACE method 指定した除外
RewriteCond %{REQUEST_METHOD} ^TRACE

個人サーバのファイアウォールを活かしつつ、どこからでもログインする方法 (CGI編)

27 11月, 2013 (10:32) | cgi, ssh | By: hogehoge

ttp://d.hatena.ne.jp/kazuhooku/20131126/1385483337

いつでもどこからでもサーバにログインしたくなるときってありますよね。かといって、サーバの sshd への接続を全世界から可能にしておくというのは、たとえパスワード認証を無効化していても避けたいところ。

ということでDynamic DNSを使う方法でやってきてたんだけど、いろいろ不便があったので、HTTPベースに変えた。具体的に言うと、

#! /usr/bin/perl

use strict;
use warnings;

my $TARGET_FILE = '/etc/hosts.allow.d/www/update_addr_cgi';

print "Content-Type: text/plain\r\n\r\n";

my $remote_addr = $ENV{REMOTE_ADDR}
    or die "no remote addr\n";

open my $fh, '>', $TARGET_FILE
    or die "failed to open $TARGET_FILE:$!";
print $fh "$remote_addr\n";
close $fh;

print "address updated to:$remote_addr\n";

のような CGI を個人サーバの秘密の場所に設置して、/etc/hosts.allowに
ssh: /etc/hosts.allow.d/www/update_addr_cgiと書いた感じ*1(実際は他のサービスもマッピングしてる)。

サーバプロセスをファイアウォール(というかtcpwrapper)で保護することでインターネットから攻撃される可能性を下げつつ、CGIを一発たたいたらどこからでも作業できるようになるので、わりと便利。

MySQLの参照ロードバランスで最近困った2つのこと

16 10月, 2013 (15:25) | mysql | By: hogehoge

ttp://tech.aainc.co.jp/archives/5754

1)ipvs環境化でのコネクションプーリングでMySQLがmax_connectionsに到達問題

MySQLレプリケーション機能を利用し、参照のみを利用するクエリ向けにスレーブ機を複数台並べ、
ロードバランスさせる構成を取っていました。
ロードバランサはipvs(lvs)、アプリはコネクションプール機能の利用を前提としていました。

ある程度稼働させたところ、

・負荷が大してかかってない
・サーバ上のMySQLのコネクション数がnetstatでみると異常に残っている
・lvs的にもアクティブコネクション(ActiveConn)、待機コネクション(InActConn)がほとんど存在しない
・かつ他アプリプロセスからMySQLへの接続に不安定な状態(接続できたりできなかったり)

という事象に出くわしました。

うーむ、とlvsとmysqlの接続周りで何かの閾値を踏んでいるのかなと予想をたててたところ、
こちら(http://d.hatena.ne.jp/hirose31/20110607/1307419229)のサイトを拝見しました。

■ ipvs の timeout 設定の確認
# ipvsadm -Ln --timeout
Timeout (tcp tcpfin udp): 900 120 300

■ MySQL の timeout 設定の確認

mysql> show global variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.00 sec)

mysql> show global variables like 'interactive_timeout';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
+---------------------+-------+
1 row in set (0.00 sec)

上記の通り、MySQL側のタイムアウトが28,880秒(8時間)に対してipvs側は900秒(15分)において、
アプリからの永続化接続(コネクションプーリング)状態での無通信になると、
LVSでのTCPタイムアウトしてしまうが、MySQL側のタイムアウトにならず…ですね。

今回はMySQLのタイムアウト値をLVSのタイムアウト値より少なくしました。
#この問題はもう7〜8年前からよく言われている事のようですね。
 最近DB接続を永続化するアプリとの出会いが少なかったので私は初めて遭遇しました…

2) Amazon EC2 / Elastic Loadbalancer 配下でのMySQL ロードバランス

Internal ELB 利用して、1)と同様にMySQLをレプリしているEC2インスタンスに対して
ロードバランスをさせたところ、ある程度稼働した段階で以下のアラートがでてしまって
MySQL接続ができなくなってしまいました。
〜 is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' in 〜
DoSなどの攻撃に対して自衛する機能が生きたようです。
「mysqladmin flush-hosts」すれば一時的には解決しますが、定期的な疎通をする何かが
原因であることがわかったので調べたところ、ELBの Health Check設定において
MySQL(3306)に対して直接pingを打ち続けるとこのアラートとなることでわかりました。

Health Check は HTTP/TCP/HTTPS/SSL のみ選択でき、他のチェック機能を差し込むことはできません。
そのため mysqladmin ping の結果によってHTTPレスポンスコードを返却するPHPを一枚作成し、
このPHPファイルをindexに設定したhttpdポートをELBの Health Check 対象に設定して事なきを得ました。

うっかりnohup無しで長時間かかるコマンドを実行したときに後から終了しないようにする

7 10月, 2013 (12:05) | process | By: hogehoge

ttp://blog.glidenote.com/blog/2013/09/26/bg-and-disown/

すぐ終わるかと思って、nohupもtmuxも無しで 実行したら全然終わらなくて帰れなくなって久々にググってやったのでメモ。
定時前に数分で終わると思ったrpm作成で下記みたいなことを実行して、
#rpmbuild -ba hoge.spec
いつまで経っても終わらないから帰れない… 途中で終了してしまうと困るので、ログアウトしても終了しないように。

作業の流れ
1.Ctrl+Zでコマンドの中断
2.bgでバックグラウンドに回す
3.jobsでジョブの確認
4.disownでログアウトしても実行されるようにする

実際のコマンドだと
1.#rpmbuild -ba hoge.spec
2.# Ctrl+Zで中断
3.bg 1
4.jobs 1
5.disown %1
で帰れる。

●nohupとdisownの違いは
・nohup:コマンドをハングアップシグナル無視で実行させる。
・disown:nohupをつけ忘れた時、途中からバックグラウンドでの実行へ切り替える

nginxのキャッシュが効かなくてハマりかけた

1 10月, 2013 (12:21) | nginx | By: hogehoge

ttp://zafiel.wingall.com/archives/6642

このブログのサーバーで使っているnginxの設定を見直していました。んー、何か遅いよなぁ。

もしかしてnginxのキャッシュ、使われてないんじゃないか?

キャッシュ未使用だと、ページ受信にかかる時間は551ms
キャッシュが使われると42ms。大違い。

今回設定を見直すまで、ずっと上の「キャッシュ未使用な状態」だったのです。

原因は何だろう

とりあえず、ヘッダーにキャッシュステータスを出力させてみる。configファイルのserverブロック以下に下記の一行を追加。

add_header X-Cache $upstream_cache_status;

これでレスポンスヘッダにキャッシュ状態が表示されるようになります。
キャッシュ状態: X-Cache MISS
キャッシュが使われるとHIT、使われていない場合はMISSと出力されます。

これであちこちのページを見てみる。・・・うん、やっぱりキャッシュ使われていないや・・・。

レスポンスヘッダーをよく見てみると

Cache-Controll: no-cache, max-age=0

というのが出力されている。そりゃキャッシュしないわけだ。

犯人はどのファイルだ?

WordPress本体がno-cacheを吐きだしているのか?と思い、調べてみる。しかしそんな情報はなし。それじゃプラグイン? grepしてみよう。

#find WordPressディレクトリパス -name ‘*.php’ | xargs grep ‘no-cache’
./wp-content/plugins/wordpress-flickr-manager/FlickrPanel.php: header(“Cache-Control: no-cache”);

いた( ゚Д゚ )
このプラグインを無効にするとキャッシュが効きました。よしよし。

原因追及まとめ

configにadd_header X-Cache $upstream_cache_status;を追加してキャッシュ状態を確認してみる
WordPress自体はキャッシュ無効ヘッダーを出さない
プラグイン、あるいはWPテーマPHPのどこかにno-cacheを出力するものが埋まっているはず
原因除去してキャッシュ有効化完了

find -exec で複数行のコマンドを実行する

17 9月, 2013 (13:04) | process | By: hogehoge

ttp://takuya-1st.hatenablog.jp/entry/2013/09/12/193439

-exec を複数個書く
複数行のコマンドを実行するには、execを複数個掛けば( ・∀・)イイ!!
#find ./ -type f -exec echo ファイル:{} \; -exec echo cat {} \;

分かりやすく複数行に分けて書けばナイス
#find ./ -type f -name '*.html' \
-exec echo {} \; \
-exec echo head -n 5 {} \; \
-exec head -n2 {} \; \

MySQLサーバーのディスク容量減少アラートが飛んでくる前に

2 8月, 2013 (15:22) | mysql | By: hogehoge

ttp://yoku0825.blogspot.jp/2013/07/mysql.html

さてまず、MySQLで勝手に(?)増えるものといえば。

1.データファイル(.MYD, .MYI, .ibd, ibdata1)
2.バイナリーログファイル, リレーログファイル
3.スローログファイル
4.エラーログファイル
5.テンポラリーファイル, テンポラリーテーブル
くらいかなあ。少なくとも俺が過去にDISKをあふれさせたことがあるのはこいつらくらいです。

1. データファイル

ウチの場合、取り敢えず一番増えるのは ほげほげ_history とか なんちゃら_logs とかそんな名前のテーブルです。最低限、1行あたりのサイズを押さえておく必要があります。

mysql> SHOW TABLE STATU LIKE 'hogehoge_logs'\G
*************************** 1. row ***************************
           Name: hogehoge_logs
         Engine: InnoDB
        Version: 10
     Row_format: Compressed
           Rows: 18
 Avg_row_length: 455
    Data_length: 8192
Max_data_length: 0
   Index_length: 32768
      Data_free: 0
 Auto_increment: 19
    Create_time: 2013-07-22 23:55:13
    Update_time: NULL
     Check_time: NULL
      Collation: ujis_japanese_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

Avg_row_lengthはデータ部分だけのサイズなので、(Data_length + Index_length) / Rowsでインデックス込みの1行あたりの値をとっときます。意外と大きいケースが多かったりするので。するので。

↑のテーブルだと(ちょっとデータ量が少なすぎて全くアテにならないとはいえ)1行当たり2.2KiBくらい、Create_timeから察するに1日の増加量は2.5行、万一(ひどい)サービスがバズってトラフィックが10倍になったとしても1日50KiBくらいですね。

というのを、information_schemaから全テーブル分ばっこ抜きます。

mysql> SELECT
    ->  TABLE_SCHEMA,
    ->  TABLE_NAME,
    ->  ENGINE,
    ->  SUM(((DATA_LENGTH + INDEX_LENGTH) / TABLE_ROWS) / TIMESTAMPDIFF(DAY,  CREATE_TIME, NOW())) AS 1d_size
    -> FROM
    ->  information_schema.tables
    -> WHERE
    ->         ENGINE IN ('InnoDB', 'MyISAM')
    ->  AND TABLE_SCHEMA NOT IN ('information_schema', 'mysql')
    -> GROUP BY
    ->  TABLE_SCHEMA, TABLE_NAME
    -> WITH ROLLUP;
+--------------+-------------------------+--------+----------------+
| TABLE_SCHEMA | TABLE_NAME              | ENGINE | 1d_size        |
+--------------+-------------------------+--------+----------------+
| xxxx         | hogehoge_logs           | InnoDB |   537.18032787 |
..
| xxxx         | NULL                    | InnoDB | 61917.19581370 |
| NULL         | NULL                    | InnoDB | 61917.19581370 |
+--------------+-------------------------+--------+----------------+

SUMもへったくれもない1d_sizeカラムにSUM()をつけているのは、WITH ROLLUPで最後に合計値を出すためです。
便利ですよWITH ROLLUP。滅多に使わないけど。
ttp://dev.mysql.com/doc/refman/5.6/en/group-by-modifiers.html

というわけで、このMySQL(検証用DBだけど)は1日あたり60KiBちょっと、バズって100倍来ても大丈夫ですね(にっこり)
information_schemaだし、飽くまで参考値にしかなりませんが、定常状態を知っておくのは大事だと思います。

ApacheのRewriteRuleで検索条件に^/がマッチしない場合がある理由

30 7月, 2013 (16:47) | apache | By: hogehoge

ttp://d.hatena.ne.jp/tmatsuu/20130725/1374761978

ApacheのRewriteRuleで「/hoge/配下にアクセスがあったら http://example.jp/fuga/ に301リダイレクトを行う」ってな処理を書く場合、先頭に/が必要な場合と必要ない場合がある。

RewriteEngine on
# これでいける場合もある
RewriteRule ^/hoge/(.*) http://example.jp/fuga/$1 [R=301,L]
# こう書かないとダメな場合もある
RewriteRule ^hoge/(.*) http://example.jp/fuga/$1 [R=301,L]

この先頭に/が必要不要の違いはなんだろうなーと思っていたが、先日理由がわかった。

Apacheのドキュメントにしっかり書いてあった。

What is matched?
In VirtualHost context, The Pattern will initially be matched against the part of the URL after the hostname and port, and before the query string (e.g. “/app1/index.html”).
In Directory and htaccess context, the Pattern will initially be matched against the filesystem path, after removing the prefix that led the server to the current RewriteRule (e.g. “app1/index.html” or “index.html” depending on where the directives are defined).

つまりざっくり説明するとこんな感じか。
●VirtualHostディレクティブの場合はURLのパスが対象となり先頭に/がつく
●Directoryディレクティブや.htaccessの場合はそのディレクトリからの相対ファイルパスが対象になり先頭に/がつかない
知らなかった。そういうことだったのね。
ちなみにVirtualHost内にDirectoryディレクティブの中でRewriteRuleを書いた場合は後者になった。

■どこに書いても使えるパターン

上記を意識して書いてもいいのだが、できればポータビリティを考慮してどこに書いても使えるように書きたい。
じゃあどう書くか。私はこんな感じで書いてる。

RewriteEngine on
RewriteRule ^/?hoge/(.*) http://example.jp/fuga/$1 [R=301,L]

?を入れてどちらでもマッチするように。

さらにエンコードやQueryStringもきちんと引き継ぐようにするなら、こんな感じで。

RewriteEngine on
RewriteRule ^/?hoge/(.*) http://example.jp/fuga/$1 [R=301,L,QSA,NE]

これはすべてのパスを別サイトに転送する場合も同様に書ける。

RewriteEngine on
RewriteRule ^/?(.*) http://example.jp/$1 [R=301,L,QSA,NE]

先頭の/を除外するおかげて転送先URLの記述が美しいですね。

[2013/07/30 add]
? クエスチョンマーク 0 回または 1 回の繰り返し

図形コマンド (Ascii Art)

19 7月, 2013 (15:50) | tools | By: hogehoge

ttp://d.hatena.ne.jp/acotie/20090731/1249019746
ttp://u-suke.no-ip.org/memo/index.php?Linux%E9%96%A2%E9%80%A3%2Fgraph-easy
ttp://99blues.dyndns.org/blog/2011/02/ditaa/

■ASCIIでの出力

+------+     +-------+
| AAA  | --> |  BBB  |
+------+     +-------+
+------+     +-------+
| test | --> | test2 |
+------+     +-------+
 #!/usr/bin/perl 
 use strict;
 use warnings;
 use utf8;
 use Graph::Easy;
 
 my $graph = Graph::Easy->new();
 $graph->add_edge_once ('AAA', 'BBB');
 $graph->add_edge_once ('test', 'test2');
 print $graph->as_ascii();

■コマンドラインから出力
ex1)
graph-easy コマンドで簡単にできます。
echo “[ Bonn ] — car –> [ Berlin ], [ Ulm ]” | graph-easy

+--------+  car   +-----+
|  Bonn  | -----> | Ulm |
+--------+        +-----+
  |
  | car
  v
+--------+
| Berlin |
+--------+

ex2)
◆直接書く方法
# graph-easy <<<'[ Internet ] -- WAN --> [ Router1 ] — LAN –> [ PC ],[ Router2 ]‘

◆テキストファイルから書く方法 †
# vim topology.txt
[ Internet ] — WAN –> [ Router ] — LAN1 –> [ Server ]
[ Router ] — LAN2 –> [ PC ]
# graph-easy topology.txt

+----------+  WAN   +--------+  LAN1   +--------+
| Internet | -----> | Router | ------> | Server |
+----------+        +--------+         +--------+
                      |
                      | LAN2
                      v
                    +--------+
                    |   PC   |
                    +--------+

How the Query Cache Operates

24 6月, 2013 (11:59) | sql | By: hogehoge

ttp://dev.mysql.com/doc/refman/5.5/en/query-cache-operation.html

>http://dba.stackexchange.com/questions/44266/does-mysql-cache-queries

“As of MySQL 5.1.17, the query cache is used for prepared statements under the conditions described in Section 8.6.3.1, “How the Query Cache Operates”. Before 5.1.17, the query cache is not used for prepared statements.” dev.mysql.com/doc/refman/5.1/en/query-cache.html – Zsolt Szilagy Jun 7 at 12:46

>http://stackoverflow.com/questions/11605783/mysql-permanently-prepared-statements

This is not true, most prepared statements are cached! See dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html – Ross Smith II Jul 23 ’12 at 5:33
Ah, this is new behavior in 5.1.17, thank you. – Joshua Martell Jul 23 ’12 at 13:03

>http://stackoverflow.com/questions/14665781/is-it-possible-to-store-a-pdo-prepared-statement-in-php-mysql-apc-memcache

If you read the link I supplied it does mention prepared statments using the query cache: “Before MySQL 5.1.17, prepared statements do not use the query cache. Beginning with 5.1.17, prepared statements use the query cache under certain conditions, which differ depending on the preparation method:” This leaves me unclear as to if a new PDO prepare with the same query but different values will hit the cache, even if it is still there. Can you explain / back up your statement that there is no way? Did you actually try and stick the handle in memory cache (I might do this for the learning) – Jared Chmielecki Feb 3 at 16:42

>http://php.net/manual/ru/pdo.prepare.php

Using prepared SELECT statements on a MySQL database prior to MySQL 5.1.17 can lead to SERIOUS performance degradation.

Quote from http://dev.mysql.com/doc/refman/5.1/en/query-cache.html :

>> The query cache is not used for server-side prepared statements before MySQL 5.1.17 <<

The MySQL query cache buffers complete query results and is used to satisfy repeated identical queries if the underlying tables do not change in the meantime - just what happens all the time in a typical web application. It speeds up queries by a several hundred to a several thousand percent.

Obviously, it doesn't make much sense to give up query caching for the relatively small performance benefit of prepared statements (i.e. the DBMS not having to parse and optimize the same query multiple times) - so using PDO->query() for SELECT statements is probably the better choice i you’re connecting to MySQL < 5.1.17.

クエリーキャッシュはカレントデータベースやキャラクターセットも区別する

24 6月, 2013 (11:06) | tuuning | By: hogehoge

ttp://yoku0825.blogspot.jp/2013/06/blog-post.html

クエリーキャッシュに載ってるかどうかの判定はSQLがパースされる前に比較されるから、SELECTステートメントは一字一句同じでないとクエリキャッシュ利かないよ、というのは有名な話。
ttp://dev.mysql.com/doc/refman/5.5/en/query-cache-operation.html

取り敢えず↑に答えが書いてあったがメモ。

MariaDBのinformation_schema.QUERY_CACHE_INFOをいじっていて気が付いたんだけど、クエリーキャッシュってカレントデータベースも区別してクエリキャッシュに登録してる。
(↓の出力は、そのプラグインをMySQL5.5向けに書き直したやつだけど)

mysql55> RESET QUERY CACHE;
Query OK, 0 rows affected (0.00 sec)

mysql55> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Qcache_queries_in_cache | 0     |
+-------------------------+-------+
1 row in set (0.00 sec)

mysql55> USE d1; SELECT * FROM d1.t1 LIMIT 2;
Database changed
+-----+------+
| num | val  |
+-----+------+
|   1 | NULL |
|   2 | NULL |
+-----+------+
2 rows in set (0.00 sec)

mysql55> USE information_schema; SELECT * FROM d1.t1 LIMIT 2;
Database changed
+-----+------+
| num | val  |
+-----+------+
|   1 | NULL |
|   2 | NULL |
+-----+------+
2 rows in set (0.00 sec)

mysql55> USE mysql; SELECT * FROM d1.t1 LIMIT 2;
Database changed
+-----+------+
| num | val  |
+-----+------+
|   1 | NULL |
|   2 | NULL |
+-----+------+
2 rows in set (0.00 sec)

mysql55> SELECT * FROM information_schema.query_cache_info;
+--------------------+-----------------------------+---------------------+--------------------+-------------------------+
| STATEMENT_SCHEMA   | STATEMENT_TEXT              | RESULT_BLOCKS_COUNT | RESULT_BLOCKS_SIZE | RESULT_BLOCKS_SIZE_USED |
+--------------------+-----------------------------+---------------------+--------------------+-------------------------+
| d1                 | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
| information_schema | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
| mysql              | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
+--------------------+-----------------------------+---------------------+--------------------+-------------------------+
3 rows in set (0.01 sec)

mysql55> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Qcache_queries_in_cache | 3     |
+-------------------------+-------+
1 row in set (0.01 sec)

とはいえこれはよく考えれば当たり前で、↑のクエリはたまたまデータベース名まで修飾してるけど、テーブル名だけの場合はカレントデータベースが違えば当然別のクエリだもんね。
SQLステートメントの大文字小文字が違っただけで別のクエリーとしてキャッシュに押し込むsql/cache.ccが、データベース名まで修飾されてるからってよしなにやってくれるとは当然思えないので、これはこれで良い。

でもでも。

さっきのマニュアル、キャラクターセットやプロトコル(MySQLプロトコル)のバージョンも区別するって書いてあるぞ。。。

mysql55> SET NAMES sjis;
Query OK, 0 rows affected (0.00 sec)

mysql55> USE d1; SELECT * FROM d1.t1 LIMIT 2;
Database changed
+-----+------+
| num | val  |
+-----+------+
|   1 | NULL |
|   2 | NULL |
+-----+------+
2 rows in set (0.00 sec)

mysql55> USE information_schema; SELECT * FROM d1.t1 LIMIT 2;
Database changed
+-----+------+
| num | val  |
+-----+------+
|   1 | NULL |
|   2 | NULL |
+-----+------+
2 rows in set (0.00 sec)

mysql55> USE mysql; SELECT * FROM d1.t1 LIMIT 2;
Database changed
+-----+------+
| num | val  |
+-----+------+
|   1 | NULL |
|   2 | NULL |
+-----+------+
2 rows in set (0.00 sec)

mysql55> SELECT * FROM information_schema.query_cache_info;
+--------------------+-----------------------------+---------------------+--------------------+-------------------------+
| STATEMENT_SCHEMA   | STATEMENT_TEXT              | RESULT_BLOCKS_COUNT | RESULT_BLOCKS_SIZE | RESULT_BLOCKS_SIZE_USED |
+--------------------+-----------------------------+---------------------+--------------------+-------------------------+
| d1                 | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
| information_schema | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
| mysql              | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
| information_schema | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
| d1                 | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
| mysql              | SELECT * FROM d1.t1 LIMIT 2 |                   1 |                512 |                     177 |
+--------------------+-----------------------------+---------------------+--------------------+-------------------------+
6 rows in set (0.02 sec)

mysql55> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Qcache_queries_in_cache | 6     |
+-------------------------+-------+
1 row in set (0.00 sec)

うええええ、ホントだ。これ知らなかった。
確かにbyte単位でSQLステートメントを比較してるだけなら、キャラクターセットが違ったら一致しないねぇ。

プロトコルバージョンの違いは、どのバージョンで変わってるのかよく判らなくて断念。
取り敢えず4.0.30だとプロトコルバージョン違いそうなんだけど、Authのアレで蹴られるので試せず。

nginxでメソッドごとにリクエスト数制限を掛けたい

21 6月, 2013 (15:09) | nginx | By: hogehoge

ttp://d.hatena.ne.jp/sfujiwara/20130620/1371709568

アプリケーションでどうしても捌けない量のリクエストが一時的に押し寄せてしまう場合、アプリケーションサーバが死ぬのを避けるために GET は制限を掛けたいが、POST はリトライが面倒なのでなるべく通してあげたい、というような要求を nginx で処理できるかどうか。

実装として一番望ましいのは

  • GET は 100 req/sec で制限 (超えたら503)
  • POST は無制限
  • のようにメソッドごとに別々の制限を掛けることだったのですが、とりあえず HttpLimitReqModule を使うことで、メソッドごとに同一の上限を設定することはできました。

    のようにメソッドごとに別々の制限を掛けることだったのですが、とりあえず HttpLimitReqModule を使うことで、メソッドごとに同一の上限を設定することはできました。

    http {
    limit_req_zone $request_method zone=method:1m rate=100r/s;
    server {
    listen 80;
    location / {
    limit_req zone=method;
    proxy_pass http://127.0.0.1:5000/;
    }
    }
    }

    これで $request_method をキーにして、100req/sec を超えた分を 503 にします。

    この設定では GET も POST も同じ上限になりますが、たとえば仮に

  • リクエストの比率が GET : POST = 9 : 1
  • アプリケーションが処理できる上限が 150req/sec
  • そこに 300req/sec が到達する
  • という場合を考えると GET=270, POST=30 req/sec になるので、

  • GET は 100 req/sec を超えた分 (170 req/sec) が 503
  • POSTは 30 req/sec なのですべてアプリケーションの処理に回る
  • アプリケーションに到達するリクエストは合計 100 + 30 = 130 req/sec
  • となって、POSTを全部通しつつ限界を超えないような制限ができます。

    CentOS で Ruby が動かないときは prelink が怪しい

    20 6月, 2013 (12:07) | ruby | By: hogehoge

    ttp://qiita.com/hwaki11/items/678db06ee9dfdcf5862d
    ttp://www.moreslowly.jp/mw/index.php/CentOS_4.7_%E3%81%A7%E3%81%AF_prelink_%E3%81%8C_ruby_1.9.1_%E3%81%AE%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%82%92%E7%A0%B4%E5%A3%8A%E3%81%99%E3%82%8B
    ttp://lugia.hatenablog.com/category/Linux

    ↓これに嵌った。。。

    いつエラーになるか定期的に restart 繰り返してチェックしたら朝4時すぎると Ruby がセグフォするようになった。朝4時は /ect/cron.daily/* が実行される時間帯なので中身見たら prelink というタスクが(あやしい!)。でこのタスク実行したらすぐにめでたくセグフォするようになりました。

    症状

    prelink は知らないうちに自動的に行われるようです。
    ruby をインストールしたあと、しばらくは正常に動作していて、突然

    $ /usr/bin/ruby
    /usr/bin/ruby: failed to allocate memory (NoMemoryError)

    などと言い出します。
    /usr/bin/ruby の日付やパーミッションは変りがなく、ただファイルサイズのみ変っています。
    以下は prelink が ruby バイナリを破壊する様子です。
    一時ディレクトリに ruby のバイナリをコピーします。

    ~/tmp/tmp$ cp ../ruby ./ruby

    サイズと動作を確認します。

    ~/tmp/tmp$ ls -l ruby
    -rwxr-xr-x 1 hgwr users 1242704 2月 13 04:25 ruby*
    ~/tmp/tmp$ ./ruby -e 'p "hello"'
    "hello"

    prelink を行います。

    ~/tmp/tmp$ /usr/sbin/prelink ./ruby
    /usr/sbin/prelink: Could not write prelink cache: 許可がありません
    $ ls -l ./ruby
    -rwxr-xr-x 1 hgwr users 1248396 2月 13 04:25 ./ruby

    prelink が行われ、サイズが増えたことを確認できました。そして動作を確認します。

    ~/tmp/tmp$ ./ruby -e 'p "hello"'
    ./ruby: [BUG] iseq_compile_each: unknown node: NODE_METHOD
    ruby 1.9.1p0 (2009-01-30 revision 21907) [i686-linux]
    
    -- control frame ----------
    c:0002 p:-38112082 s:0004 b:0004 l:000003 d:000003 TOP
    c:0001 p:0000 s:0002 b:0002 l:000664 d:000664 TOP    <main>:17
    ---------------------------
    -- Ruby level backtrace information-----------------------------------------
    
    -- C level backtrace information -------------------------------------------
    0x8104657 ./ruby(rb_vm_bugreport+0x2b) [0x8104657]
    
    [NOTE]
    You may encounter a bug of Ruby interpreter. Bug reports are welcome.
    For details: http://www.ruby-lang.org/bugreport.html
    
    アボートしました (core dumped)

    このように、prelink は ruby バイナリを破壊してしまうようです。
    ほかのバイナリだと、破壊することはないようです。 python でためしました。

    ~/tmp/tmp$ cp /usr/local/bin/python ./python
    ~/tmp/tmp$ ls -l ./python
    -rwxr-xr-x 1 hgwr users 1129168 2月 13 04:26 ./python*
    ~/tmp/tmp$ /usr/sbin/prelink ./python
    /usr/sbin/prelink: Could not write prelink cache: 許可がありません
    ~/tmp/tmp$ ls -l ./python
    -rwxr-xr-x 1 hgwr users 1134772 2月 13 04:26 ./python*
    ~/tmp/tmp$ ./python -c "print 'hello'"
    hello

    対処方法

    /etc/prelink.conf に、次のような行を追加すると、自動的に prelink されなくなるようです。

    -b /usr/bin/ruby

    ■やってみた。

    # cp /usr/bin/ruby /tmp/
    # cd /tmp/
    # ll ruby
    -rwxr-xr-x 1 root root 2220344 6月 20 11:37 ruby
    # ./ruby -e 'p "hello"'
    "hello"
    # /usr/sbin/prelink ./ruby
    # ll ruby
    -rwxr-xr-x 1 root root 2246496 6月 20 11:37 ruby
    # ./ruby -e 'p "hello"'
    ./ruby: wrong argument type Object (expected Data) (TypeError)
    # gem -v
    1.8.24

    確かに、prelink後はサイズが変更している。

    mailmanの監視

    13 6月, 2013 (15:04) | shell | By: hogehoge

    ttp://ex-cloud.jp/techblog/?p=71

    状況としては以下の2パターンあります。
    1.
    ・ /var/spool/mailman/in 配下に配送されないメールがたまっている。
    ・ /var/spool/mailman/shunt にはなにもない。
    ・ /var/log/mailman/error になにもない。
    ・ mailman の再起動で配送されるようになる。

    2.
    ・ mailmanが落ちている。
    ・ mailmanの起動をおこなう。

    #!/bin/bash
    #----------------------------
    # Monitering of mailman process
    #----------------------------
    
    # /var/spool/mailman/in 配下に配送されないメールがたまっているかを確認 
    unsent=`ls -l /var/spool/mailman/in/ | grep -c ".pck"`
    
    # Mailmanのプロセスが立ち上がってないかを確認
    isAlive=`ps ax | grep -v grep | grep -c "mailmanctl"`
    _host=`hostname`
    _to=alert@example.com
    
    #$unsentの値が0でない場合はmailmanの再起動を行います。
    #mailmanの再起動通知メールを送ります。
    if [ $unsent -ne 0 ]; then
    
    /etc/init.d/mailman restart > /dev/null 2>&1
    /usr/sbin/sendmail -t <<EOF
    
    To: $_to
    Subject: [Alert] Mailman restarted.
    Mailman has been restarted on $_host
    
    EOF
    
    #mailmanのプロセスが存在しない場合はmailmanを起動します。
    #mailmanの起動通知メールを送ります。
    elif [ $isAlive -eq 0 ]; then
    
    /etc/init.d/mailman start > /dev/null 2>&1
    /usr/sbin/sendmail -t <<EOF
    
    To: $_to
    Subject: [Alert] Mailman started.
    Mailman has been started on $_host
    
    EOF
    
    fi

    ▼vi エディターなどを開いて上記スクリプトをサーバに保存します。
    私の場合は/usr/local/bin/以下に保存しています。

    # vi /usr/local/bin/mailman-monit.sh

    ▼パーミッションを変更する

    # chmod a+x mailman-monit.sh

    ▼一分毎にスクリプトが実行されるようにクーロンに登録する。

    * * * * * /usr/local/bin/mailman-monit.sh > /dev/null

    これで毎分クーロンが実行され、mailmanの監視されるようになります

    PHPのAPCの apc.include_once_override 設定時にいくつかの条件を満たした場合に include_once 等が失敗する現象

    12 6月, 2013 (16:19) | apache, php | By: hogehoge

    ttp://d.hatena.ne.jp/narusase/20121030/p1
    ttp://d.hatena.ne.jp/narusase/20121105/p1

    ↓の続き
    誤報 APC の apc.include_once_override で動的な include_once 等が失敗する問題
    ttp://d.hatena.ne.jp/narusase/20121030

    どうも同僚の調査によると、動的なinclude_onceが原因ではないようだ
    ・・・というわけで、極限まで単純化したサンプルコードを書いてテストしてみた。

    ファイルその1. /home/htdocs/test.php

    <?php
    function include_test($x = array()) {
    include_once('hogehoge.php');
    }
    echo '1'."\n";
    include_test();
    echo '2'."\n";
    include_test();
    echo 'end-'."\n";

    ファイルその2. /home/lib/hogehoge.php

    <?php
    class hogehoge {
    
    }
    
    or
    
    <?php
    const hogehoge = 0;

    ざっくりとわかっている条件を列挙する
    1.二つのファイルにわかれている必要がある(include_onceするので当然だが)
       さらに読みこまれるファイルがカレントディレクトリにある場合は起こらない(パス周辺の条件がある?)
    2.include_onceする対象は相対パス指定でないといけない(フルパス指定では起こらない)
       なお、当然だが、パスは通っている必要がある
    3.include_onceするされる相対はclass or constを記述する(functionでは起こらない)
       const のばあい 下記のNoticeになるがFATALではないので動きはする
         Notice: Constant hogehoge already defined in /home/lib/hogehoge.php on line 2
    4.関数内でinclude_onceしないと起こらない
    5.関数内には必ず引数が必要で array() で初期化されている(引数が無い、初期化の値がNULLだとおこらない)
       また、その引数にはコールする際いかなる値も渡してはならない

    つまり、5. の条件は不明だがそもそも関数内でinclude_onceされており、さらにパスがini_setなどによるパスの変化によって別ファイルが指定される可能性がある場合に起こっているようだ。

    ◆結論としては、発生条件は良く分からないが、下記の条件のいずれかを守れば「回避できそう」ということが分かりました。
    1.関数内でinclude_once or require_once を呼ばない。
    2.カレントディレクトリ以外のファイルの include_once or require_once はフルパスで指定する
    3.どうしても関数内でinclude_once or require_once を呼ぶ場合、引数の初期化を行わないか、必ずarray()以外(NULL推奨)で初期化する

    linux-fincoreを使ってページキャッシュを覗く

    17 5月, 2013 (15:15) | mysql | By: hogehoge

    http://yoku0825.blogspot.jp/2013/05/linux-fincore.html

    ページキャッシュにどれだけページが載っているかを調べたいなーと思うと、
    ファイルがページキャッシュに乗っているかどうかを調べる というのがよく引っかかって、
    そもそもfincoreってどっから出てきた名前なんだと思ったらlinux-ftoolsというところに行き着いた。

    linux-ftoolsはApacheライセンスで、PerlとINLINE Cで書かれたfincoreはGPLv2なんだけど、
    どっちが本家なんだかもともとオリジナルがあるのかよく判らない。

    取り敢えずlinux-ftoolsを落としてきてコンパイル。
    ダウンロード可能なtarballが存在しないので、hgとやらでcloneするしかなさそう。
    hgコマンドが入ってなかったので、mercurialパッケージを突っ込んでから(恥ずかしながらこれ知らなかった。。)

    $ sudo yum install -y mercurial
    $ hg clone https://code.google.com/p/linux-ftools/
    $ cd linux-fincore
    $ ./configure && make

    makeさえすればバイナリ単品で使い回せそうなので、make installはしない前提でconfigureオプションなし。

    $ ./linux-fincore /tmp/*
    filename size total_pages min_cached page cached_pages cached_size cached_perc
    -------- ---- ----------- --------------- ------------ ----------- -----------
    /tmp/access.log 191,010 47 -1 0 0 0.00
    /tmp/logs.sql.201305091141.gz 8,908 3 -1 0 0 0.00
    /tmp/logs.sql.201305131834 72,382 18 -1 0 0 0.00
    ---
    total cached size: 0

    うむ。-Lつけて縦表示にした方が見やすいかも。

    $ ./linux-fincore -L -s /tmp/*
    filename size total_pages min_cached page cached_pages cached_size cached_perc
    -------- ---- ----------- --------------- ------------ ----------- -----------
    /tmp/access.log
    size: 191,010
    total_pages: 47
    min_cached_page: -1
    cached: 0
    cached_size: 0
    cached_perc: 0.00
    /tmp/logs.sql.201305091141.gz
    size: 8,908
    total_pages: 3
    min_cached_page: -1
    cached: 0
    cached_size: 0
    cached_perc: 0.00
    /tmp/logs.sql.201305131834
    size: 72,382
    total_pages: 18
    min_cached_page: -1
    cached: 0
    cached_size: 0
    cached_perc: 0.00
    ---
    total cached size: 0

    さあ、これでmroonga載っててswapが鬼の様なサーバー覗いてこよう。

    MySQLのデータベースお引っ越し

    14 5月, 2013 (12:44) | mysql | By: hogehoge

    http://dev.classmethod.jp/server-side/db/moving-mysql-database/

    MySQLのデータベースのコピーとか、お引っ越しをしたい時、ありますよね。今AWS上で新規にMySQLを使いたいのであれば迷い無くRDSを選ぶと思いますが、まだRDSのmicroインスタンスが無かった頃に、microのEC2上に生でMySQLを建てた…とか、まぁ歴史上の理由は様々でしょう。
    まぁ「無停止で」とか言い出すと色々大変ですが、そうでなければ(計画停止をすれば)MySQLの引っ越しって実はかなり簡単です。
    まずは事前に出力先のMySQLの方はCREATE DATABASEを済ませておきます。CREATE DATABASE済みの場合は不要です。

    $ mysqladmin \
    -u'cmsampleuser' \
    -p'cmsamplepass' \
    -h'dest.hostname.example.com' \
    --default-character-set=utf8
    create dest_database

    あとは、アプリを止めて、mysqldumpとmysqlをパイプで繋いでドーンと一発です。

    $ mysqldump \
    --single-transaction \
    --no-autocommit \
    -u'cmsampleuser' \
    -p'cmsamplepass' \
    -h'source.hostname.example.com' \
    source_database \
    | mysql \
    -u'cmsampleuser' \
    -p'cmsamplepass' \
    -h'dest.hostname.example.com' \
    dest_database

    場合によっては、パイプで直結ではなく、ファイルを経由させれば、バックアップにもなりますね。

    $ mysqldump \
    --single-transaction \
    --no-autocommit \
    -u'cmsampleuser' \
    -p'cmsamplepass' \
    -h'source.hostname.example.com' \
    source_database \
    | gzip cmsampledb-dump.sql.gz
    $ gunzip -c cmsampledb-dump.sql.gz | mysql \
    -u'cmsampleuser' \
    -p'cmsamplepass' \
    -h'dest.hostname.example.com' \
    dest_database

    終わったら、アプリの接続先を新DBに切り替えて起動するだけですね。
    その他、お引っ越し用途だけでなく、ローカルの開発用DBのコピーを作る場合など、かなり応用は効くと思います。

    McAfeeのMySQL監査ツールを導入する!!

    2 5月, 2013 (10:49) | log | By: hogehoge

    http://www.s-quad.com/wordpress/?p=1609

    今まで、あまり気にしていなかったのですが、MySQLって監査(audit)の機能ってないのです。例えば、アプリケーション上のトラブルでデータが消えてしまった。原因がわからないなんていった場合の「いつ?」「だれか?」「何をした?」ってのを追跡したい場合ってどう対応しています?
    私的には、24時間365日で、クエリーログとっておけばいいんじゃない?とか思いつつ、馬鹿でかいファイルサイズになっちゃうから結局やらない。・・で結局、なんにもしてません状態だったのですが、MySQLでそういう機能があるのであれば使うつもりでした。。。つまり、MySQLにはそういう機能はありません。oracleとか、MS-SQLserverとかはあるのにね・・・
    と思ってたら、McAfeeから、MySQLの監査ツールを無償公開しておりました。知らなかった・・・
    というわけで、
    McAfeeのMySQL監査ツールを導入してみたいと思います。

    セットアップ
    プラグインファイルのダウンロード
    github上に公開されております。そこからダウンロードします。
    ttps://github.com/mcafee/mysql-audit/downloads

    # wget https://github.com/downloads/mcafee/mysql-audit/audit-plugin-mysql-5.5-1.0.3-371-linux-x86_64.zip

    MySQLの事前調査

    McAfeeのMySQL監査ツールは、MySQL5.1、5.5対応です。なので、ご自身のMySQLが対応しているかどうか事前に確認しておきます。併せて、インストール時にプラグインがローディングできているのかもこちらでは確認しております。

    ●バージョンの確認
    mysql> select @@version;
    +————+
    | @@version |
    +————+
    | 5.5.28-log |
    +————+

    ●プラグインディレクトリの確認
    mysql> show variables like ‘plugin_dir’;
    +—————+————————–+
    | Variable_name | Value |
    +—————+————————–+
    | plugin_dir | /usr/lib64/mysql/plugin/ |
    +—————+————————–+

    pluginディレクトリにファイルを格納
    解凍したファイルを所定のMySQLプラグインフォルダにコピーしてあげるだけです。

    # unzip audit-plugin-mysql-5.5-1.0.3-371-linux-x86_64.zip
    Archive: audit-plugin-mysql-5.5-1.0.3-371-linux-x86_64.zip
    creating: audit-plugin-mysql-5.5/
    creating: audit-plugin-mysql-5.5/lib/
    inflating: audit-plugin-mysql-5.5/lib/libaudit_plugin.so
    inflating: audit-plugin-mysql-5.5/COPYING
    inflating: audit-plugin-mysql-5.5/THIRDPARTY.txt
    inflating: audit-plugin-mysql-5.5/README.txt
    # cd audit-plugin-mysql-5.5/lib/
    # ls -l
    -rw-r--r-- 1 root root 737422 11月 14 17:52 libaudit_plugin.so
    # cp libaudit_plugin.so /usr/lib64/mysql/plugin/

    プラグインのインストール

    mysql> INSTALL PLUGIN AUDIT SONAME 'libaudit_plugin.so';
    Query OK, 0 rows affected (0.42 sec)

    プラグインインストール後の確認
    ●プラグインがローデイングされているか確認

    mysql> show plugins like 'AUDIT';
    +--------------------------+--------+--------------------+--------------------+---------+
    | Name | Status | Type | Library | License |
    +--------------------------+--------+--------------------+--------------------+---------+
    | AUDIT | ACTIVE | DAEMON | libaudit_plugin.so | GPL |
    +--------------------------+--------+--------------------+--------------------+---------+

    ●auditのパラメータが増えているか確認

    mysql> show global variables like 'audit%';
    +---------------------------------+----------------------------+
    | Variable_name | Value |
    +---------------------------------+----------------------------+
    | audit_checksum | |
    | audit_delay_cmds | |
    | audit_delay_ms | 0 |
    | audit_json_file | OFF |
    | audit_json_file_flush | OFF |
    | audit_json_file_sync | 0 |
    | audit_json_log_file | mysql-audit.json |
    | audit_json_socket | OFF |
    | audit_json_socket_name | /tmp/mysql-audit.json.sock |
    | audit_offsets | |
    | audit_offsets_by_version | ON |
    | audit_record_cmds | |
    | audit_record_objs | |
    | audit_uninstall_plugin | OFF |
    | audit_validate_checksum | ON |
    | audit_validate_offsets_extended | ON |
    +---------------------------------+----------------------------+

    設定
    ●パラメータ設定
    上記のパラメータを設定します。尚、設定内容はこちらを参照しました。
    今回は検証の為、ざっくり設定しております
    ttps://github.com/mcafee/mysql-audit/wiki/Configuration

    ●my.cnfの編集
    今回は、動作検証のでざっくりとした設定になっているのですが、実運用ではこのままの設定ですと結構なログの量になってしまいますので、audit_record_cmdsでコマンドは色々とフィルタはかけたほうが良いかと思います。監査としては、個人的には更新だけで良いのではないかなあ・・・とおもいます

    # vi /etc/my.cnf
    # ******************************************************
    # McAfee Audit-Plugin
    # ******************************************************
    plugin-load=AUDIT=libaudit_plugin.so# 監査ログの出力パスを指定
    audit_json_log_file=/var/log/mysql/mysqld-audit-log.json
    # 監査 ソケットファイルの使用(1:ON 0:OFF)
    audit_json_socket=0
    # 監査 ソケットファイルのパス指定
    audit_json_socket_name='/var/lib/mysql/mysql-audit.sock'
     
    # ログファイルに出力(1:出力 0:出力しない)
    audit_json_file=1
     
    # ログの同期タイミング(0:OSに従い、0以外: 秒???)
    audit_json_file_sync=0
     
    # 記録対象のコマンドを指定
    # show global status like 'Com_%'; にて、表示されるものが対象のコマンドだと思ってくれて良いです
    audit_record_cmds=insert,select,update,delete
     
    # 記録対象のスキーマまたは、テーブルを指定する
    # "{"の部分は、SET、FLUSH等の監査対象外をログに取りたい場合に指定
    # audit_record_objs=mydb.*,{}

    ●設定を反映させます
    MySQLの再起動をします

    # /etc/init.d/mysql restart

    ●設定後のパラメータの確認
    当たり前だけど、設定が変更されているとおもいます

    mysql> show global variables like '%audit%';
    +---------------------------------+--------------------------------------+
    | Variable_name | Value |
    +---------------------------------+--------------------------------------+
    | audit_checksum | |
    | audit_delay_cmds | |
    | audit_delay_ms | 0 |
    | audit_json_file | ON |
    | audit_json_file_flush | OFF |
    | audit_json_file_sync | 0 |
    | audit_json_log_file | /var/log/mysql/mysqld_audit_log.json |
    | audit_json_socket | OFF |
    | audit_json_socket_name | /var/lib/mysql/mysql-audit.sock |
    | audit_offsets | |
    | audit_offsets_by_version | ON |
    | audit_record_cmds | |
    | audit_record_objs | |
    | audit_uninstall_plugin | OFF |
    | audit_validate_checksum | ON |
    | audit_validate_offsets_extended | ON |
    +---------------------------------+--------------------------------------+

    ●補足:コマンドで直接実行の場合
    実際、リアルタイムで変更されるのかされないのかはわからないのです。(^_^;)でもエラーは出なかったので載せておきます。

    mysql>
    set global audit_json_file=1 ; -- ログをJSON形式で出力する
    Query OK, 0 rows affected (0.00 sec)set global audit_json_file_flush=1 ; -- ログローテートの設定、1を指定した時にローテートする
    Query OK, 0 rows affected (0.00 sec)
     
    set global audit_json_file_sync=0 ; -- ログファイルの同期(0:OSに従い 0以外:秒)
     
    mysql> set global audit_json_socket_name='/var/lib/mysql/mysql-audit.sock' ; -- socketファイルの指定
    Query OK, 0 rows affected (0.01 sec)

    動作確認
    ▼ MySQLでコマンドを実行
    ここは、あれです。単純に、auditログが書かれているかどうか確認したかっただけなので、好きなコマンドを打ってくださいませ。

    mysql> use mysql
    Reading table information for completion of table and column names
    You can turn off this feature to get a quicker startup with -A
     
    Database changed
    mysql> show tables;
    +---------------------------+
    | Tables_in_mysql |
    +---------------------------+
    | columns_priv |
    | db |
    | event |
    | func |
    | general_log |
    | help_category |
    | help_keyword |
    | help_relation |
    | help_topic |
    | host |
    | ndb_binlog_index |
    | plugin |
    | proc |
    | procs_priv |
    | proxies_priv |
    | servers |
    | slow_log |
    | tables_priv |
    | time_zone |
    | time_zone_leap_second |
    | time_zone_name |
    | time_zone_transition |
    | time_zone_transition_type |
    | user |
    +---------------------------+
     
    mysql> select * from plugin limit 10;
    +----------------------+--------------------+
    | name | dl |
    +----------------------+--------------------+
    | rpl_semi_sync_master | semisync_master.so |
    | rpl_semi_sync_slave | semisync_slave.so |
    | AUDIT | libaudit_plugin.so |
    +----------------------+--------------------+

    以下の通り、監査ログが出力されております。JSON形式なので、このデータを、fluentdで転送して、ログを一つのサーバに転送しちゃって監理するのに便利かもしれませんね。

    ▼ 監査ログの確認
    上記の監査ログが出力されているのがわかるかと思います。

    # cat /var/log/mysql/mysqld-audit-log.json
    {"msg-type":"activity","date":"1367401014808","thread-id":"1","query-id":"1","user":"myuser","priv_user":"myuser","host":"localhost","cmd":"select","query":"select @@version_comment limit 1"}
    {"msg-type":"activity","date":"1367401068086","thread-id":"2","query-id":"3","user":"myuser","priv_user":"myuser","host":"localhost","cmd":"select","query":"select @@version_comment limit 1"}
    {"msg-type":"activity","date":"1367401092429","thread-id":"2","query-id":"5","user":"myuser","priv_user":"myuser","host":"localhost","cmd":"select","query":"SELECT DATABASE()"}
    {"msg-type":"activity","date":"1367401145285","thread-id":"3","query-id":"88","user":"myuser","priv_user":"myuser","host":"localhost","cmd":"select","query":"select @@version_comment limit 1"}
    {"msg-type":"activity","date":"1367401266863","thread-id":"1","query-id":"1","user":"myuser","priv_user":"myuser","host":"localhost","cmd":"select","query":"select @@version_comment limit 1"}
    {"msg-type":"activity","date":"1367401350586","thread-id":"1","query-id":"1","user":"myuser","priv_user":"myuser","host":"localhost","cmd":"select","query":"select @@version_comment limit 1"}
    {"msg-type":"activity","date":"1367401354071","thread-id":"1","query-id":"2","user":"myuser","priv_user":"myuser","host":"localhost","cmd":"select","query":"SELECT DATABASE()"}
    {"msg-type":"activity","date":"1367401385511","thread-id":"1","query-id":"31","user":"myuser","priv_user":"myuser","host":"localhost","cmd":"select","objects":[{"db":"mysql","name":"plugin","obj_type":"TABLE"}],"query":"select * from plugin limit 10"}

    とまあ、例として適切だったかどうかはわかりませんが、いかがでしょうか?これであれば、DBサーバに過剰な負荷を欠けずに、監査することが可能かと思います。便利!!

    MySQLでデータ領域をシステムと別diskにするならtmpdirも設定した方がいい

    25 4月, 2013 (13:08) | tuuning | By: hogehoge

    http://d.hatena.ne.jp/sfujiwara/20130424/1366777066

    ということで、Percona-Toolkit の pt-ioprofile を教えていただいたので試してみました。

    日々の覚書: pt-ioprofileでMySQLのテンポラリテーブルのサイズを測る

    結果。ばっちり /tmp を使っていました。

    $ sudo pt-ioprofile --cell sizes --run-time 60
    2013年 4月 24日 水曜日 11:06:23 JST
    Tracing process ID 22049
    total pread pwrite filename
    1309671424 0 1309671424 /tmp/ibQo90KP
    1308622848 1308622848 0 /tmp/ibGwKRu3

    /data/tmp を作成し、my.cnf で tmpdir を指定することで、MySQLにそちらを使用させるよう変更し ALTER 再実行したところ、無事 SSDのほうを使ってくれるようになりました。

    # mkdir /data/tmp
    # chmod 1777 /data/tmp
    [mysqld]
    tmpdir = /data/tmp

    /tmp が十分に高速な場合はデータと別にしておくことでパフォーマンス向上が見込める可能性はあると思いますが、今回は /tmp が低速だったので SSD 上に置いてしまうほうがいいですね。

    [記事 add]

    MySQL5.6 ALTER TABLE中のtmpdirのファイル

    http://yoku0825.blogspot.jp/2013/04/mysql56-alter-tabletmpdir.html

    もともとの話は、MySQL5.6で巨大テーブルのALTER TABLE中に
    tmpdirに何かI/Oがあるせいで遅くなってる、という話で、
    ⇒MySQLでデータ領域をシステムと別diskにするならtmpdirも設定した方がいい – 酒日記 はてな支店

    というか、でも、

    SH2 @sh2nd
    During an online DDL operation that copies the table, files are written to the temporary directory. http://ow.ly/kmDJ1 って書いてあったけど待ちます!

    Σ(゚д゚lll) 結論出てるじゃないですか!?

    はい、調べます。

    1) ALTER TABLEが走るとALGORITHM = INPLACEかどうか、INPLACEでALTER TABLEできるかどうかの判定が走る(sql/sql_table.cc:mysql_inplace_alter_table)

    2) もろもろの関数を経て、InnoDBのOnline ALTER TABLEにたどりつく(storage/innobase/handler/handler0alter.cc:ha_innobase::inplace_alter_table)

    3) Online ALTER TABLE用のtemporary merge fileが作られる。DDL中の更新差分をココに閉じ込めて、後からテンポラリテーブルにマージする動き。内部的にはMySQL内共通のテンポラリファイル作る関数を、ファイル名の先頭”ib”で呼んでる。(storage/innobase/row/row0log.cc:row_log_table_open)

    4) DDL中の更新差分は、InnoDBのINSERT, UPDATE, DELETE用APIの中でOnline ALTER TABLE中かどうかを判定して、
    フラグが立ってたらさっきのテンポラリファイルにデータを突っ込む関数が呼ばれる(storage/innobase/row/row0ins.ccとか)

    if (err == DB_SUCCESS && dict_index_is_online_ddl(index)) {
    row_log_table_insert(rec, index, offsets);
    }

    こんなかんじ。

    5) ALTER TABLE中の面倒は(storage/innobase/row/row0log.cc:row_log_online_op)が見ているぽくて、

    if (byte_offset + srv_sort_buf_size >= srv_online_max_size) {
    goto write_failed;
    }

    ファイルの末尾+sort_buffer_size(ソートバッファサイズごとに処理してるぽいので、
    次の1回分くらいしか残りがない、という感じ?)がinnodb_online_alter_log_max_sizeに達するとエラーで抜けるようになってる。

    6) ALTER TABLEの終了後に、テンポラリファイルからINSERT, UPDATE, DELETEの差分をマージする(storage/innobase/row/row0log.cc:row_log_table_apply_*)

    こんな感じでしょうか。5) はちょっと自信無い…。

    InnoDBのロックは行ロックじゃなくてインデックス行ロックだからね、という例

    24 4月, 2013 (17:25) | InnoDB | By: hogehoge

    http://yoku0825.blogspot.jp/2013/04/innodb.html

    mysql1> CREATE DATABASE d1;
    mysql1> USE d1;
    mysql1> CREATE TABLE t1 (num SERIAL, val VARCHAR(32), KEY (val(1))) ENGINE = InnoDB;
    mysql1> INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three');
    mysql1> START TRANSACTION;
    mysql1> SELECT * FROM t1 WHERE val = 'two' FOR UPDATE;
    
    mysql2> SELECT * FROM t1 WHERE num = 3 LOCK IN SHARE MODE;

    valの先頭1文字でインデックスになっているので、”[t]wo”のロックは”[t]hree”も一緒にロックする。
    よってnum = 3のロックは不可能。

    mysql_install_db

    11 4月, 2013 (10:51) | mysql | By: hogehoge

    http://kowloonet.org/memo/tech/index.php?MySQL%A5%C7%A1%BC%A5%BF%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8%A5%EA%A4%CE%CA%D1%B9%B9

    ※datadir以下が初期化(旧データ削除)されない為、別ディレクトリにて対応

    ・一時dataディレクトリ作成

    #mkdir -p /tmp/data/mysql
    #chown mysql: -R /tmp/data/

    ・初期化スクリプト実行

    #mysql_install_db --user=mysql --datadir=/tmp/data/mysql

    ・データ入れ替え

    #/etc/init.d/mysqld stop
    #mv /var/lib/mysql{,.org}
    #mv /tmp/data/mysql/ /var/lib/mysql/
    #chown mysql: -R /var/lib/mysql/
    #/etc/init.d/mysqld start

    ※エラーが発生し、起動不可の場合、ログ確認

    # less /var/log/mysqld.log
    /usr/libexec/mysqld: Can't change dir to '/var/lib/mysql/' (Errcode: 13)

    ググった。
    MySQL の datadir を標準以外に変更すると起動できなくなる場合
    ttp://network.station.ez-net.jp/os/linux/db/mysql/error/datadir/centos/5.4.asp

    # getenforce
    Enforcing
    # setenforce 0
    # getenforce
    Permissive

    ・再起動

    # /etc/init.d/mysqld restart

    # mysql

    無事起動した。

    Passengerとmod_dir

    25 3月, 2013 (15:07) | apache | By: hogehoge

    http://higelog.brassworks.jp/?p=2180

    ApacheでPassengerを有効にするとmod_dirが動かなくなる。
    つまりディレクトリにアクセスした際に末尾のスラッシュを忘れると404が発生。

    これはPassengerのバグではなく意図された仕様のようだ。

    とりあえずそのサーバーでRailsしか動かしていないのなら問題ないのだけれど、その他のバーチャルホストで静的サイトやPHPで作成したアプリなどを置いている場合はmod_dirが効かないことで微妙に404が発生するようになる。
    末尾のスラッシュというものはリンクが拡散する途中でまま抜け落ちるものらしい。

    というわけで他のサイトが動いているサーバーにPassengerを載せる場合は基本PassengerEnabledをoffにした方がよい。
    httpd.confのグローバルコンテキストに一行追加。

    PassengerEnabled off

    Railsを動かすバーチャルホストではonとする。

    PassengerEnabled on

    逆にすでにPassengerで色々アプリが動いているサーバーに静的サイトを乗せるなら、そのバーチャルホストにだけPassengerEnabled offを設定することもできる。

    またRailsのpublic以下に静的コンテンツを置いている場合は、その部分ではPassengerEnabled offしておいた方が良い。

    例えばpublic/some_static_contentに何か置いているなら以下のようにする。

    <Location /some_static_content>
    PassengerEnabled off
    </Location>

    バイナリログやスローログを見やすくするワンライナー

    15 3月, 2013 (14:23) | replication | By: hogehoge

    http://yoku0825.blogspot.jp/2013/03/blog-post.html

    バイナリログやスローログを遡って更新履歴をチェックしたりする時に(個人的に)面倒なのが、
    `こいつらはSQLステートメントの改行を反映するし、インデントもしっかり記録してくれる’こと。

    grep -v “INSERT INTO t1″とかやっても、”VALUES”の手前で改行してインデントするような
    小綺麗に書かれたコードだと上手く引っ掛けにくい。
    個人的にはインデントあった方が好きだけど、大量のログを見る時には不向き。

    なので1分で考えたワンライナー。
    # perl -e '$/="#"; while(<>) { s/[ \n\r\t]{2,}/ /g; print $_;}' | egrep -iv '^SET (TIMESTAMP|.+session\.|INSERT_ID)|^/\*|^BEGIN|^COMMIT|^DELIMITER' | grep '^[^#]' -B1
    これで連続する改行なり空白なりを全部まとめて単独のスペース文字に変換できる。
    grepでもawkでも自由自在。

    ついでに、いつものSET TIMESTAMPやBEGIN, COMMITを弾いた上で、
    コメント行以外の行とその直前のコメント行を引っこ抜いてくる。
    スローログだったら-B2にすると良いかと。

    TOPコマンドをcronで動かす

    12 3月, 2013 (11:50) | process | By: hogehoge

    ■ ワンライナー版
    cronの実行間隔:任意
    実行コマンド: dateコマンドを実行後、topコマンドを5秒毎に3回実行した結果を /tmpへ保存する。
    36 11 * * * export TERM=vt100; /bin/date >> /tmp/top.`date +\%H\%M`.txt; /usr/bin/top -b -n 3 -d 5 >> /tmp/top.`date +\%H\%M`.txt 2>&1

    ■ SHELL版
    #!/bin/bash
    FILENM=top.log
    (
    date
    top -b -n 1
    ) > ${FILENM}