WordPress の負荷分散環境構築

NTTPC の VPS サービスである WebARENA PROv3 で WordPress の負荷分散環境(※)を構築をしました。

※ 今あなたが読んでいる UltraMonkey.info のことです:)

そこで、この構築手順を解説します。

!注意!: 本記事の内容には間違いのないよう細心の注意をはらって記述していますが、実施環境によっては 予期せぬデータ喪失等発生する可能性もあります。本記事を参考に作業される場合はくれぐれも 自己責任でお願いします。

概要

2台のサーバで負荷分散を行います。2台はそれぞれ以下の役割になります。(FQDN, IPアドレス等は実際のものとは異なります)

  1. www1.example.jp(192.168.1.65): httpd+php, MySQL(マスタ)
  2. www2.example.jp(192.168.1.66): UltraMonkey-L7, httpd+php, MySQL(スレーブ)
注意事項

www.example.jp のAレコードまたはCNAMEは、 UML7 を動かす www2, つまり 192.168.1.66 に向けます。

WordPress が動作するサーバを負荷分散することに伴う注意

  1. アップロードコンテンツの同期について: WordPressが動作するWebサーバを負荷分散すると、アップロードコンテンツ(wp-content配下等) をWebサーバ間でsyncする必要があります。今回は lsyncd(+rsync) を使いました。 ただし、lsyncdによるコピーは非同期に行われるため、WordPressコンテンツアップロード直後に アップロードしたはずのファイルが見当たらない、といった事象が起こりえます
  2. パーシステンスとログイン状態の保持について: 素のWordPressはステートレスな設計になっており、 ログイン状態の保持にロードバランサでパーシステンスは不要です。 プラグインによってはステートフルになってしまう場合も考えられますので、 その場合は ip モジュールを使って IP パーシステンスを行ってください。 (本記事ではsessionlessモジュールで設定しています)

可用性について

  1. 前述のとおり、フロントに来るロードバランサは冗長化していません。つまりSPOFになります。
  2. ただし、WebARENA PROv3自体の機能として、仮想マシンを収容している ホストサーバの故障時は仮想マシンをフェイルオーバしてくれるそうなので、 そもそもハード故障が大きな問題にならない環境ではあります。(HAクラスタの導入はオーバスペックかも)
設定の流れ

以下のような順序で設定を行います。

  1. 各種サーバ(httpd, php, mysql, lsyncd, UltraMonkey-L7)のインストール
  2. httpd, UltraMonkey-L7 を設定し、Webの振り分けを確認する
  3. Webサーバを1台停止し、WordPressをインストールする
  4. lsyncd を設定し、WordPressの設定、コンテンツを同期する
  5. MySQL のレプリケーション設定を行う
  6. WordPressからの更新・参照アクセスを分散させる。
1. 各種サーバのインストール

両系で httpd, php, mysql, lsyncd, UltraMonkey-L7 をインストールします。 httpdはCentOS5.xのものですが、mysql, php は CentOS 付属の ものだと若干古いので remi リポジトリからインストールします。

EPEL リポジトリ (remi 用) をインストールします。

# rpm -ivh http://download.fedora.redhat.com/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm

remi リポジトリ (mysql-5.5, php用)

# rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-5.rpm

remi リポジトリを有効化します。

# cd /etc/yum.repos.d/
# cp -p remi.repo remi.repo.orig
# vi remi.repo
# diff -u remi.repo.orig remi.repo
--- remi.repo.orig      2010-05-01 16:35:55.000000000 +0900
+++ remi.repo   2011-11-15 11:42:07.583359948 +0900
@@ -2,7 +2,7 @@
 name=Les RPM de remi pour Enterprise Linux $releasever - $basearch
 #baseurl=http://rpms.famillecollet.com/enterprise/$releasever/remi/$basearch/
 mirrorlist=http://rpms.famillecollet.com/enterprise/$releasever/remi/mirror
-enabled=0                                                                      <1>
+enabled=1
 gpgcheck=1
 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi
 failovermethod=priority

 <1> enabled を 0 から 1 に変更します。

各種サーバをインストールします。

# yum install httpd mysql-server php php-mysql php-mbstring lsyncd

UltraMonkey-L7のインストール(一応両系にインストールしておきましょう) UltraMonkey-L7 は log4cxx を使っていますが、log4cxx のRPMはないのでソースからインストールします。 ビルド環境のインストール(UML7の依存関係も含みます)

root@www1# yum install gcc-c++ apr-devel automake autoconf libtool boost.x86_64 apr-util-devel doxygen perl-libwww-perl

log4cxxをインストール。

root@www1# wget http://ftp.riken.jp/net/apache//logging/log4cxx/0.10.0/apache-log4cxx-0.10.0.tar.gz
root@www1# wget http://www.apache.org/dist/logging/log4cxx/0.10.0/apache-log4cxx-0.10.0.tar.gz.md5
root@www1# md5sum apache-log4cxx-0.10.0.tar.gz; cat apache-log4cxx-0.10.0.tar.gz.md5
b30ffb8da3665178e68940ff7a61084c  apache-log4cxx-0.10.0.tar.gz
MD5(apache-log4cxx-0.10.0.tar.gz)= b30ffb8da3665178e68940ff7a61084c
root@www1# tar zxf apache-log4cxx-0.10.0.tar.gz
root@www1# cd apache-log4cxx-0.10.0
root@www1# ./autogen.sh
root@www1# ./configure && make && make install
root@www1# echo  /usr/local/lib >> /etc/ld.so.conf.d/local.conf
root@www1# ldconfig

UltraMonkey-L7をインストール

root@www1# wget 'http://sourceforge.jp/frs/redir.php?m=jaist&f=%2Fultramonkey-l7%2F47898%2Fultramonkey-l7-2.1.3-1.x86_64.rpm'
root@www1# md5sum ultramonkey-l7-2.1.3-1.x86_64.rpm
0ae6bba11d60f9d9dd6073baba979ab7  ultramonkey-l7-2.1.3-1.x86_64.rpm
root@www1# rpm -ivh ultramonkey-l7-2.1.3-1.x86_64.rpm
Preparing...                ########################################### [100%]
   1:ultramonkey-l7         ########################################### [100%]
2. httpd, UltraMonkey-L7 を設定し、Webの振り分けを確認する

httpd の設定

httpd.conf を編集します。両系で実施してください。

# diff -u httpd.conf.orig httpd.conf
--- httpd.conf.orig     2011-11-15 14:15:05.557343068 +0900
+++ httpd.conf  2011-11-17 15:32:29.963612933 +0900
@@ -131,7 +131,7 @@
 # prevent Apache from glomming onto all bound IP addresses (0.0.0.0)
 #
 #Listen 12.34.56.78:80
-Listen 80
+Listen 8080                                                            <1>
 
 #
 # Dynamic Shared Object (DSO) Support
@@ -248,7 +250,7 @@
 # e-mailed.  This address appears on some server-generated pages, such
 # as error documents.  e.g. admin@your-domain.com
 #
-ServerAdmin root@localhost
+ServerAdmin www@example.jp                                             <2>
 
 #
 # ServerName gives the name and port that the server uses to identify itself.
@@ -262,7 +264,7 @@
 # You will have to access it by its address anyway, and this will make 
 # redirections work in a sensible way.
 #
-#ServerName www.example.com:80
+ServerName www.example.jp:80                                           <3>
 
 #
 # UseCanonicalName: Determines how Apache constructs self-referencing 

 <1> 80 番ポートは UltraMonkey-L7 が待ち受けるので Listen を 8080 に設定。
 <2> UML7による死活監視を別のログに振り分けるため、User-Agentがlibwww-perlの場合は
     is_umcheck をセットします。(実際はもうすこし条件を煮詰めたほうが良いです)
 <3> ServerAdmin, ServerName を設定

httpd を起動し、UltraMonkey-L7による死活監視用のファイルを用意します。

root@www1# chkconfig httpd on
root@www1# /etc/init.d/httpd start
Starting httpd:                                            [  OK  ]
root@www1# echo 'Check OK' > /var/www/html/check.html
UltraMonkey-L7 の設定

当面 www2 でしか動かさないので、以下の実行例は www2 のものを記述します。

/usr/sbin/l7directord の編集 VZ環境では /dev/console が書き込み不能なため、起動時に以下のようなエラーが出て起動しません。

[Wed Nov 16 11:15:49 2011|14317] [INF0006] Exiting with exit status 10: Cannot open /dev/console for become daemon and exit.

そこで、l7directord を以下のように編集します。

root@www2# cp -p l7directord l7directord.orig
root@www2# vi l7directord 
root@www2# diff -u l7directord.orig l7directord
--- l7directord.orig    2010-06-16 15:06:46.000000000 +0900
+++ l7directord 2011-11-16 11:25:44.615893099 +0900
@@ -3695,9 +3695,9 @@
 
     eval { open  *STDIN, '<', '/dev/null'; };
     ld_exit(9, _message_only('ERR0704') ) if ($EVAL_ERROR);
-    eval { open *STDOUT, '>>', '/dev/console'; };
+    eval { open *STDOUT, '>>', '/dev/null'; };
     ld_exit(10, _message_only('ERR0705') ) if ($EVAL_ERROR);
-    eval { open *STDERR, '>>', '/dev/console'; };
+    eval { open *STDERR, '>>', '/dev/null'; };
     ld_exit(10, _message_only('ERR0705') ) if ($EVAL_ERROR);
 }

下記のようなl7directord.cfを作成します。

root@www2# cp /etc/ha.d/conf/l7directord.cf.sample /etc/ha.d/conf/l7directord.cf
root@www2# vi /etc/ha.d/conf/l7directord.cf
root@www2# cat /etc/ha.d/conf/l7directord.cf
##
## This is the l7directord configuration file.
## See `l7directord --help' for detailed information.
##

# = GLOBAL DIRECTIVES

# - Monitor Settings
checktimeout     = 5
negotiatetimeout = 5
checkinterval    = 10
retryinterval    = 5
checkcount       = 3

# - Logging
logfile          = /var/log/l7vs/l7directord.log
#logfile          = local0
#supervised

# - Real Server Operation
quiescent        = no
#fallback         = 127.0.0.1:80

# - Monitor Configuration File
configinterval   = 10
autoreload       = no
#callback         = /opt/config_change.sh

# = VIRTUAL DIRECTIVES
# - A sample virual section with a sorry server.
# - checkcount and quiescent settings are override the global settings.
virtual  = 0.0.0.0:80
        real = 192.168.1.65:8080 masq 1
        real = 192.168.1.66:8080 masq 1
        module      = sessionless
        scheduler   = rr
        checktype   = negotiate
        service     = http
        request     = "check.html"
        receive     = "Check OK"

l7vsdとl7directordを起動します。

root@www2# chkconfig l7vsd on
root@www2# chkconfig l7directord on
root@www2# /etc/init.d/l7vsd start
Starting l7vsd: done.
root@www2# /etc/init.d/l7directord start
Starting l7directord ...                                   [  OK  ]
root@www2# l7vsadm
Layer-7 Virtual Server version 2.1.3-1
Prot LocalAddress:Port ProtoMod Scheduler
  -> RemoteAddress:Port           Forward Weight ActiveConn InactConn
TCP 0.0.0.0:http ip rr
  -> www1.example.jp:webcache     Masq    1      0          5           <1>
  -> www2.example.jp:webcache     Masq    1      0          6           <1>

 <1> www1 と www2 の両方が表示され、Weight 値が 1 になっていることを確認し、
     www.example.jp にアクセスするごとに両系のInactConnが増加していくことを確認します。
3. Webサーバを1台停止し、WordPressをインストールする

httpdを片系(www1のみ)運転にし、MySQL マスタ側(www1)を起動

www2 の httpd を停止します。

root@www2# /etc/init.d/httpd stop
Stopping httpd:                                            [  OK  ]
root@www2# l7vsadm
Layer-7 Virtual Server version 2.1.3-1
Prot LocalAddress:Port ProtoMod Scheduler
  -> RemoteAddress:Port           Forward Weight ActiveConn InactConn
TCP 0.0.0.0:http ip rr
  -> www1.example.jp:webcache     Masq    1      0          6                   <1>

 <1> www2 が振り分け先から外れている(表示されない)ことを確認。
     外れるまで数秒かかる場合があります。

www1 の mysqld を起動します。

root@www1# chkconfig mysqld on
root@www1# /etc/init.d/mysqld start
Starting mysqld:                                           [  OK  ]

mysqld はこれで新しくデータベースが作成されます。

wordpress 用 DB を作成します。

# mysqladmin create wordpress

wordpress 用ユーザ作成します。

# mysql
mysql> grant all privileges on wordpress.* TO 'wpuser'@'localhost' IDENTIFIED BY 'wppasswd' WITH GRANT OPTION;
mysql> grant all privileges on wordpress.* TO 'wpuser'@'%' IDENTIFIED BY 'wppasswd' WITH GRANT OPTION;
mysql> grant all privileges on wordpress.* TO 'wpuser'@'%.example.jp' IDENTIFIED BY 'wppasswd' WITH GRANT OPTION;
mysql> grant all privileges on wordpress.* TO 'wpuser'@'192.168.1.65' IDENTIFIED BY 'wppasswd' WITH GRANT OPTION;
mysql> grant all privileges on wordpress.* TO 'wpuser'@'192.168.1.66' IDENTIFIED BY 'wppasswd' WITH GRANT OPTION;

セキュリティ的に問題なければ localhost と 接続元になりうる全てのホスト名についてユーザを設定するのが よいでしょう。 両方のサーバから接続できることを確認しておきます。

WordPress のインストール
# cd /var/www
# wget http://ja.wordpress.org/wordpress-3.3.1-ja.tar.gz        <1>
# tar zxf wordpress-3.2.1-ja.tar.gz
# mv html html.orig
# mv wordpress html
# cp html.orig/check.html html/                                 <2>
# chown -R apache:apache html                                   <3>

 <1> 例では 3.3.1 を使っていますが、常に最新版を使うことをお勧めします。
 <2> UML7死活監視用のファイルも忘れずに。
 <3> セットアップ時のエラーを防ぐために所有者をapacheに変更しています。
     セットアップ後に適切な所有者、権限に変更することを検討してください。
WordPress の設定

ブラウザでトップページ www.example.jp にアクセスし、指示にしたがってセットアップを行います。 だいたい以下のような感じ:)

データベースのホスト名だけは、かならずマスタ側の FQDN を入力します。 (後で設定を同期したときにもマスタに接続できるようにするためです)

  1. 「設定ファイルを作成する」のボタンを押下
  2. 「次に進みましょう!」のボタンを押下
  3. 下記の情報の入力を促されるので、それぞれ入力して「作成する」のボタンを押下
    • データベース名: wordpress
    • ユーザ名: wpuser
    • パスワード: wppasswd
    • ホスト名: www1.example.jp
    • テーブル接頭辞: wp_
  4. 前述の /var/www/html のパーミッションでは wp-config.php を書き込めない旨が表示されるので、同ペー ジに表示されている指示に従い、表示されている内容を/var/www/html/wp-config.phpとして保存、 「インストール実行」を押下する。
  5. 必要情報として下記を適宜入力し、「WordPressをインストール」のボタンを押下
    • サイトのタイトル
    • ユーザ名
    • パスワード
    • メールアドレス
  6. 成功しました!のページが表示されたら「ログイン」を押下
4. lsyncd を設定し、WordPressの設定、コンテンツを同期する

WordPressのコンテンツ同期用にlsyncdをセットアップします。 この作業はWordPressがセットアップされたwww1側で開始し、完了したらwww2側からも同様に実施します。

sshの鍵を生成、対抗のマシンに格納し、パスワードなしでログインできるようにします。

root@www1# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):                                     <1>
Enter same passphrase again:                                                    <1>
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
8a:dd:00:4c:74:c9:ed:fc:5a:da:25:b4:8d:57:e1:18 root@www1.example.jp
root@www1# ssh www2 mkdir -pm 700 /root/.ssh                                    <2>
root@www2's password: 
root@www1# scp /root/.ssh/id_rsa.pub www2:/root/.ssh/authorized_keys            <3>
root@www2's password: 
id_rsa.pub                             100%  402     0.4KB/s   00:00    
root@www1# scp /root/.ssh/id_rsa.pub www2:/root/.ssh/authorized_keys            <4>
id_rsa.pub

 <1> パスフレーズは入力しません。(これが妥当かどうかはセキュリティポリシーに照らして検討してください)
 <2> 対抗のマシンに .ssh ディレクトリを作成します。
 <3> 自分の id_rsa.pub を相手の authorized_keys として登録します。
 <4> Passwordが不要になっていることを確認します。

下記のような /etc/lsyncd.conf を用意します。

-- vim:set ft=lua:                                      <1>
settings = {
        logfile         = "/var/log/lsyncd.log",
        statusFile      = "/var/run/lsyncd.status",
--      statusInterval  = 10,
--      nodaemon        = true,                         <2>
}

sync {
        default.rsync,
        source="/var/www/html",
        target="www1:/var/www/html",
        rsyncOps = {
                "-lptgoDsu",
        }
}

 <1> '--'から始まる行はコメントです。
 <2> いろいろ試す場合は nodaemon オプションを有効化し、lsyncd /etc/lsyncd.conf のようにコマンドラインから
     実行すると便利です。

EPEL 付属の lsyncd は initscript が付属しないため、下記のようなinitscriptを作成します。

#!/bin/sh
#
# lsyncd - Startup script for the Lsyncd
#
# chkconfig: - 86 14
# description: Lsyncd is a realtime sync daemon.
# processname: lsyncd
# config: /etc/lsyncd.conf
# config: /etc/sysconfig/lsyncd
# pidfile: /var/run/lsyncd.pid

# Source function library.
. /etc/rc.d/init.d/functions

exec=${LSYNCD-/usr/bin/lsyncd}
prog=$(basename $exec)
pidfile=${PIDFILE-/var/run/lsyncd.pid}
options=${LSYNCD_OPTS}
configfile=${LSYNCD_CONFIG-/etc/lsyncd.conf}

[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog

lockfile=/var/lock/subsys/$prog

start() {
    echo -n $"Starting $prog: "
    # if not running, start it up here, usually something like "daemon $exec"
    daemon --pidfile=${pidfile} $exec $options $configfile
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}

stop() {
    echo -n $"Stopping $prog: "
    # stop it here, often "killproc $prog"
    killproc $prog
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}

restart() {
    stop
    start
}

case "$1" in
    start|stop|restart)
        $1
        ;;
    force-reload)
        restart
        ;;
    status)
        status $prog
        ;;
    try-restart|condrestart)
        if status $prog >/dev/null ; then
            restart
        fi
        ;;
    reload)
        status $prog >/dev/null || exit 7
        killproc $prog -HUP
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|try-restart|force-reload}"
        exit 2
esac

作成した initscript を有効化し、起動します。

root@www1# chmod +x /etc/init.d/lsyncd
root@www1# chkconfig lsyncd --add
root@www1# chkconfig lsyncd on
root@www1# chkconfig lsyncd --list
lsyncd          0:off   1:off   2:on    3:on    4:on    5:on    6:off
root@www1# /etc/init.d/lsyncd start
Starting lsyncd:                                           [  OK  ]

すぐに同期が始まるので、同期が完了したら、httpd を www2 だけを起動している状態でも WordPressにアクセスできることを確認します。

root@www2# l7vsadm
Layer-7 Virtual Server version 2.1.3-1
Prot LocalAddress:Port ProtoMod Scheduler
  -> RemoteAddress:Port           Forward Weight ActiveConn InactConn
TCP 0.0.0.0:http ip rr
  -> www2.example.jp:webcache     Masq    1      0          0           <1>

 <1> www1 の httpd を停止し、www2 を起動すると、上記のような状態になります。
     ここで http://www.example.jp/ にアクセスし、問題なく WordPress が表示できることを確認します。

www2 -> www1 の同期設定

最後に、同様の作業を www2 でも実施し、お互いに lsyncd で同期をとるようにします。 /etc/lsyncd.conf の target のホスト名だけは書き換える必要があることに注意してください。

--- /etc/lsyncd.conf.orig    2012-03-19 19:08:57.000000000 +0900
+++ /etc/lsyncd.conf 2012-03-19 18:16:17.000000000 +0900
@@ -9,7 +9,7 @@
 sync {
        default.rsync,
        source="/var/www/html",
-       target="www2:/var/www/html",
+       target="www1:/var/www/html",                    <1>
        rsyncOps = {
                "-lptgoDsu",
        }

 <1> www2 側からは www1 へ同期することになります。
5. MySQL のレプリケーション設定を行う

マスタでレプリケーション用ユーザを作成します

mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%.example.jp' IDENTIFIED BY 'repl_passwd';
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.65' IDENTIFIED BY 'repl_passwd';
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.66' IDENTIFIED BY 'repl_passwd';
Query OK, 0 rows affected (0.00 sec)

マスタにて以下を /etc/my.cnf の [mysqld] セクションに追記します

log-bin=mysql-bin
server-id=1

mysql を再起動します

root@www1# /etc/init.d/mysqld restart
Stopping mysqld:                                           [  OK  ]
Starting mysqld:                                           [  OK  ]

マスタのロックとステータスの確認・ダンプの取得

ターミナルエミュレータ(シェル)を2つ立ち上げます。 マスタで既存データをダンプするため、1つめのターミナルでテーブルにロックをかけ、ステータスをメモしておきます。

mysql> FLUSH TABLES WITH READ LOCK;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW MASTER STATUS;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      107 |              |                  |       <1>
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

 <1> File と Position の値をメモする

1つめのターミナルはロック状態のまま、別ターミナルでダンプを取得し、スレーブへ転送します。

root@www1# mysqldump --all-databases --lock-all-tables > /root/mysqldump.db
root@www1# scp /root/mysqldump.db www2:
mysqldump.db                                   100%  890KB 889.6KB/s   00:00

先ほどのターミナルにもどり、ロックを解除します。

mysql> UNLOCK TABLES;
Query OK, 0 rows affected (0.00 sec)

スレーブ(www2)側DBの立ち上げ

以下を www2:/etc/my.cnf の [mysqld] セクションに追記します

server-id=2

スレーブ側をすでに起動していた場合、スレーブDBを停止し、データベースを削除、レプリケーションを無効化して起動します。

www2# cd /etc/sysconfig/
www2# cp -p mysqld mysqld.orig
www2# vi mysqld
www2# /etc/init.d/mysqld stop
Stopping mysqld:                                           [  OK  ]
www2# rm -rf /var/lib/mysql
www2# mkdir /var/lib/mysql; chown mysql:mysql /var/lib/mysql
root@www2# diff -u mysqld.orig mysqld
--- mysqld.orig 2011-10-23 00:19:26.000000000 +0900
+++ mysqld      2011-11-17 14:30:35.731073289 +0900
@@ -7,5 +7,5 @@
 STOPTIMEOUT=60
 
 # Other options to pass to the server (p.e. --federated)
-MYOPTIONS=
+MYOPTIONS=--skip-slave
 
ww2# /etc/init.d/mysqld start
Initializing MySQL database:  Installing MySQL system tables...
OK
Filling help tables...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

/usr/bin/mysqladmin -u root password 'new-password'
/usr/bin/mysqladmin -u root -h www2.ultramonkey.info password 'new-password'

Alternatively you can run:
/usr/bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /usr ; /usr/bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /usr/mysql-test ; perl mysql-test-run.pl

Please report any problems with the /usr/bin/mysqlbug script!

                                                           [  OK  ]
Starting mysqld:                                           [  OK  ]

先ほど転送したダンプを流し込み、マスタ位置情報を投入、レプリケーションを開始します。

www2# mysql < /root/mysqldump.db
www2# mysql
mysql> CHANGE MASTER TO MASTER_HOST='192.168.1.65',
  -> MASTER_USER='repl', MASTER_PASSWORD='repl_passwd',
  -> MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=107;            <1>
Query OK, 0 rows affected (0.00 sec)

mysql> START SLAVE;
Query OK, 0 rows affected (0.00 sec)


 <1> MASTER_LOG_FILE と MASTER_LOG_POS は先程メモした値を用いる

起動オプションから –skip-slave を取り除き再起動します。

www2# cd /etc/sysconfig/
www2# mv mysqld.orig mysqld
mv: overwrite `mysqld'? y
www2# /etc/init.d/mysqld restart
Stopping mysqld:                                           [  OK  ]
Starting mysqld:                                           [  OK  ]

以上でMySQLのマスタ(www1)への書き込みがスレーブ(www2)へレプリケーションされるようになります。

6. WordPressからの更新・参照アクセスを分散させる

このままでは WordPress の読み書きは www1 のみへ行われ、負荷分散になりません。 また、www1 が停止した場合にもサービス停止してしまいますので、WordPressからの更新・参照を調節します。

具体的には WordPress の HyperDB プラグインで接続先のDBサーバを分散させます。

HyperDB のインストール

HyperDB は通常のWordPressプラグインと設定方法が異なり、 WordPress管理インターフェースからは設定できません。

インストールについても http://wordpress.org/extend/plugins/hyperdb/ からダウンロードしたファイルを wordpress インストールディレクトリに手動で展開します。

root@www1# unzip hyperdb.zip
Archive:  hyperdb.zip
hyperdb packaged: Sun, 08 Jan 2012 18:20:35 +0000
   creating: hyperdb/
  inflating: hyperdb/readme.txt      
  inflating: hyperdb/db.php          
root@www1# cp hyperdb/db.php /var/www/html/wp-content/
root@www1# cp hyperdb/db-config.php /var/www/html/

まず wp-config.php に ReadOnly な DB サーバとして DB_HOST_RO を定義します。

root@www1# cp -p wp-config.php wp-config.php.orig
root@www1# vi wp-config.php
root@www1# diff -u wp-config.php.orig wp-config.php 
--- wp-config.php.orig  2012-03-19 19:56:38.000000000 +0900
+++ wp-config.php       2012-03-19 19:56:59.000000000 +0900
@@ -31,6 +31,7 @@
 
 /** MySQL のホスト名 */
 define('DB_HOST', '192.168.1.65');
+define('DB_HOST_RO', '192.168.1.66');
 
 /** データベースのテーブルを作成する際のデータベースのキャラクターセット */
 define('DB_CHARSET', 'utf8');

db-config.php のリードオンリーサーバの名前を先ほど定義した DB_HOST_RO に変更します。

root@www1# cp -p db-config.php db-config.php.orig
root@www1# vi db-config.php
root@www1# diff -u db-config.php.orig db-config.php
--- db-config.php.orig  2012-03-19 19:55:42.000000000 +0900
+++ db-config.php       2012-03-19 19:59:36.000000000 +0900
@@ -226,7 +226,7 @@
<ul>
<li>The last three parameters are set to the defaults but are shown for clarity.</li>
  */
 $wpdb->add_database(array(
-       'host'     => DB_HOST,     // If port is other than 3306, use host:port.
+       'host'     => DB_HOST_RO,     // If port is other than 3306, use host:port.
        'user'     => DB_USER,
        'password' => DB_PASSWORD,
        'name'     => DB_NAME,

この設定により、書き込みアクセスは www1 の MySQL へ、 読み込みアクセスは www1 と www2 へ負荷分散され、www1 が落ちた場合でも 記事の閲覧は継続できるようになります。

いろいろな障害パターンをテストしてみてください。

まとめ

UltraMonkey-L7, httpd, php, MySQL を用いて WordPress 負荷分散環境を作成しました。 ご意見、ご感想等ありましたら、公式メーリングリスト、Twitterなどなどにご連絡ください。