@ymmt2005 こと山本泰宇です。今回は去る 5 月から 6 月にかけて行った、cybozu.com のデータセンター移転作業について、失敗してしまったことを中心に解説します。
失敗と書いたのは、移転作業中に何度か、一部のお客様環境でストレージ高負荷による障害を起こしてしまったためです。移転作業自体はスケジュール通り進行し、6 月第二週に完了しています。障害に関しては、こちら(PDF)でお詫びとご報告をしていますが、この記事では技術面ならびに障害を引き起こすにいたった背景について詳述します。
移転に至った背景
まず、なぜデータセンターを移転することにしたかを説明します。
端的に言うと、当時のデータセンターが手狭になり拡張が困難だったためです。ポイントは、「拡張が困難」というところです。本当は、データセンターの移転ではなく拡張ができれば良かったのです。拡張とはどういうことかというと、複数のデータセンターを高速な専用線で接続してあたかも一つのネットワークであるかのように使うということです。
拡張を断念した理由は、当時のデータセンターは他データセンターとの接続実績が乏しく、拡張する場合は非常にコスト高になってしまうためでした。そのため、移転先にはデータセンター間の拡張実績に富み、接続コストが低いデータセンターを選定しました。
移転方式の検討
移転方式として、以下の3通りの方式を検討しました。
物理移転
定期メンテナンスのタイミングでサービスを停止し、物理的にサーバーを新データセンターに輸送する方式。サービス停止時間が長引く可能性が高いこと、および物理輸送中のサーバー破損の可能性があることから見送りました。
バックアップからリストアする方式
定期メンテナンスのタイミングでサービスを停止し、差分バックアップし、差分を新データセンターに転送してリストアする方式。これも全体の工程が数時間かかること、および直前に大きな変更が入ると差分が大きくなりバックアップ・リストア時間が延びる可能性があることから見送りました。
ストレージ事前同期方式
新データセンターに用意したストレージに事前に運用中のストレージを同期させ、定期メンテナンスのタイミングで切り替える方式。同期をし続けているため、ストレージの移転はサービス停止後数秒で確実に完了します。
もっとも安定したオペレーションを期待できるため、3番目のストレージ事前同期方式を採用することにしました。また、同期用の回線として新旧データセンター間に一時的に 1 Gbps 帯域の専用線を用意しました。
ストレージ同期の方法
結論から言うと、ストレージの同期には DRBD を非同期モードで使うことにしました。他にあり得る選択肢としては mdadm で新データセンターのストレージを --write-mostly
で登録し、--write-behind
で遅延同期を有効にする方式も考えられます。
DRBD に絞った理由は、以下の2点です。
- DRBD のほうが実績が豊富。mdadm の
--write-mostly
の実績はほとんど見当たらない。 - DRBD のほうが同期速度の調節などで調整可能な項目が多い。
当時 DRBD について調査したメモをこちらで公開します。移転作業中に発覚したことも追記しています。
DRBD による同期の詳細
既にデータが書き込まれているストレージを DRBD で同期するには、以下の手順が必要です。
- 既存のブロックデバイスと別に、DRBDのメタデータを用意する
- 既存のブロックデバイスと作成した DRBD のメタデータで DRBD ブロックデバイス(プライマリ)を作成する
- 移転先データセンターに移転元と同サイズもしくはより大きなブロックデバイスで DRBD デバイス(セカンダリ)を作成する
- 2, 3 で作成した DRBD デバイスを接続する
4 で接続すると、まず初期同期と呼ばれる同期プロセスが走ります。初期同期が終わったあとは、DRBD の非同期モードになり、プライマリデバイスへの書き込みが非同期(とはいってもセミリアルタイムに)にセカンダリデバイスに転送されるようになります。
DRBDの非同期モードは、セカンダリデバイスに書き込みが完了する前にプライマリデバイスの書き込みを完了とみなすものです。データセンター移転の目的では同期である必要はないので非同期で良いのです。ですが、DRBDの非同期モードには一つ問題となる仕様があります。それは、
プライマリデバイス側の TCP 送信バッファが一杯になると、プライマリデバイスへの書き込みが停止する
というものです。つまり、セカンダリ側の書き込みが遅延することがあればプライマリデバイスに影響してしまうのです。このことは事前の調査で分かっていましたので、対策として TCP 送信バッファをとても大きく(8 MiB)することにしました。
セカンダリデバイスは基本的に何もしていないので、書き込みが遅延することはまずないと考えていました。
まずは自社環境を移転、成功
以上で準備は万端と考えて、データセンター移転作業を開始しました。まず自社環境を移転し、問題が発生しないか検証しました。
サイボウズはグループウェアの超ヘビーユーザーです。社員数 400 人ほどとはいえ、数千人のユーザー企業と同等以上のアクセスがあります。この自社環境の移転に成功すれば、お客様環境の移転もつつがなく成功するだろうと考えていました。
そして、サイボウズ自社環境の移転は何の問題もなく、DRBD の同期速度も相当に高く設定しても障害は発生しませんでした。この結果を受けて、我々は自信を持ってお客様環境の移転を開始したのです。
そして障害は発生した
以下、発生した障害について列挙していきます。
初期同期開始直後にプライマリデバイスへの書き込みが停止
これは完全に想定外でした。なぜかというと、初期同期中はプライマリデバイスへの書き込みが停止しないと思い込んでいたためです。
なぜそう思い込んでいたかというと、初期同期後にセカンダリデバイスとの接続が切れた場合、DRBDはプライマリデバイスへの変更をダーティービットマップという領域に記録し、接続再開後、ダーティービットマップに記録された領域のみ差分同期をするようになっています。初期同期中は負荷が高くなることが想定されるので、障害を避けるために、同様にダーティービットマップを利用するものと思い込んでいました。
書き込みが停止するに至った原因は、セカンダリデバイスの遅延にありました。実はこのとき、セカンダリデバイスはバックグラウンドでRAIDを初期化中だったのです。バックグラウンド初期化は低優先度ではあるものの、DRBDの同期と重なるとセカンダリ側での書き込みが遅延することがあり、結果としてプライマリ側の TCP 送信バッファが一杯になってプライマリデバイスへの書き込みが停止する現象が発生しました。
同期速度が速すぎてプライマリデバイスへの書き込みが遅延
自社環境で問題のなかった設定だったのですが、お客様環境では障害が発生してしまいました。
アクセス数で比較すると自社環境は確かに高負荷であったのですが、お客様環境とはアクセスパターンが異なったため(お客様環境のほうがランダムアクセスが多い)、実はストレージ負荷の観点では比較的に低負荷だったのです。
結果、DRBD の同期速度が速すぎてプライマリデバイスの書き込みが追い付かなくなくなり、遅延障害となりました。最終的にお客様環境で問題が起こらない同期速度を探し当てるまで、数度に渡って障害を発生させてしまいました。
セカンダリデバイスのバックアップをとろうとして障害発生
これはうっかりミスです。本来は、DRBD の接続を切ってからセカンダリデバイスを操作すれば障害は発生しませんでした。
うっかりミスが発生した背景には、初期同期で複数回の障害が発生したため、初期同期が終わった後のオペレーションに対するレビューが手薄くなってしまったことがあります。また、私以外のオペレーション要員はセカンダリデバイスに負荷を与えるとプライマリデバイスの書き込みが停止するということを認識できていなかったことも一因でした。
切断した DRBD の接続を再開後に障害発生
上記の通り、バックアップ時には DRBD の接続を切ることにしたのですが、接続再開時には切断中に溜まったプライマリデバイスへの書き込みを同期しようとします。この同期速度は初期同期で設定した速度と同じなのですが、初期同期が夜間に終わっていたデバイスでは夜間の高速な同期速度設定のままであったため、障害が発生することになりました。
なぜ障害につながったのか
結果として複数回の障害を引き起こしてしまったのですが、その原因をまとめます。
そもそも「移転」をしなければならない状況にしてしまったこと
拡張しやすいデータセンターを最初に選んでおけば、移転作業自体を避けられました。
障害が起きやすいツールを選択したこと
結果論ですが、DRBD の非同期モードは障害につながりやすい仕様でした。 もちろん DRBD が悪いのではなく、選んだ私たちの責任です。
ストレージの負荷をアクセス数で推計したこと
ハードディスクドライブはランダムアクセスの多寡で大幅に性能が変わります。アクセス数で判断するのではなく、ストレージの余力を慎重に見極めながら同期速度を調整するべきでした。
まとめ
DRBD を使ってデータセンターを移転しようという人(ほとんどいないと思いますが...)は、障害発生するポイントに気を付けましょう。次回同様のことをする機会があれば、非同期・遅延前提の WalB を利用しようと思います。
報告書では割愛した、詳細な技術および背景情報は以上となります。障害の影響を受けたお客様には、大変ご迷惑をおかけしました。改めてお詫び申し上げます。