タイトル通り、「サーバ/インフラを支える技術」という本を読んで内容を要約しました。若干負荷分散についても触れています。
ちなみにこの本を読んだ後にAWSのソリューションアーキテクチャの本を読んだんですが、内容がスラスラと入ってくるようになりました。実サービスレベルでのインフラ構築を知ることでAWSのソリューションアーキテクチャにも生きてくると思います。
対象読者
- デプロイしている成果物がある学生で可用性を向上させたいと思っている人
- AWSのソリューションアーキテクチャを学ぼうと思っている人
- インフラエンジニアを目指している人
- MySQL(DB)について学ぼうとしている人
レプリケーションが必要な背景
サービスで利用されているDBが止まってしまったとき、DBサーバのプロセスの異常終了か、ディスクの容量がいっぱいになったなどの理由ならば、すぐに復旧はできるが、ディスクや電源などハードウェアが故障した場合は復旧までに時間がかかる。このような場合に短時間で復旧させる方法としてレプリケーションがある。
レプリケーションによって短時間で復旧する仕組み
レプリケーションとはデータをリアルタイムで他の場所に複製することである。DBのレプリケーションの場合は例えばDBを二台用意し、データをレプリケーションしておけば片方が故障しても短時間でDBサービスを再開できるという事になる。
実サービスでは可用性を考えるうえで、単一障害点(その箇所が故障したらシステム全体が障害となる箇所)をなくすように冗長化をすることが一般的で、DBも冗長化し、その際にレプリケーションという仕組みが利用される。
MySQLでのレプリケーションの特徴と構成
マスタとスレーブで構成されていて、マスタはクライアントからの更新と参照の両方の種類のクエリを受け付けるサーバで、スレーブはクライアントからの更新は受け付けず、データの更新はマスタとの連携でのみ行う役割のサーバである。MySQLのレプリケーション機能でサポートされているのは1台のマスタと複数のスレーブという構成である。スレーブが複数存在することができるので、SELECT文など参照系のクエリはうまく分散させて性能向上を図るという構成をつくることも可能。
特徴としてはデータコピーが非同期で行われていること。非同期なのでマスタに対して行った更新系の処理が同時にスレーブに反映されるとは限らないという事。同期レプリケーションをサポートしているRDBMSもあるが、どちらが優れているかは言えない。
MySQLのレプリケーションは「SQL文単位」で行われるのでレプリケーションされるデータの間に違いが生じてしまうことがある。例えば、ORDER BY句を伴わないLIMIT句で選ばれる行はマスタとスレーブで異なってしまう可能性があり、異なる行が更新されてしまうことだ。この問題の対策としては、問題のあるクエリを発行しないようにすることである。しかし、MySQL5.1.5以降ならば「行単位のレプリケーション機能」を使うことで解決できる。
レプリケーションの仕組み
スレーブではレプリケーションのために2つのスレッド(「I/Oスレッド」、「SQLスレッド」)が働いている。I/Oスレッドはマスタから得たデータ(更新ログ)を「リレーログ」と呼ばれるファイルにひたすら記録する。SQLスレッドはリレーログを読み取ってひたすらクエリを実行する。二つのスレッドに分かれて別々に動作することで、時間のかかるSQLがあってもレプリケーションの遅延を少なくできる。
マスタには「バイナリログ」、スレーブには「リレーログ」と呼ばれるファイルが作成される。バイナリログにはデータを更新する処理のみが記録され、参照系のクエリなどは記録されない。また、バイナリログはレプリケーションのほかにもフルバックアップからの更新分のみを保管したいという場合にも使われる。バイナリログはテキスト形式ではないので、mysqlbinlogコマンドでテキスト形式に変換すれば見れる。リレーログはバイナリログと同じだが、削除が自動でされるため、手動で削除する必要がない。また、スレーブはどこまでレプリケーションしたか(ポジション情報)、という情報を覚えている。ポジション情報は「master.info」というテキスト形式のファイルで管理されていて、SHOW SLAVE STATUS
というSQL文で確認できる。
レプリケーション構成をつくるまで
レプリケーションの条件
- マスタは複数のスレーブを持つことができる。
- スレーブはマスタをただ一つ持つことができる。
- 全てのマスタは、スレーブの中で一意なserver-idを指定しなければならない。
- マスタはバイナリログを出力しなければならない。
MySQLの設定ファイル「my.cnf」
[mysqld]
server-id = 1
log-bin = mysql-bin
log-bin-index = mysql-bin
relay-log = relay-bin
relay-log-index = relay-bin
log-slave-updates
①server-idは1~4294967295までの整数値を指定できる。
②log-binとlog-bin-indexはバイナリログの有効化及びバイナリログのファイル名とその一覧のファイル名の指定。
③relay-logとrelay-log-indexはリレーログの有効化とそのファイル名の指定。
④log-slave-updatesはスレーブでもバイナリログを出力するように指示するための設定。これは、スレーブをマスタに昇格させるステップをスムーズに進められるようにあらかじめスレーブでもバイナリログを出力しておく。
設定ファイルが書けたら、レプリケーション用ユーザ(スレーブがマスタに接続するためのユーザ)をマスタに作成する。最低限与えなければならないのはREPLICATION SLAVE権限だけである。
レプリケーションの開始はスレーブを正しく設定(マスタとスレーブのmy.cnfの違いはserver-id飲み)し、スレーブの起動時に、マスタとの関係を指示すればOK
MySQLのスレーブ+内部ロードバランサの活用例
複数のスレーブに分散する方法として、①アプリケーションで分散する方法と②内部ロードバランサで分散する方法がある。基本的には、②の方法が使われている。その理由として、②はアプリケーション側の処理を減らせたり、新しいスレーブを追加する場合でも、ロードバランサより下の部分の作業で完結できたり、アプリケーションはスレーブ群の状態を知らなくて済むといったメリットがあるからである。
スレーブ参照をロードバランサ経由で行う方法
使用するMySQLのバージョンは5.0.45でkeepalivedは1.1.15である。
内部ロードバランサの設定「keepalived.conf」
### basiv section
vrrp_instance VI {
state BACKUP
interface eth0
garp_master_delay 5
virtual_router_id 230
priority 100
nopreempt
advert_int 1
authentication {
auth_type PASS
auth_pass himitsu
}
cirtual_ipaddress {
192.168.31.230/24 dev eth0
192.168.31.119/24 dev eth0
}
}
### MySQL slave section
cirtual_server_group MYSQL100 {
192.168.31.119 3306
}
virtual_server group MYSQL100 {
delay_loop 3
lvs_sched rr
lvs_method DR
protocol TCP
real_server 192.168.31.111 3306 {
weight 1
inhibit_on_failure
TCP_CHECK {
connect_port 3306
connect_timeout 3
}
}
real_server 192.168.31.112 3306 {
weight 1
inhibit_on_failure
TCP_CHECK {
connect_port 3306
connect_timeout 3
}
}
}
これはマスタ1台、スレーブ2台の設定ファイルである。virtual_router_idで指定するVRID(VRRPルータのグループ識別子)は同一ネットワークセグメントでは仮想ルータグループごとに異なるVRIDを付けなければならない。またvirtual_ipaddressは内部ロードバランサ自身の仮想ルータアドレスに加えて、仮想スレーブ用のIPアドレスも設定してある。
内部ロードバランサの注意点
MySQLのスレーブ参照に限らず、外部ロードバランサにはない内部ロードバランサ特有の注意点がある。それは「分散方法はNATではなくDSRにする」である。NATにした場合、クライアントから見ると、パケットを送ったのとは違う相手から応答パケットが返ってくるように見えるため、戻りのパケットを受理できないからである。