AWS で HA クラスタで WordPress を動かす

最近お仕事でも AWS を使わざるを得ない状況になってきたので、ためしに AWSWordPress を HA 構成にしてみました。

なお、RDS や S3 は使っていないのであまり AWS っぽくはありません(オンプレをできるだけそのまま AWS に移行する想定だったので)。

構成

次のような構成にします。

  • APx2 DBx2 NASx2 で Multi-AZ
    • すべて CentOS 6.5
    • 東京リージョン
  • AP は Apache/PHP
    • 負荷分散に ELB を使用する
  • DB は DRBD/Pacemaker/Heartbeat/MySQL
    • MHA よりもこの構成の方がなれているから
  • NAS は GlusterFS
    • DRBD/Pacemaker/Heartbeat/NFS でもできるけど面倒なのと GlusterFS 使ってみたかった
  • ドメインは既存のもののサブドメインを Route53 に委任
    • 詳細は省略

IAM ユーザーを作成

aws cli を使うために IAM ユーザーを作成します。

本当は必要最小限のパーミッションにするべきなのでしょうが、お試しなので Power User Access で作成しました。

Access Key ID と Secret Access Key はあとで必要になるのでメモっておきます。

VPC を作成

VPC

次のように VPC を作成します。

Name tag            wp-vpc
CIDR block          10.1.0.0/16
Tenancy             Default

Subnets

VPC に2つの AZ のサブネットを作成します。なぜか ap-northeast-1b だと作成できなかったので ap-northeast-1a と ap-northeast-1c で作りました。

Name tag            wp-net-a
VPC                 wp-vpc
Availability Zone   ap-northeast-1a
CIDR block          10.1.1.0/24
Name tag            wp-net-c
VPC                 wp-vpc
Availability Zone   ap-northeast-1c
CIDR block          10.1.2.0/24

Internet Gateways

Internet Gateway を追加します。

Name tag            wp-gw

追加後に wp-vpc にアタッチしておきます。

Route Tables

Route Tables で wp-vpc のルーティングを追加します。

Destination     0.0.0.0/0
Target          igw-xxxxx | wp-gw

Target には↑で作成した Internet Gateway を指定します。

Security Group

EC2 の Management Console を開いて wp-vpc の Security Group を設定します。

VPC の Management Console でもできますが、そちらだと後述の MyIP が選択できなかったので。

VPC を新規作成したときにデフォルトの Security Group が作成されています。この Security Group は、同じ Security Group のインスタンス同士の通信をすべて許可するようになっています。

インスタンス間の通信はこの Security Group で通すことにして、これから作業するために SSH で繋げられる必要があるので次のように inbound Rules を追加します。

Type    SSH(22)
Source  My IP
Type    All ICMP
Source  My IP

Source に My IP を選択すると、今現在 Management Console に接続しているグローバルIPアドレスが設定されます。

ついでに Name tag も wp-default などとわかりやすく変更しておきます。

さらに、最終的に ELB を HTTP で公開する必要があるので、次のように Security Group を新規作成します。

Security group name     public
Description             public
VPC                     wp-vpc
inbound Rules
    Type                HTTP
    Source              Anywhere

Name tag も wp-public などとわかりやすく変更しておきます。

ベースとなるインスタンスの作成

この後、インスタンスを合計6台起動して HA 構成にするのですが、いきなり6台起動して最初から構築するのは面倒なので、ベースとなる AMI を作成します。

EC2 の Management Console の Instances から Lunch Instance をクリックして、次のような Instance を1台だけ作成します。

Step 1: Choose an Amazon Machine Image (AMI)

AWS Marketplace を CentOS で検索して CentOS 6.5 の AMI を選択します。

Step 2: Choose an Instance Type

t1.micro を選択します。

本当は t2.micro が良いのですが、仮想化の種類が paravirtual なので t2.micro は選択できません。

Step 3: Configure Instance Details

下記のみ変更して、その他はデフォルトのままにします。

Network                 wp-vpc
Auto-assign Public IP   Enable

Step 4: Add Storage

Volume Type を General Purpose (SSD) に変更します。

Step 5: Tag Instance

Name に wp-base などと判りやすい名前を付けます。

Step 6: Configure Security Group

Select an existing security group を選択して default と public を選択します。

Step 7: Review Instance Launch

ざっくり内容を確認して、Launch をクリックします。

しばらく待つと Instance が起動するので SSH でログインします。

ベースに必要なソフトウェアをインストール

作成したベースとなるインスタンスにいろいろインストールします。

SELinuxiptables

/etc/selinux/config を編集して SELinux を無効にします。

sed -i '/^SELINUX=/c SELINUX=disabled' /etc/selinux/config
cat /etc/selinux/config

iptables を無効にします。

service iptables stop
service ip6tables stop
chkconfig iptables off
chkconfig ip6tables off

再起動します。

reboot

yum

再起動が終わったら yum で update と base とかの基本的なものをインストールします。

yum update
yum groupinstall base
yum install bash-completion nc git

mysqlapachephp をインストールします。

yum install mysql-server httpd php php-pecl-zendopcache php-mysql

chkconfig mysqld off
chkconfig httpd off

サードパーティリポジトリを追加して drbd と heartbeat と pacemaker をインストールします。

yum localinstall http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum localinstall http://www.elrepo.org/elrepo-release-6-6.el6.elrepo.noarch.rpm

cd /usr/local/src
wget "http://sourceforge.jp/frs/redir.php?m=iij&f=%2Flinux-ha%2F60151%2Fpacemaker-1.0.13-1.2.el6.x86_64.repo.tar.gz" \
  -O pacemaker-1.0.13-1.2.el6.x86_64.repo.tar.gz
tar xvzf pacemaker-1.0.13-1.2.el6.x86_64.repo.tar.gz
mv pacemaker-1.0.13-1.2.el6.x86_64.repo /tmp

cd /tmp/pacemaker-1.0.13-1.2.el6.x86_64.repo
sed -i '/name=/a exclude=pacemaker pacemaker* clusterlib' \
  /etc/yum.repos.d/CentOS-Base.repo

yum install -c pacemaker.repo heartbeat pacemaker
yum install kmod-drbd83 drbd83-utils

chkconfig heartbeat off
chkconfig drbd off

glusterfs をインストールします。

wget http://download.gluster.org/pub/gluster/glusterfs/LATEST/EPEL.repo/glusterfs-epel.repo \
  -O /etc/yum.repos.d/glusterfs-epel.repo

yum install glusterfs-server glusterfs-fuse

chkconfig glusterd off
chkconfig glusterfsd off

aws cli をインストールします。

AWS Access Key ID と AWS Secret Access Key には↑の方で作成した IAM ユーザーのものを指定してください。

Default region name は東京リージョンである ap-northeast-1 を指定します。

Default output format はなんでもいいです。

yum install python-pip
pip install awscli
aws configure

ベースの各種設定

後の作業が楽になるように、ベースイメージに各種設定ファイルをあらかじめ作成しておきます。

hosts

/etc/hosts を編集します。

cat <<EOS> /etc/hosts
127.0.0.1    localhost.localdomain localhost
::1          localhost6.localdomain6 localhost6
10.1.1.11    db01.aws.example.net db01
10.1.2.11    db02.aws.example.net db02
10.1.1.22    ns01.aws.example.net ns01
10.1.2.22    ns02.aws.example.net ns02
EOS
cat /etc/hosts

AP は固定の IP アドレスが必要ないので記述していません。

drbd

/etc/drbd.d/global_common.conf を編集します。

cp -a /etc/drbd.d/global_common.conf{,.orig}
cat <<EOS> /etc/drbd.d/global_common.conf
global {
    usage-count no;
}

common {
    protocol C;
    startup {
        wfc-timeout 60;
    }
    net {
        cram-hmac-alg sha1;
        shared-secret "drbd_no_himitu";
    }
    syncer {
        rate 5M;
    }
}
EOS
cat /etc/drbd.d/global_common.conf

/etc/drbd.d/r0.res を編集します。drbd のためのディスクはあとでアタッチするので、仮で /dev/xvdj とします。

cat <<EOS> /etc/drbd.d/r0.res
resource r0
{
    protocol C;
    device /dev/drbd0;
    meta-disk internal;

    on db01.aws.example.net {
        address 10.1.1.11:7791;
        disk /dev/xvdj;
    }

    on db02.aws.example.net {
        address 10.1.2.11:7791;
        disk /dev/xvdj;
    }
}
EOS
cat /etc/drbd.d/r0.res

heartbeat

/etc/ha.d/ha.cf を編集します。AWS はブロードキャストやマルチキャストができないので ucast で設定します(そもそも Multi-AZ だと別セグメントなのでブロードキャストは届かないし)。

cat <<EOS> /etc/ha.d/ha.cf
keepalive 5
deadtime 60
warntime 20
initdead 120

udpport 694
ucast eth0 10.1.1.11
ucast eth0 10.1.2.11

node db01.aws.example.net
node db02.aws.example.net

watchdog /dev/watchdog
pacemaker on
uuidfrom nodename
debug 0
use_logd yes
EOS
cat /etc/ha.d/ha.cf

/etc/logd.cf を編集します。

cat <<EOS> /etc/logd.cf
debugfile /var/log/ha-debug
logfile /var/log/ha-log
logfacility none
EOS
cat /etc/logd.cf

/etc/ha.d/authkeys を編集してパーミッションを変更します。

cat <<EOS> /etc/ha.d/authkeys
auth 2
2 sha1 heartbeat_no_himitu
EOS
cat /etc/ha.d/authkeys
chown root:root /etc/ha.d/authkeys
chmod 600 /etc/ha.d/authkeys

/etc/logrotate.d/heartbeat を作成します。これはログローテートのためです。

cat <<EOS> /etc/logrotate.d/heartbeat
/var/log/ha-debug {
        missingok
        daily
        rotate 10
        ifempty
}

/var/log/ha-log {
        missingok
        daily
        rotate 10
        ifempty
}
EOS
cat /etc/logrotate.d/heartbeat

mysql

/etc/my.cnf を編集します。

ログや pid や socket のパスを変えているのは単なる好みですが、Pacemaker のリソース設定で必要なので、変更した値は覚えておきます。

その他のパラメータは適当です。

cat <<EOS> /etc/my.cnf
[client]
port = 3306
socket = /var/run/mysql/mysql.sock

[mysqld_safe]
log-error = /var/log/mysql/error.log
pid-file = /var/run/mysql/mysqld.pid

[mysqld]
user = mysql
port = 3306

log-error = /var/log/mysql/error.log
pid-file = /var/run/mysql/mysqld.pid
socket = /var/run/mysql/mysql.sock
datadir = /var/lib/mysql

general_log_file = /var/log/mysql/query.log
slow_query_log_file = /var/log/mysql/slow.log

log-warnings
general_log
slow_query_log

query_cache_type = 0
symbolic-links = 0
skip-external-locking
skip-name-resolve

character-set-server = utf8
default-storage-engine = innodb

key_buffer_size = 16M
max_allowed_packet = 16M
table_open_cache = 64
sort_buffer_size = 512K
join_buffer_size = 512K
thread_cache_size = 2
thread_concurrency = 2
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M

# innodb
innodb_data_file_path = ibdata1:10M:autoextend
innodb_buffer_pool_size = 128M
innodb_additional_mem_pool_size = 8M
innodb_log_buffer_size = 8M
innodb_log_file_size = 16M
innodb_file_per_table
#innodb_file_format = Barracuda

[mysqldump]
quick
max_allowed_packet = 16M

[mysql]
connect-timeout = 5
no-auto-rehash
show-warnings

[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M

[mysqlhotcopy]
interactive-timeout
EOS
cat /etc/my.cnf

ログや pid や socket のためのディレクトリを準備します。

mv /var/run/mysqld /var/run/mysql
mkdir /var/log/mysql
chown -R mysql:mysql /var/log/mysql/
rm -f /var/log/mysqld.log

/etc/logrotate.d/mysql を作成します。これはログローテートのためです。

cat <<'EOS'> /etc/logrotate.d/mysql
/var/log/mysql/*.log {
    missingok
    daily
    rotate 10
    ifempty
    nocreate
    sharedscripts
    postrotate
        if test -x /usr/bin/mysqladmin && \
            /usr/bin/mysqladmin ping &>/dev/null
        then
            /usr/bin/mysqladmin flush-logs
        fi
    endscript
}
EOS
cat /etc/logrotate.d/mysql

/etc/cron.d/mysql を作成します。これは日次で Analyze するためですが、無くてもいいです。

cat <<EOS> /etc/cron.d/mysql
09 04 * * * root /usr/bin/mysqladmin ping 1>/dev/null 2>&1 && /usr/bin/mysqlcheck -a --all-databases 1> /dev/null
EOS
cat /etc/cron.d/mysql

apache

apachewelcome.conf を無効にします。削除すると yum update で復活することがあるので空にするのがオススメです。

echo -n "" > /etc/httpd/conf.d/welcome.conf

php

php の設定ファイルを作ります。

/etc/php.ini を直接編集せずに /etc/php.d/ に変更分を作成します。

cat <<EOS> /etc/php.d/_default_.ini
expose_php = Off
error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/php.log
date.timezone = Asia/Tokyo
post_max_size = 500M
upload_max_filesize = 500M
memory_limit = 1G
EOS
cat /etc/php.d/_default_.ini

ログディレクトリを準備します。

mkdir /var/log/php/
chmod 777 /var/log/php/
touch /var/log/php/php.log
chmod 666 /var/log/php/php.log

ログローテートの設定を作成します。

cat <<EOS> /etc/logrotate.d/php
/var/log/php/*.log {
    missingok
    daily
    rotate 10
    ifempty
    create 0666 root root
}
EOS
cat /etc/logrotate.d/php

wordpress

WordPress をダウンロードして /var/www/html に展開します。

cd /usr/local/src/
wget http://ja.wordpress.org/wordpress-3.9.2-ja.zip
unzip wordpress-3.9.2-ja.zip
rm -fr /var/www/html/
mv wordpress /var/www/html/

wp-config.php を作ります。

DB サーバとして指定している 192.168.0.10 は Routing-Based HA のためのものです。

cd /var/www/html
mv wp-config-sample.php wp-config.php

sed wp-config.php -e "
  /^define('DB_NAME'/c     define('DB_NAME', 'wordpress');
  /^define('DB_USER'/c     define('DB_USER', 'wordpress');
  /^define('DB_PASSWORD'/c define('DB_PASSWORD', 'wordpress');
  /^define('DB_HOST'/c     define('DB_HOST', '192.168.0.10');
" -i

curl https://api.wordpress.org/secret-key/1.1/salt/ > /tmp/secret

sed wp-config.php -e "
  /^define('AUTH_KEY'/r /tmp/secret
  /^define('AUTH_KEY'/d
  /^define('SECURE_AUTH_KEY'/d
  /^define('LOGGED_IN_KEY'/d
  /^define('NONCE_KEY'/d
  /^define('AUTH_SALT'/d
  /^define('SECURE_AUTH_SALT'/d
  /^define('LOGGED_IN_SALT'/d
  /^define('NONCE_SALT'/d
" -i

cat wp-config.php

ec2-route lsb

後で Pacemakedr で Routing-Based HA をするので、そのための lsb のスクリプトを作成します。

まずは Management Console の VPC の画面で Route Table の ID を調べます。

もしくはインスタンス内で次のように調べることもできます。

mac=$(curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/ | head -1)
vpc=$(curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/$mac/vpc-id)
route_table_id=$(aws ec2 describe-route-tables --output text --query "RouteTables[?VpcId==\`$vpc\`].[RouteTableId][]")
echo $route_table_id

Route Table を変更するための lsb を作成します。rtb-xxxxxxxx の部分は↑で調べた ID に読み替えてください。

cat <<'EOS'> /etc/rc.d/init.d/ec2-route
#!/bin/sh
#
# chkconfig: 12345 01 99
# description: EC2 route change

. /etc/rc.d/init.d/functions

route_table_id=rtb-xxxxxxxx
destination_cidr_block=192.168.0.10/32
instance_id=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)

function start () {
  if status > /dev/null; then
    echo -n "already create"
  else
    echo -n "create route ..."
    aws ec2 delete-route --output text \
      --route-table-id $route_table_id \
      --destination-cidr-block $destination_cidr_block \
        2>&1 | logger -t ec2-route-delete
    aws ec2 create-route --output text \
      --route-table-id $route_table_id \
      --instance-id $instance_id \
      --destination-cidr-block $destination_cidr_block \
        2>&1 | logger -t ec2-route-create
  fi
  success; echo
  return 0
}

function stop()
{
  if ! status > /dev/null; then
    echo -n "already delete"
  else
    echo -n "delete route ..."
    aws ec2 delete-route --output text \
      --route-table-id $route_table_id \
      --destination-cidr-block $destination_cidr_block \
        2>&1 | logger -t ec2-route-delete
  fi
  success; echo
  return 0
}

function status()
{
  cnt=$(aws ec2 describe-route-tables --output text \
    --filters Name=route.instance-id,Values=$instance_id | wc -l)

  if [ "$cnt" -gt 0 ]; then
    echo "ok"
    return 0
  else
    echo "ng"
    return 3
  fi
}

case "$1" in
  start)
    start
    exit $?
    ;;
  status)
    status
    exit $?
    ;;
  stop)
    stop
    exit $?
    ;;
  *)
    echo "Usage: ec2-route {start|stop|status}"
    exit 2
esac
EOS

バーミッションを変更して chkconfig --add します。

chmod +x /etc/rc.d/init.d/ec2-route
chkconfig --add ec2-route
chkconfig ec2-route off
chkconfig --list | grep ec2-route

なお、このスクリプトだと結構な頻度で aws cli を実行するし、失敗時の例外処理も無いので、よろしくありません。実運用で使うためには修正が必要です。

ベースの AMI 化

ここまでできたらベースとなるインスタンスを AMI にします。

Marketprice の CentOS 6.5 は起動時に /root/firstrun があれば root のパスワードが自動設定されるので作成します。

touch /root/firstrun

SSH の公開鍵認証の設定も、未作成なら自動的に作成設定されるので削除します。

rm -f /root/.ssh/authorized_keys

MAC アドレスが変わってもインタフェース名が変わらないように 70-persistent-net.rules を削除します。

rm -f /etc/udev/rules.d/70-persistent-net.rules

SSH のホスト鍵が全部同じになるのは好ましくないので削除します。

rm -f /etc/ssh/ssh_host_dsa_key
rm -f /etc/ssh/ssh_host_dsa_key.pub
rm -f /etc/ssh/ssh_host_key
rm -f /etc/ssh/ssh_host_key.pub
rm -f /etc/ssh/ssh_host_rsa_key
rm -f /etc/ssh/ssh_host_rsa_key.pub

ログはいらないので削除します。

find /var/log -type f | xargs rm -fv

最後に root の .bash_history も削除してシャットダウンします。

rm -f /root/.bash_history
shutdown -h now

シャットダウンが終わったら(Instance State が stopped になったら)、インスタンスから AMI を作成します。

名前はわかりやすく wp-base-20140809 などとしておきます。

AMI の作成が終わったら、元になったインスタンスは削除して構いません。

AMI からインスタンスを作成

↑で作成した AMI から AZ ap-northeast-1a と ap-northeast-1c でそれぞれ3台ずつ、合計6台起動します。

  • VPC は作成済の wp-vpc を指定
  • サブネットは各 AZ に作成済の wp-net-awp-net-c を指定
  • Auto-assign Public IP を Enable にする
  • セキュリティグループは Select an existing security group を選択して default を指定
  • ストレージや NIC はデフォルトのまま

作成後にインスタンスの名前をわかりやすくするための次のように変更します。

  • ap-northeast-1a
    • wp-ap01
    • wp-db01
    • wp-ns01
  • ap-northeast-1c
    • wp-ap02
    • wp-db02
    • wp-ns02

セカンダリ IP アドレスをインスタンスに指定

Management Console で DB と NAS にそれぞれセカンダリ IP アドレスを設定します。

  • db01 10.1.1.11
  • ns01 10.1.1.22
  • db02 10.1.2.11
  • ns02 10.1.2.22

DB サーバの Source/Dest チェックを無効 にする

Management Console で DB サーバのインスタンスの Source/Dest チェックを無効にします。

これは Routing-Based HA のために必要な設定です。

DB と NAS のボリュームを作成してアタッチ

Management Console で DB と NAS 用にボリュームを作成してアタッチします。AZ の異なるボリュームはアタッチできないので ap-northeast-1a と ap-northeast-1c でそれぞれ2つずつ作成します。

作成できたら DB と NASインスタンスに1つずつアタッチします。

NAS に関しては ルートボリュームをそのまま使っても良いのですが・・・なんとなくです。

インスタンスの IP アドレスとホスト名を設定

DB と NAS にログインしてセカンダリ IP アドレスを設定します。

次のようにメタデータから設定すると楽です。

cat <<EOS> /etc/sysconfig/network-scripts/ifcfg-eth0:0
DEVICE=eth0:0
BOOTPROTO=none
ONBOOT=yes
IPADDR=$(curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/$(
  curl -s http://169.254.169.254/latest/meta-data/mac/
)/local-ipv4s | tail -1)
PREFIX=24
EOS
cat /etc/sysconfig/network-scripts/ifcfg-eth0:0
ifup eth0:0

ホスト名を設定します。

hostname $(hostname -A)
hostname
sed -i "/^HOSTNAME=/c HOSTNAME=$(hostname)" /etc/sysconfig/network
cat /etc/sysconfig/network

ホスト名を変更したあとは再起動するのがマイルールです。

reboot

再起動後に IP アドレスとホスト名が意図通りになっているか確認しておきます。

hostname
ip addr

drbd

DB サーバで drbd のセットアップをします。

まずはアタッチされた EBS のデバイス名を調べます。

fdisk -l

/etc/drbd.d/r0.res のデバイス名を修正します。デバイス名が /dev/xvdj なら修正の必要はありません。

vi /etc/drbd.d/r0.res

メタデータを作成して drbd を開始します。

drbdadm create-md r0
service drbd start
service drbd status

db01 を Primary として同期します(db01 のみ)。

drbdadm -- --overwrite-data-of-peer primary r0
watch -n 1 service drbd status

同期が終わったらフォーマットしてファイルシステムを作ります(db01 のみ)。

mkfs.ext4 /dev/drbd/by-res/r0
tune2fs -c 0 /dev/drbd/by-res/r0

マウントして MySQL のデータディレクトリを初期化します(db01 のみ)。

mkdir -p /drbd/r0
mount -t ext4 /dev/drbd/by-res/r0 /drbd/r0 

mkdir /drbd/r0/mysql
chown mysql:mysql /drbd/r0/mysql
rm -fr /var/lib/mysql
ln -s /drbd/r0/mysql /var/lib/mysql

service mysqld start
mysql -e 'select 1'
service mysqld stop

db02 にもマウントポイントと mysql のデータディレクトリのシンボリックリンクを作成します(db02 のみ)。

mkdir -p /drbd/r0
rm -fr /var/lib/mysql
ln -s /drbd/r0/mysql /var/lib/mysql

動作確認のために db01 を Secondary に戻します(db01 のみ)。

umount /drbd/r0
drbdadm secondary r0
service drbd status

動作確認のために db02 を Primary にして MySQL を起動します(db02 のみ)。

drbdadm primary r0
service drbd status

mount -t ext4 /dev/drbd/by-res/r0 /drbd/r0 

service mysqld start
mysql -e 'select 1'
service mysqld stop

動作確認が終わったら db02 も Secondary に戻します。

umount /drbd/r0
drbdadm secondary r0
service drbd status

drbd を停止しておきます(db01 と db02 の両方)。

service drbd stop
service drbd status
lsmod | grep drbd

pacemaker

次に DB サーバで pacemaker/heartbeat の設定をします。

logd は常時起動していても問題ないので常時起動にします。

chkconfig logd on
service logd start
service logd status
tail /var/log/messages

heartbeat を起動します。

rm -fr /var/lib/heartbeat/crm/*
service heartbeat start
service heartbeat status

ps auxw | grep heartbeat
watch crm_mon -1

crm_mon でお互いを認識していることを確認できたら、どちらが DC になっているかを確認します。

これから crm を用いた pacemaker の設定を行いますが、この作業は DC で行います。

まずは基本設定。

crm configure
  property stonith-enabled="false"
  property start-failure-is-fatal="false"
  rsc_defaults migration-threshold="5"
  rsc_defaults resource-stickiness="INFINITY"
  rsc_defaults failure-timeout="3600s"
  verify
  commit
  quit

続いてリソース設定。

crm configure

  primitive drbd \
    ocf:linbit:drbd \
    params drbd_resource="r0" drbdconf="/etc/drbd.conf" \
    op start timeout="240s" on-fail="restart" \
    op stop  timeout="100s" on-fail="block" \
    op monitor interval="20s" role="Slave"  timeout="20s" start-delay="1m" on-fail="restart" \
    op monitor interval="10s" role="Master" timeout="20s" start-delay="1m" on-fail="restart"

  ms ms_drbd \
    drbd \
    meta master-max="1" master-node-max="1" clone-max="2" clone-node-max="1" notify="true"

  primitive vip ocf:heartbeat:IPaddr2 \
    params ip="192.168.0.10" cidr_netmask="32" nic="lo"

  primitive fs \
    ocf:heartbeat:Filesystem \
    params device="/dev/drbd/by-res/r0" directory="/drbd/r0" fstype="ext4" \
    op start   interval="0s"  timeout="60s" on-fail="restart" \
    op monitor interval="10s" timeout="60s" on-fail="restart" \
    op stop    interval="0s"  timeout="60s" on-fail="block"

  primitive mysql \
    ocf:heartbeat:mysql \
    params binary="/usr/bin/mysqld_safe" \
      log="/var/log/mysql/error.log" \
      pid=/var/run/mysql/mysqld.pid \
      socket=/var/run/mysql/mysql.sock \
    op monitor interval="10s" timeout="30s"  on-fail="restart" start-delay="30" OCF_CHECK_LEVEL="20" \
    op start   interval="0s"  timeout="120s" on-fail="restart" \
    op stop    interval="0s"  timeout="120s" on-fail="block"

  primitive route lsb:ec2-route \
    op start   interval="0s"  timeout="60s" on-fail="restart" \
    op monitor interval="60s" timeout="60s" on-fail="restart" \
    op stop    interval="0s"  timeout="60s" on-fail="block"

  group group_db \
    fs mysql vip route

  colocation colocation_db_drbd \
    inf: group_db ms_drbd:Master

  order order_drbd_db \
    inf: ms_drbd:promote group_db:start

  verify
  commit
  quit

設定内容が正しいことを確認し、mysql が起動するのを待ちます。

crm configure show
watch crm_mon -1n

mysql

crm_mon で db01 と db02 のどちらで mysql が起動しているか調べて、起動している方で mysql に接続します。

crm_mon -1n
mysql mysql

いらない権限を削除して wordpress 用のデータベースを作成します。

delete from user;
delete from host;
delete from db;

grant all on *.* TO root@localhost with grant option;
grant all on *.* TO root@127.0.0.1 with grant option;

flush privileges;

create database wordpress;
create user wordpress@'%';
set password for wordpress@'%' = password('wordpress');
grant all on wordpress.* to wordpress@'%';

mysql が起動していない方のインスタンスから接続できることを確認します。

mysql -h 192.168.0.10 -u wordpress -p wordpress

glusterfs

NAS サーバで glusterfs の設定を行います。

まずは ns01 と ns02 にアタッチされたディスクのデバイス名を調べます。

fdisk -l

パーティションを切ります。

fdisk /dev/xvdj
n
p
1


w

フォーマットします。

mkfs.ext4 /dev/xvdj1
e2label /dev/xvdj1 glfs

/etc/fstab に追記します。

cat <<EOS>> /etc/fstab
LABEL=glfs /glfs/vols     ext4    defaults         0 0
EOS
cat /etc/fstab

マウントします。

mkdir -p /glfs/vols
mount -a
df

ns01 と ns02 間の通信はセカンダリ IP アドレスで指定した固定アドレスがソースアドレスになる必要があるようなので、ルーティングを変更します。

ip route add 10.1.0.0/16 \
  via $(ip -4 -o route get 8.8.8.8 | grep -Eo 'via [.0-9]+' | cut -d' ' -f2) \
  src $(ip -4 -o addr show secondary | grep -Eo 'inet [.0-9]+' | cut -d' ' -f2) \
  dev eth0

ip route list

glusterfs を開始します。

service glusterd start

ns01 を作業ノードとして ns02 を peer に登録します(ns01 のみ)

gluster peer probe ns02

ns01 と ns02 で互いに認識していることを確認します。

gluster peer status

次のように表示されます。

Number of Peers: 1

Hostname: ns02
Uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
State: Peer in Cluster (Connected)

ns01 と ns02 でブリックを作成します。

mkdir /glfs/vols/data

ns01 でボリュームを作成・開始します(db01 のみ)。

gluster volume create data replica 2 transport tcp  ns01:/glfs/vols/data ns02:/glfs/vols/data
gluster volume start data

ns01 と ns02 でボリュームが作成されていることを確認します。

gluster volume info data

次のように表示されます。

Volume Name: data
Type: Replicate
Volume ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Status: Started
Number of Bricks: 1 x 2 = 2
Transport-type: tcp
Bricks:
Brick1: ns01:/glfs/vols/data
Brick2: ns02:/glfs/vols/data

ns01 と ns02 でマウントします。

mkdir -p /glfs/data
mount -t glusterfs localhost:/data /glfs/data

AP で glusterfs をマウント

ap01 と ap02 で glusterfs をマウントします。

mkdir -p /glfs/data
mount -t glusterfs ns01:/data /glfs/data

ap01 で wordpress の wp-content の中身をごっそり移動します(ap01 のみ)。

rsync -av /var/www/html/wp-content/ /glfs/data/wp-content/
chown -R apache:apache /glfs/data/wp-content/

ap01 と ap02 で wordpress の wp-content をシンボリックリンクにします。

rm -fr /var/www/html/wp-content/
ln -sf /glfs/data/wp-content /var/www/html/wp-content
ll /var/www/html/wp-content/

ap01 と ap02 から mysql に接続できることを確認します。

mysql -h 192.168.0.10 -u wordpress -p wordpress -e "select 1"

Apache を起動します。

service httpd start
chkconfig httpd on

ELB の作成

ELB を作成します。

1. Define Load Balancer

  • Load Balancer name は wp-lb
  • Create LB Inside は wp-vpc を選択
  • その他はデフォルト

2. Configure Health Check

  • Ping Path は /readme.html に変更
  • その他はデフォルト

3. Select Subnets

  • ap-northeast-1a と ap-northeast-1c を追加

4. Assign Security Groups

  • default と wp-public を選択

5. Add EC2 Instances

  • なにも選択せずに次へ(あとで cli で追加する)

ELB に AP を追加

ap01 と ap02 にログインして awc cli で ELB に自分自身を追加します。

aws elb register-instances-with-load-balancer \
  --load-balancer-name wp-lb \
  --instances "$(curl -s http://169.254.169.254/latest/meta-data/instance-id)"

ELB の DNS 名を登録

Route53 で ALIAS レコードで ELB を登録します。

ブラウザで閲覧

↑で登録したDNS名でブラウザで閲覧して、wordpress のインストール画面が表示されたら成功です。

・・・この時点で全世界にインストール画面が公開されているのでよくありませんね・・・

インストール画面で必要な項目を入力し、適当な投稿を POST できることを確認します。

フェイルオーバー

wordpress で適当に何件か投稿したあと、EC2 の Management Console で mysql が起動している側の AZ のインスタンスを皆殺しにします。

しばらくは閲覧できなくなりますが、フェイルオーバーが完了すれば wordpress が再び閲覧できるようになります。

さいごに

そのまま放置しておくと凄い勢いでチャリンチャリンと課金されていくので、いらないものは削除しておきましょう。

特にインスタンスと ELB がお高いです、EBS はそこそこです、AMI とスナップショットはお安いので記念に残しておいても良いでしょう。