« August 2010 | Main | November 2010 »

October 28, 2010

WEB上からapacheの生ログを見ることができるようにするプログラム

apache生ログをWEB上から見たいという要望を受けて簡単にPHPで作ってみましたのでご紹介致します。突っ込みどころ満載かと思いますが、その場合はコメントに記載いただけましたら幸いです。改善的コメント大歓迎です。

※重大な警告:この手のスクリプトは重大なセキュリティホールを招く場合がありますので、もし実利用される場合は十分ご注意ください。


【プログラムの概要】
今回作成したプログラムでは、特定ディレクトリ内のファイルリストを取得して一覧を出します。そして一覧の中から特定ファイルをクリックするとそのファイルの最後10行(もしくは指定行数)を表示するというものです。例えばこんな感じです。

web1 top>
tail -

access_log 38543
access_log 5435
access_log 23533
error_log 10206
error_log 236
error_log 1256


【プログラム】
これを実現するプログラムは以下の通りです。特に難しいことはやってないので容易に理解できるかと思います。(赤字の部分を直して使ってください)

■logview.php

<?php
$scripturl = "http://www.example.com/admin/logview.php";
$basedir = "/var/log/httpd";

global $f_cmd;
global $f_filename;
$f_numlist = 10;
if (isset($_GET["c"])) $f_cmd = htmlspecialchars(@$_GET["c"], ENT_QUOTES);
if (isset($_GET["f"])) $f_filename = htmlspecialchars(@$_GET["f"], ENT_QUOTES);
if (isset($_GET["n"])) $f_numlist = htmlspecialchars(@$_GET["n"], ENT_QUOTES);

if (strpos($f_filename, "..") !== FALSE) {
     print "You can't use '..'.";
     exit;
}

print `/bin/hostname` . "<a href='$scripturl'>top></a><br />";
?>

<form method="GET" action="viewlog.php">
  <input type="hidden" name="c" value="<?=$f_cmd?>" />
  <input type="hidden" name="f" value="<?=$f_filename?>" />
  tail -<input type="text" name="n" size=3 value="<?=$f_numlist?>" />
  <input type="submit" />
</form>

<pre>
<?php
//--------------------
//ログ一覧表示
//--------------------
if ($f_cmd == "") {
    $lists = `ls -lb  $basedir/*log* |awk '{print $9,$5}'`;
    $arr_lists = split("\n", $lists);
    foreach ($arr_lists as $list) {
        $list = str_replace($basedir."/", "", $list);
        $arr_list = split(" ", $list);
        if ($arr_list[1] != 0) {
            $arr_list_new = str_replace("error", "<font color=red>error</font>", $arr_list[0]);
            print "<a href='$scripturl?c=p&f=$arr_list[0]'>$arr_list_new</a> $arr_list[1]<br />";
        }
    }
}

//--------------------
//ログ表示
//--------------------
if ($f_cmd == "p" && !empty($f_filename)) {
    system("tail -$f_numlist $basedir/$f_filename");
}
?>
</pre>
</html>

| | Comments (3) | TrackBack (1)

October 26, 2010

サーバ間の設定ファイル差異を比較するスクリプト

サーバ台数が増えてくると、例えば同じ種類のサーバをスケールアウトしているとき、1台だけ設定ミスがあったとしてもなかなか気付きづらくなります。サービスの安定稼動のためにはこういった些細なミスは完全に潰しておく必要があります。そこで今回はサーバ間の設定ファイル差異を比較するスクリプトを作りましたのでご紹介してみます。


【前準備】
まずはこちらでも記したような方法で、パスワードなしで各サーバにSSH接続できるようにしておいてください。

【サーバ間の設定ファイル差異を比較するスクリプト】
下記スクリプトをどこかに保存してください。ファイル名は便宜上「filediff.sh」とでもしておきます。そして、今回比較するファイル名を羅列し、かつサーバ番号(下記の例だと10.0.0.1の設定ファイルを10.0.0.2~10の設定ファイルと比較しています)を書き換えてください。

$ cat > filediff.sh

~スクリプトの内容をペースト後、Ctrl+C~

$ chmod gou+x filediff.sh
$ vim filediff.sh

■filediff.sh

#!/bin/bash

FILES[0]="/etc/httpd/conf/httpd.conf"
FILES[1]="/etc/php.ini"
FILES[2]="/etc/my.cnf"

SVR_FROM=2 # サーバ番号始まり
SVR_TO=10 # サーバ番号終り

#
for file in ${FILES[@]};do
  echo "**************************************************"
  echo $file
  echo "**************************************************"
  scp 10.0.0.1:${file} /tmp/_a
  for i in `seq ${SVR_FROM} ${SVR_TO}`;
  do
    echo "[10.0.0.${i}]"
    scp 10.0.0.${i}:${file} /tmp/_b
    diff /tmp/_a /tmp/_b
    rm -f /tmp/_b
  done
  rm -f /tmp/_a
done

# 後処理
unset SVR_FROM
unset SVR_TO
unset FILES


【実行方法】

以下のように実行します。

$ filediff.sh

差異があると下記のように表示されます。

[10.105.4.33]
103c103
< ServerName web1:80
---
> ServerName web2:80


※緊急お知らせ
今週4~6名くらいで技術者交流会という名の飲み会を行なうことになったのですが、メンバーが全然集まっていません。当然面識がなくても構いませんのでどなたか飲みませんか? 参加可能な方は調整さんにエントリーお願い致します!
・・・ということで大変ありがとうございます。4名集まりましたので、10/27(水) 19:00 JR五反田駅改札を出たところ集合でいきましょう。多少であれば飛び入り参加もきっと大丈夫ですのでよろしくお願いいたしまーす。・・・ということで開催してきました。とても盛り上がって楽しかったですね。それにしても皆さん経験豊富だなあ、頑張らないとなあと思いました。またやりましょうね。

[調整さんにエントリー]

●開催趣旨:居酒屋土間土間で1時間525円飲み放題らしいので、たくさん飲みましょうというただそれだけになります。^^

| | Comments (0) | TrackBack (0)

October 24, 2010

WEB上からrsyncを実行する方法

各サーバ群にソースコードを配信するためにrsyncを使っているところも多いと思います。この操作をWEB上から行えるようになれば、例えばデザイナーがちょっとした画像データを配信するといった場面などで便利そうです。そこで今回はrsyncをWEB上から実行する方法について記してみたいと思います。


【ハマりどころ確認】
WEB上からrsyncを実行するのって、簡単に見えて実はちょっと難しいのです。ハマりどころは2つあります。それは

  (1) rsyncを実行するユーザがパスワードなしで配信先サーバにSSH接続できるようにしておかなければならない。

  (2) 配信先サーバでは、rsyncを実行するユーザと同じ名前のユーザで、該当ファイルやディレクトリの書き込み/編集権限を持っていなければならない。

というものです。

(1)については、こちらでも記したような方法で、パスワードなしで他のサーバにSSH接続できるようにしておかなければなりません。ただ厄介なのは、CentOSでyumを使ってapacheをインストールした場合、初期状態のままだとapacheユーザがnologinユーザ(すなわちログインできないユーザ)として登録されているので、これをログインできるユーザに直すところからはじめなければなりません。(注:もしくはapache起動ユーザを変えるという手もあります)

(2)については、配信先サーバ側でもapacheユーザとして該当ファイルやディレクトリに書き込みしに行きますので、パーミッション設定を行わなければなりません。方法としては、ファイルやディレクトリの所有者をapacheユーザにするか、もしくはファイルやディレクトリの所属グループをapacheにした上で、グループの書き込み権限を「w」にする方法があります。今回は後者の方法を採用しました。

ということを踏まえて、以降具体的な設定方法を記していきます。手順は面倒ですが、ほぼコピー&ペーストでいけるので作業自体はそんなに大変ではないかもしれません。


【配信元サーバ側の作業】
OSのapacheユーザがnologinになっているので、通常はapacheユーザでログインできませんが、今回は特別にapacheユーザでもログインできるようにします。(/etc/passwdのapacheユーザの最後の/sbin/nologinを/bin/bashにします)

# sed -i "s/apache:x:48:48:Apache:\/var\/www:\/sbin\/nologin/apache:x:48:48:
Apache:\/var\/www:\/bin\/bash/" /etc/passwd

apacheユーザの秘密鍵と公開鍵を置く場所を作ります。

# mkdir /var/www/.ssh
# chmod 700 /var/www/.ssh
# chown apache.apache /var/www/.ssh

apacheユーザの秘密鍵と公開鍵を作ります。

# su - apache

$ ssh-keygen -t rsa (とりあえず全部Enterで)

すると、秘密鍵(id_rsa)と公開鍵(id.rsa.pub)が生成されるので、公開鍵(id_rsa.pub)の中身をメモ帳などにコピーしておきます。(あとでペーストします。)

$ cat ~apache/.ssh/id_rsa.pub


【各配信先サーバ側の作業】
先程コピーした公開鍵をペーストしつつ、下記のスクリプトを実行します。特に一番最後の行がとても重要です。wwwディレクトリ配下をapacheユーザでも更新できるようにグループ変更を行っています。

# sed -i "s/apache:x:48:48:Apache:\/var
\/www:\/sbin\/nologin/apache:x:48:48:Apache:\/var\/www:\/bin\/bash/" /etc/passwd

# mkdir /var/www/.ssh
# chmod 700 /var/www/.ssh
# chown apache.apache /var/www/.ssh

# echo "ssh-rsa ABCDB3NzaC1yc2EAAAABIwAAAQEAuN0ZaKuttD5/XZna/B2vMKBe1VhuWu/mBqGoYncjkVM
qaWMBRyK1qI6sE4+2k2HGdJa3/RFQu5TMo1f3nxHpvM90wUMoyuTpu2b88WeWZ+ZZxjtS8E
ML5OGsnPBXJ0FMGhuIwQekIBWXiQ7VdfBEIMITngNqzhu+X3dZJgutaJudWyM0j4FZA0b9+
tVsTJw2Kz28r4HwR6Gkcl4vXAo4e4HS8Y7WlabTVXJIkKwN1VqfC/jMohXqUS3SkzMxpS0C
gII0Nmj0PNl+F7zPhJEpsVZbBP9vIYeOKzsKVnD3HWHeeW/QAwVguW8DG1xv4edTR5bwTHS
He3u4UVazwxaSFG== apache@web1
" >> /var/www/authorized_keys

# chown apache.apache /var/www/.ssh/authorized_keys
# chmod 600 /var/www/.ssh/authorized_keys

# chgrp -R apache /var/www/html ←超重要!

【rsync用スクリプトの準備】
細かくは説明しませんが、例えば以下のようなrsync用スクリプトを用意しておきます。

#!/bin/sh
servers[0]=10.0.0.1
servers[1]=10.0.0.2
servers[2]=10.0.0.3
for server in ${servers[@]};
  rsync -avz /var/www/html ${server}:/var/www/
done

【phpプログラムの準備】
そして最後に以下のようなphpプログラムを用意すれば、やっとWEB上からrsyncを実行できるようになります。

<?php
if (@$_GET["c"], ENT_QUOTES == "r") {
    system("/home/miracle/bin/myrsync.sh");
}
?>
<a href="rsync.php?c=r">[rsync]</a>

| | Comments (0) | TrackBack (0)

October 23, 2010

パスワードなしで他のサーバにSSH接続する方法

サーバ台数が増えてくると、サーバ間移動のたびにパスワード入力が求められるのが手間になってきます。これは何とかしたいところです。そこで今回はパスワードなしで他のサーバにSSH接続する方法を記していきます。

※今回、他のサーバにログインしようとするサーバを「admin」、ログインされるサーバを「web1」、対象ユーザを「user」としておきます。

【admin側の作業】
まずは秘密鍵と公開鍵を作ります。

$ su - user
$ ssh-keygen -t rsa (とりあえず全部Enterで)

すると、秘密鍵(id_rsa)と公開鍵(id.rsa.pub)が生成されるので、公開鍵(id_rsa.pub)だけを「web1」にコピーします。

$ scp /home/user/.ssh/id_rsa.pub user@web1:/home/user/.ssh/id_rsa.pub_admin


【web1側の作業】
adminからコピーされてきた公開鍵を「authorized_keys」というファイルに追加します。

$ su - user
$ cd .ssh
$ cat id_rsa.pub_admin >> authorized_keys
$ chmod 600 authorized_keys

すると、adminからweb1にパスワードなしでSSH接続できるようになります。

[user@admin ~] $ ssh web1

Last login: Sat Oct 23 00:42:19 2010 from admin
[user@web1 ~] $


ただ、この方法だとadminからログインされる全てのサーバにadminの公開鍵を配布するのが面倒なので、私は以下のようなスクリプトをログインされる側のサーバ上で実行して済ませています。

$ echo "ssh-rsa ABCDB3NzaC5yc2EAAAABIwAAAIEA0AIDjalANyx09R5KoWCBRheartsNMNmli95CBQ1eBhzZGEQRdHqyS4I9v/5qgKMsrgruhNMBlGUUrop19ALwodHB852vWvAZG3gReu2c3CbP9qGlA0mTG7nYUCWU0ggFwq1b+HAA5wF/BqkSvWXlTe+9G0/8nmgqZMzKr81fL2u8= user@web1" > /home/user/.ssh/id_rsa.pub_admin

$ chown user.user /home/user/.ssh/id_rsa.pub_admin
$ cat /home/user/.ssh/id_rsa.pub_admin >> /home/user/.ssh/authorized_keys
$ chmod 600 /home/user/.ssh/id_rsa.pub_admin
$ chmod 600 /home/user/.ssh/authorized_keys

| | Comments (0) | TrackBack (0)

October 18, 2010

サーバリソース監視ツール「munin」をCentOSで手軽に導入する方法

サーバリソース監視をする際、MTRG、Cacti、Nagiosなどを使う場合が多いですが、SNMPベースのツールは設定が面倒です。

そこで今回はSNMPを使わないお手軽サーバリソース監視ツールとしてmuninを手軽に導入する方法について記してみたいと思います。自動インストールスクリプト付きです。


【muninを使う前に最低限知っておいたほうが良いこと】
muninでは、監視用WEBサーバに入れるパッケージ「munin」と監視対象の各ノードに入れる「munin-node」があります。ただし監視用WEBサーバには「munin」だけでなく「munin-node」も入れなければなりません。


【監視用WEBサーバでの設定】
監視用WEBサーバ上では以下のスクリプトをroot権限で実行してみてください。(myhostnameのところだけそのサーバのホスト名に直した方がよいでしょう)

cd /root/Packages rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm

echo "[dag]" > /etc/yum.repos.d/dag.repo

echo "name=Dag RPM Repository for Red Hat Enterprise Linux" >> /etc/yum.repos.d/dag.repo

echo "baseurl=http://ftp.riken.jp/Linux/dag/redhat/el4/en/\$basearch/dag/" >> /etc/yum.repos.d/dag.repo

echo "gpgcheck=1" >> /etc/yum.repos.d/dag.repo

echo "gpgkey=http://dag.wieers.com/rpm/packages/RPM-GPG-KEY.dag.txt" >> /etc/yum.repos.d/dag.repo

echo "enabled=1" >> /etc/yum.repos.d/dag.repo

yum -y --enablerepo=dag install munin
yum -y --enablerepo=dag install munin-node

sed -i 's/enabled=1/enabled=0/' /etc/yum.repos.d/dag.repo

mkdir /var/www/munin
mkdir /var/www/munin/htmldir

chown munin.munin /var/www/munin/htmldir
chmod 755 /var/www/munin/htmldir

# munin.conf htmldir変更(9行目)
sed -i 's/# htmldir/htmldir/' /etc/munin/munin.conf

# munin.conf ホスト名に変更(60行目)
sed -i 's/localhost/myhostname/' /etc/munin/munin.conf

# munin-node.conf ホスト名指定(29行目)
sed -i 's/#host_name x86-19.phx2.fedoraproject.org/host_name myhostname/' /etc/munin/munin-node.conf

service munin-node start
chkconfig munin-node on


【各ノードでの設定】
各ノードでは以下のスクリプトをroot権限で実行してみてください。(myhostnameのところをそのサーバのホスト名に直すことと、監視サーバのIP指定部分だけ変更します)

cd /root/Packages rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm

echo "[dag]" > /etc/yum.repos.d/dag.repo

echo "name=Dag RPM Repository for Red Hat Enterprise Linux" >> /etc/yum.repos.d/dag.repo

echo "baseurl=http://ftp.riken.jp/Linux/dag/redhat/el4/en/\$basearch/dag/" >> /etc/yum.repos.d/dag.repo

echo "gpgcheck=1" >> /etc/yum.repos.d/dag.repo

echo "gpgkey=http://dag.wieers.com/rpm/packages/RPM-GPG-KEY.dag.txt" >> /etc/yum.repos.d/dag.repo

echo "enabled=1" >> /etc/yum.repos.d/dag.repo

yum -y --enablerepo=dag install munin-node

sed -i 's/enabled=1/enabled=0/' /etc/yum.repos.d/dag.repo

# munin-node.conf ホスト名指定(29行目)
sed -i 's/#host_name x86-19.phx2.fedoraproject.org/myhostname/' /etc/munin/munin-node.conf

# munin-node.conf 監視サーバからのアクセス許可(36行目)
sed -i 's/127\\.0\\.0\\.1\$/192\\.168\\.0\\.11\$/' /etc/munin/munin-node.conf

service munin-node start
chkconfig munin-node on


【動作確認】
WEBブラウザー上で以下のようなURLを開いてみてください。

http://monitoring.example.com/munin/

うまくいけば以下のような画面にたどり着きます。うまくいかなかった場合はapacheの設定を見直してみてください。

Munin


【監視項目の追加削除を行なう】
muninには監視項目をプラグインで追加削除できます。プラグイン追加といってもリンクを張るだけです。ここでは試しにApacheとMySQLを監視項目に加え、かつsendmailとntpを監視項目から外してみます。各ノード側で以下のスクリプトを実行してみてください。

ln -s /usr/share/munin/plugins/apache_accesses /etc/munin/plugins/apache_accesses

ln -s /usr/share/munin/plugins/apache_processes /etc/munin/plugins/apache_processes

ln -s /usr/share/munin/plugins/apache_volume /etc/munin/plugins/apache_volume

ln -s /usr/share/munin/plugins/mysql_bytes /etc/munin/plugins/mysql_bytes

ln -s /usr/share/munin/plugins/mysql_queries /etc/munin/plugins/mysql_queries

ln -s /usr/share/munin/plugins/mysql_threads /etc/munin/plugins/mysql_threads

rm -f /etc/munin/plugins/sendmail*

rm -f /etc/munin/plugins/ntp*

ただしこのままだと各ノードのApacheが必要な情報(server-status)を吐き出してくれないので、Apacheにも設定追加を行ないます。

echo "" >> /etc/httpd/conf/httpd.conf echo "ExtendedStatus On" >> /etc/httpd/conf/httpd.conf

echo "" >> /etc/httpd/conf/httpd.conf

echo "SetHandler server-status" >> /etc/httpd/conf/httpd.conf

echo "order deny,allow" >> /etc/httpd/conf/httpd.conf

echo "deny from all" >> /etc/httpd/conf/httpd.conf

echo "allow from 127.0.0.1" >> /etc/httpd/conf/httpd.conf

echo "" >> /etc/httpd/conf/httpd.conf

sed -i 's/#LoadModule status_module modules\/mod_status.so/LoadModule status_module modules\/mod_status.so/' /etc/httpd/conf/httpd.conf

service httpd graceful


【最後に】
以上のように、muninの導入は手軽なので非常に便利ですが、かゆいところに手が届かない気がするのも事実です。ただ本家のFAQは英語だけど割と親切なので一度目を通してみることをお勧めします。例えば集計間隔を5分以外に変える場合は /etc/cron.d/munin を編集してね、みたいなことが書いてあります。

| | Comments (0) | TrackBack (0)

October 12, 2010

急いでいる人のためのMySQLのユーザ権限付与講座

MySQLでは細かいレベルの権限付与が可能ですが、大抵の場合そこまで細かいレベルの権限付与は必要ないですよね? マニュアルを読まないか、もしくはちょっとしたメモ書きを見る程度でおおよそ使い方が理解できるくらいならいいのに、といつも思います。

そこで今回は、MySQLのユーザ権限付与の中でも、とりわけよく行われる手順だけを簡単にまとめてみました。

【まず知っておいたほうが良いこと】
ユーザはmysqlデータベース内のuserテーブルに作られます。

次に例えば以下のユーザの情報を見てみると「_priv」系のカラム値が全部「Y」であることがわかります。この場合はグローバルレベル権限として全部「Y」なので、全てのテーブルに対して接続が可能となります。

mysql> select * from user where User='adminuser' \G
 
*************************** 1. row ***************************
                 Host: %
                 User: adminuser
             Password: 136b4c537575b6f1
          Select_priv: Y      ←
          Insert_priv: Y      ←
          Update_priv: Y      ←
 
~~~中略~~~
 
  Create_routine_priv: Y      ←
   Alter_routine_priv: Y      ←
     Create_user_priv: Y      ←
             ssl_type:
           ssl_cipher:
          x509_issuer:
         x509_subject:
        max_questions: 0
          max_updates: 0
      max_connections: 0
 max_user_connections: 0
1 row in set (0.00 sec)

一方、以下のユーザの情報を見てみると「_priv」系のカラム値が全部「N」であることがわかります。

mysql> select * from user where User='testuser' \G
  *************************** 1. row ***************************
                 Host: localhost
                 User: testuser
             Password: 136b4c537575b6f1
          Select_priv: N      ←
          Insert_priv: N      ←
          Update_priv: N      ←
 
~~~中略~~~
 
  Create_routine_priv: N      ←
   Alter_routine_priv: N      ←
     Create_user_priv: N      ←
             ssl_type:
           ssl_cipher:
          x509_issuer:
         x509_subject:
        max_questions: 0
          max_updates: 0
      max_connections: 0
 max_user_connections: 0
1 row in set (0.00 sec)

この場合は、別途ユーザが接続するデータベースを「db」テーブルに設定します。

mysql> select * from db where User='testuser' \G
 
*************************** 1. row ***************************
                 Host: localhost
                   Db: testdatabase
                 User: testuser
          Select_priv: Y
          Insert_priv: Y
          Update_priv: Y
 
~~~中略~~~
 
  Create_routine_priv: Y
   Alter_routine_priv: Y
         Execute_priv: Y
1 row in set (0.00 sec)

普段意識すべきはこのあたりまでだと思いますが、さらに細かい設定を検討する場合は以下もご参照下さい。

※参照:mysqlデータベース内のテーブル一覧

データベース名 説明
user グローバルレベルの権限とパスワードを管理するテーブル
db データベースレベルの権限を管理するテーブル
host(今回は考えない) dbテーブルにホスト名が指定されていない場合に適用される権限を管理するためのテーブル
tables_priv(今回は考えない) テーブルレベルの権限を管理するテーブル。テーブル内のすべてのフィールドに適用される権限について格納。
columns_priv(今回は考えない) フィールドレベルの権限を管理するテーブル。テーブル内の一つのフィールドに適用される権限について格納。

これをふまえて。


【1.管理者用ユーザを作りたい】
いわゆる何でもできる管理者用ユーザは以下の要領で作ります。

GRANT ALL ON *.* TO adminuser@'%' IDENTIFIED BY 'password' WITH GRANT OPTION;

※MySQLにはGRANT権限というものがあります。GRANT権限とは、他のユーザに対して権限を付与することができる権限のことです。当然のことながらGRANT権限は通常管理者用ユーザにか付与しません。

IP制限をかける場合は以下の要領です。

GRANT ALL ON *.* TO adminuser@'172.16.0.0/255.255.255.0' IDENTIFIED BY 'password' WITH GRANT OPTION;


【2.一般ユーザを作りたい】
全てのデータベースにアクセスできる一般ユーザは以下の要領で作ります(WITH GRANT OPTIONがないのに注目)。ただし全てのデータベースにアクセスできる一般ユーザをGRANT ALL ON *.*で作ると、SUPER権限がつくのでinit_connectが無視されるなどの副作用があるそうです。(sh2さんご指摘ありがとうございました)

GRANT ALL ON *.* TO testuser@'%' IDENTIFIED BY 'password';

特定のデータベースにアクセスできる一般ユーザは以下の要領で作ります。

GRANT ALL ON testdatabase.* TO testuser@'%' IDENTIFIED BY 'password';

SELECT,INSERT,UPDATE,DELETEしかできない一般ユーザは以下の要領で作ります。

GRANT SELECT,INSERT,UPDATE,DELETE ON testdatabase.* TO testuser@'%' IDENTIFIED BY 'password';


【3.レプリケーション用ユーザを作りたい】
MySQLの大きな特徴であるレプリケーションを行う場合、MASTER DBに以下の要領でユーザを作ります。

GRANT REPLICATION SLAVE ON testdatabase.* TO repl@'172.16.0.0/255.255.255.0' IDENTIFIED BY 'password';

【最後に】
柔軟な権限設定ができるシステムは多いですが、概してどれも設定が複雑になるんですよね。しかし前提知識として今回の内容程度を押さえておけば、日常運用程度ではなんとかなるし、応用も利くようになるのではないでしょうか。

| | Comments (0) | TrackBack (0)

October 04, 2010

MySQLでSLAVEサーバを作る方法

今回は、いわゆるMySQLでレプリケーションを行う方法を記してみます。非常に今更感がありますが、自分にとってかゆいところに手が届く文献がなかったので、自分でも一度まとめてみようと思いました。

【MySQLにおけるレプリケーションとは】
MySQLにおけるレプリケーションとは、マスターサーバでの更新情報がほぼリアルタイムにスレーブサーバに同期化される仕組みのことを指します。

マスターサーバ上で更新が起こると、バイナリログ(更新ログとも呼ばれる)に更新情報が記録されていきます。スレーブサーバは随時マスターサーバ上の更新情報を追いかけることでマスターサーバ上のデータをスレーブサーバ上で再現していきます。

レプリケーションにはシングルマスタ構成とマルチマスタ構成があり、通常はシングルマスタ構成を使います。シングルマスタ構成は1台のマスターサーバの下に複数台のスレーブサーバがぶらさがっているイメージです。selectなどの参照系SQLコマンドはマスターサーバでもスレーブサーバでも受け付けることができますが、insert、update、deleteといった更新系SQLコマンドはマスターサーバ上で行う必要があります。万が一更新系SQLコマンドをスレーブサーバで実行するとそのスレーブサーバにデータ不整合が起きます。

一方マルチマスタ構成は、各サーバがマスタとスレーブを兼ねるというもので、どのサーバで更新系SQLコマンドを実行しても大丈夫という仕組みです。これだけ聞くとマルチマスタ構成のほうが使い勝手が良さそうですが、シングルマスタ構成と比較すると動作が不安定とのことであまり推奨されていないようです。ですので今回はマルチマスタ構成は無視し、シングルマスタ構成についてのみ記していきます。


【レプリケーションを行う手順】
MySQLでレプリケーションを行う手順はちょっと面倒くさいです。

(1) マスタサーバ上でレプリケーションを許可するスレーブサーバの権限を付与する。
(2) マスタサーバ上でバイナリログを書き出す設定を行う。
(3) マスタサーバのテーブルへの書き込みを止め、現在のバイナリログのファイル名や位置を記録する。
(4) スレーブサーバ上で、マスターサーバを参照する設定を行う。
(5) マスタサーバのデータを、新しく構築するスレーブサーバにコピーする。
(6) スレーブサーバ上で、現在のバイナリログのファイル名や位置を先ほどコピーしたデータと合わせる。
(7) マスタサーバのテーブルへの書き込みを再開する。
(8) スレーブサーバを有効にする。

といった作業が必要になります。

ところで(2)と(3)はマスターサーバからデータや位置情報を持ってきてもいいですが、他のスレーブサーバから持ってきても良いです。稼働中のマスターサーバを止めるのは大変ですが、他のスレーブサーバであれば少しは気が楽です。


【マスターサーバからデータをコピーしてくる場合の手順】

(1) マスタサーバ上でレプリケーションを許可するスレーブサーバの権限を付与する。

mysql> GRANT REPLICATION SLAVE ON *.* TO repl@'%' IDENTIFIED BY '';

もしくは接続を許可するIPを制限したい場合は以下のようになります。
mysql> GRANT REPLICATION SLAVE ON *.* TO repl@'192.168.0.0/255.255.255.0' IDENTIFIED BY '';

(2) マスタサーバ上でバイナリログを書き出す設定を行う。
/etc/my.cnfファイルに下記を追記します。

[mysqld]
log-bin
server-id=1

(3) マスタサーバを止め(もしくはテーブルへの書き込みを止めるだけでもOK)、現在のバイナリログのファイル名や位置を記録する。

mysql> FLUSH TABLES WITH READ LOCK;
mysql> show master status;
 
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
mysql-bin.000039 | 17510192 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

画面に出力された現在のバイナリログのファイル名(File)や位置(Position)をメモしておきます。

(4) スレーブサーバ上で、マスターサーバを参照する設定を行う。
スレーブサーバの/etc/my.cnfファイルに下記を追記します。(※server-idは他のマスター、スレーブで設定した数とは異なった数にする必要があります。1以上の整数が有効となります)

[mysqld]
server-id=2

(5) マスタサーバのデータを、新しく構築するスレーブサーバにコピーし、スレーブサーバ上で展開する。
マスターサーバ上で以下の手順を行う。

# cd /var/lib/
# tar cvf /tmp/mysql.tar mysql
# scp /tmp/mysql.tar username@slave.example.com:/tmp/
# rm -f /tmp/mysql.tar

そしてスレーブサーバ上で以下の手順を行う。

# service mysqld stop
# cd /var/lib
# mv mysql mysql_old
# tar xvf /tmp/mysql.tar
# rm -f /tmp/mysql.tar
# service mysqld start

(6) スレーブサーバ上で、スレーブサーバのステータスを確認します。

mysql> show slave status \G

*************************** 1. row ***************************
Slave_IO_State:
Master_Host: 10.0.0.1
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000039
Read_Master_Log_Pos: 17510192
Relay_Log_File: mysqld-relay-bin.000024
Relay_Log_Pos: 80878039
Relay_Master_Log_File: mysql-bin.000039
Slave_IO_Running: No
Slave_SQL_Running: No
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: 17510192
Relay_Log_Space: 80878039
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: NULL

この中の特に以下の内容が(3)でメモした内容と一致しているか確認します。

Master_Log_File: mysql-bin.000039
Read_Master_Log_Pos: 17510192

一致していなければ、現在のバイナリログのファイル名や位置を先ほどコピーしたデータと合わせます。

mysql> CHANGE MASTER TO
MASTER_HOST='10.0.0.1',
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000039',
MASTER_LOG_POS=17510192;

エラーが出たら適切に対応します。

ERROR 1201 (HY000): Could not initialize master info structure; more error messages can be found in the MySQL error log
mysql> reset slave;
の後、「CHANGE MASTER TO~」を再度実行する。


(7) マスタサーバのテーブルへの書き込みを再開する。

mysq> UNLOCK TABLES;


(8) スレーブサーバを有効にする。

mysql> START SLAVE;

そしてスレーブサーバの動作を確認します。

mysql> show slave status \G

~中略~
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
~中略~


両方ともYesであれば成功です。


【スレーブサーバからデータをコピーしてくる場合の手順】
さて、次はスレーブサーバからデータをコピーしてくる場合の手順を記します。この手順が他のホームページやブログにはなかなか載ってなかったのです。とは言っても手順はほぼ同じです。違うところだけ記していきます。

(1) マスタサーバ上でレプリケーションを許可するスレーブサーバの権限を付与する。
同じ

(2) マスタサーバ上でバイナリログを書き出す設定を行う。
同じ

(3) 他のスレーブサーバを止め、現在のバイナリログのファイル名や位置を記録する。

mysql> stop slave;
mysql> show slave status;

~中略~
Master_Log_File: mysql-bin.000039
Read_Master_Log_Pos: 17510192
~中略~


画面に出力された現在のバイナリログのファイル名(File)や位置(Position)をメモしておきます。

(4) スレーブサーバ上で、マスターサーバを参照する設定を行う。
同じ

(5) 他のスレーブサーバのデータを、新しく構築するスレーブサーバにコピーし、スレーブサーバ上で展開する。
他のスレーブサーバ上で以下の手順を行う。

# service mysqld stop
# cd /var/lib/
# tar cvf /tmp/mysql.tar mysql
# scp /tmp/mysql.tar username@slave.example.com:/tmp/
# rm -f /tmp/mysql.tar
# service mysqld start

そしてスレーブサーバ上で以下の手順を行う。

# service mysqld stop
# cd /var/lib
# mv mysql mysql_old
# tar xvf /tmp/mysql.tar
# rm -f /tmp/mysql.tar
# service mysqld start

(6) スレーブサーバ上で、現在のバイナリログのファイル名や位置を先ほどコピーしたデータと合わせる。
同じ。ただ多分この作業は必要ないです。

(7)は必要なし

(8) スレーブサーバを有効にする。
同じ


【余談】
多くのサイトではマスターサーバ1台+スレーブサーバ1台の2台構成となっていると思います。しかし予備のスレーブサーバを1台足して3台構成としておくと便利です。

もし予備のスレーブサーバあると、スレーブサーバを増強しようとしたときに、本稼働中のスレーブサーバを止めずとも増強を行うことが可能となります。またバックアップや集計といったサービスとは関係ないけれども重いDB処理を行いたいときには重宝します。

| | Comments (2) | TrackBack (0)

« August 2010 | Main | November 2010 »