DRBD 調査メモ

General information

Links:

Linux 3.2.0 に含まれているのは DRBD 8.3.11。 http://www.drbd.org/download/mainline/

Ubuntu のパッケージバージョンは "Version: 2:8.3.11-0ubuntu1"。

What is DRBD?

ブロックデバイスを他のコンピューターに TCP/IP 等でミラーリングするもの。
同期・非同期が選べるが、データセンター移転用途では非同期に行う。

Clustering

クラスタリング用途では heartbeat 等と組み合わせることが多い。

heartbeat は split brain の可能性があるときにサービスを停止するが、
keepalived は二つ master ができるという点に大きな違いがある。

DRBD Proxy

商用になるが、DRBD Proxy という遠隔転送用にバッファリング&圧縮するモジュールもあるようだ。
ただ、日本円で 1 システム 36 万円とかなり高額。

How it works

Components

drbd.ko というカーネルモジュールと、drbdadm をはじめとするユーザーランドツールからなる。

drbdadm は設定ファイルを読んで drbdmetadrbdsetup を呼び出すラッパーで、
実際に何を呼び出すかは -d オプションをつけると確認できる。

States

初期同期中は secondary ディスクは inconsistent 状態にある。
レプリケーションリンクが切れたときは outdated 状態になる。

http://www.drbd.org/users-guide-8.3/s-outdate.html

Meta data

DRBD 自体の情報を管理するメタデータがある。internal だと underlying block device
内で管理されるが、すでにデータのあるブロックデバイスに外付けのメタデータをつけて DRBD
リソースとして利用することも可能。

external meta data はブロックデバイスで、自分で用意する際はサイズを計算する必要がある。
が、meta-disk で指定する分には 128 MiB (up to 4 TiB backing disk)固定。
flexible-meta-disk やるなら計算する感じかな。

create a loopback device
$ dd if=/dev/zero of=/path/to/file bs=1M count=128
$ sudo losetup /dev/loop0 /path/to/file

Setup

linux-image-virtual パッケージに drbd.ko が含まれていないので、含めるように修正する。

$ sudo apt-get install drbd8-utils

/etc/drbd.d に設定ファイルあり。

Features

Replication mode

プロトコル A が asynchronous.

http://www.drbd.org/users-guide-8.3/s-replication-protocols.html

Synchronization

初期同期と切断後の再同期処理。再同期処理は差分のみで効率的。
同期速度は設定可能。50MiB/s 以下にするのがいいかな。

チェックサムベースの同期もあるようだが、今回のような単純レプリケーションでは不要。

http://www.drbd.org/users-guide-8.3/s-configure-checksum-sync.html

On-line verification

DRBD を使用していても、underlying block device のコンシステンシをチェックできる機能。
今回のデータセンター移転用途では、当日に DRBD 解除した後で sha1sum を双方のボリューム
でチェックしてしまえばいいので使わないだろう。

http://www.drbd.org/users-guide-8.3/s-online-verify.html

Replication traffic integrity checking

ネットワークパケットにチェックサムをつける機能。つけるべし。

http://www.drbd.org/users-guide-8.3/s-configure-integrity-check.html

Disabling backing device flushes

BBU RAID コントローラーでは disable した方が性能がでるのでそうする。
メタデータの flush は害がなければ disable しないほうがいい、のかな。
どのみち external meta data にするので、enable であっても性能に大差はなかろう。

http://www.drbd.org/users-guide-8.3/s-disable-flushes.html

Configuring I/O error handling strategies

underlying block device のエラーをどうするか。detach が推奨されているが、
これをすると secondary に直接読み書きするようになってしまい、データセンター移転
の用途にはまったくそぐわない。デフォルトの pass_on ストラテジーでいい。

http://www.drbd.org/users-guide-8.3/s-configure-io-error-behavior.html

AHEAD/BEHIND mode

TCP send buffer がいっぱいになったときに inconsistent にデグレさせる機能。
アプリケーション性能を落とさないが、resync が走る。

ただ ML のやりとりを見ると、DRBD proxy がないと意義薄いとか、resync が自動的には
働かなくて接続しなおさないといけないバグあるといったやり取りがあるようだ。一番下のは、
フルに同期しなおさなくてはならないと言っていて、安定性に欠ける印象。

       -g, --on-congestion congestion_policy, -f, --congestion-fill
       fill_threshold, -h, --congestion-extents active_extents_threshold
           By default DRBD blocks when the available TCP send queue becomes
           full. That means it will slow down the application that generates
           the write requests that cause DRBD to send more data down that TCP
           connection.

           When DRBD is deployed with DRBD-proxy it might be more desirable
           that DRBD goes into AHEAD/BEHIND mode shortly before the send queue
           becomes full. In AHEAD/BEHIND mode DRBD does no longer replicate
           data, but still keeps the connection open.

           The advantage of the AHEAD/BEHIND mode is that the application is
           not slowed down, even if DRBD-proxy's buffer is not sufficient to
           buffer all write requests. The downside is that the peer node falls
           behind, and that a resync will be necessary to bring it back into
           sync. During that resync the peer node will have an inconsistent
           disk.

           Available congestion_policys are block and pull-ahead. The default
           is block.  Fill_threshold might be in the range of 0 to 10GiBytes.
           The default is 0 which disables the check.
           Active_extents_threshold has the same limits as al-extents.

           The AHEAD/BEHIND mode and its settings are available since DRBD
           8.3.10.

Floating peers

ホスト名じゃなく、VIP で peer を指定する方法。できて当然な気がするが設定ファイルの
文法的に異なっている。設定ファイルは全 peer で同一のものを共有する前提なので、自ホスト
の設定をみつけるのに普通はホスト名を使うってことか。で、ホスト名が使えない状況では
IP アドレスのどれかで判定するってところかな。

http://www.drbd.org/users-guide-8.3/s-pacemaker-floating-peers.html

Configurations

/etc/drbd.d/*.res

ここに設定ファイルを置くと、OS 起動時に /etc/init.d/drbd で起動されてしまう。
square のボリュームマウントを待つなら、ここに設定ファイルを置くのは適当ではない?

TCP send buffer size

当然指定できる。高遅延ネットワークでは大きくするべし。

/etc/drbd.d/global_common.conf
common {
    net {
        sndbuf-size 1048576;
    }
}

大量の読み書きがある場合は、送信バッファの上限をカーネルで増やす必要があるかもしれない。
やるとしたら net.core.wmem_max を増やして SO_SNDBUF で自動チューンは無効化
するので、net.ipv4.tcp_wmem は設定しなくていいだろう。

Ubuntu default

pri-on-incon-degr, pri-lost-after-sb, local-io-error のイベントハンドラーが定義されている。
とりあえず我々の用途では無視。

Sample for us

global {
    usage-count no;
}

resource ymmt-blob {
    protocol A;
    device minor 1;
    meta-disk /dev/loop0[0];         # 128 MiB loopback device

    syncer {
        rate 16M;                    # Network bandwidth limit
        c-min-rate 16M;              # Disk I/O rate
        al-extents 3389;             # few meta data update rate
    }

    net {
        max-buffers 8000;
        max-epoch-size 8000;
        sndbuf-size 0;               # TCP send buffer auto tuning
        data-integrity-alg crc32c;
    }

    disk {
        on-io-error pass_on;
        no-disk-barrier;
        no-disk-flushes;
    }

    on ymmt2 {
        address 10.1.1.1:7801;
        disk /dev/md/ymmt-blob;
    }
    on ymmt3 {
        address 10.1.1.2:7801;
        disk /dev/md/ymmt-blob;
    }
}

Operations

同期状態の確認

http://www.drbd.org/users-guide-8.3/ch-admin.html#s-check-status

$ watch -n 1 cat /proc/drbd

Initializing meta data

$ dd if=/dev/zero of=/home/ymmt/meta bs=1M count=128
$ sudo losetup /dev/loop0 /home/ymmt/meta
$ cat hoge.conf
global {
    usage-count no;
}

resource hoge {
    protocol A;
    device minor 1;
    meta-disk /dev/loop0[0];
    disk /dev/ubuntu/hoge;

    syncer {
        rate 16M;                   # Network bandwidth limit
        c-min-rate 16M;             # Disk I/O rate 
    }

    on ymmt2 {
        address 10.1.5.213:7801;
    }
    on ymmt3 {
        address 10.1.5.214:7801;
    }
}
$ sudo drbdadm -d -c hoge.conf create-md hoge
drbdmeta 1 v08 /dev/loop0 0 create-md
$ sudo drbdadm -c hoge.conf create-md hoge
Writing meta data...
initializing activity log
NOT initialized bitmap
New drbd meta data block successfully created.

$ sudo lvcreate -L 3g -n hoge ubuntu

$ sudo drbdadm -d -c hoge.conf attach hoge
drbdsetup 1 disk /dev/ubuntu/hoge /dev/loop0 0 --set-defaults --create-device
$ sudo drbdadm -c hoge.conf attach hoge
$ cat /proc/drbd 
version: 8.3.11 (api:88/proto:86-96)
srcversion: 71955441799F513ACA6DA60 

 1: cs:StandAlone ro:Secondary/Unknown ds:Inconsistent/DUnknown   r-----
    ns:0 nr:0 dw:0 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:3145472

$ sudo drbdadm -d -c hoge.conf syncer hoge
drbdsetup 1 syncer --set-defaults --create-device --rate=16M --c-min-rate=16M
$ sudo drbdadm -c hoge.conf syncer hoge

ここまででメタデータを作って、DRBD デバイスを作って、同期スピードのパラメーターを調整している。
状態としては standalone で peer なし、secondary で primary じゃないので書き込めず、
inconsistent な状態。

Promote to primary

同期開始は後日になるので、とりあえずは primary として使いたい。

$ sudo drbdadm -d -c hoge.conf -- --overwrite-data-of-peer primary hoge
drbdsetup 1 primary --overwrite-data-of-peer 
$ sudo drbdadm -c hoge.conf -- --overwrite-data-of-peer primary hoge
$ cat /proc/drbd 
version: 8.3.11 (api:88/proto:86-96)
srcversion: 71955441799F513ACA6DA60 

 1: cs:StandAlone ro:Primary/Unknown ds:UpToDate/DUnknown   r-----
    ns:0 nr:0 dw:0 dr:664 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:3145472

peer がいなくても無事 primary になった。書き込んでみる。

$ sudo mkfs -t ext4 /dev/drbd1
$ sudo mount /dev/drbd1 /mnt
$ cd /mnt
$ sudo cp -r /etc/network
$ cd
$ sudo umount /mnt
$ cat /proc/drbd
version: 8.3.11 (api:88/proto:86-96)
srcversion: 71955441799F513ACA6DA60 

 1: cs:StandAlone ro:Primary/Unknown ds:UpToDate/DUnknown   r-----
    ns:0 nr:0 dw:115836 dr:1401 al:36 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:3145536

Connecting peers

さて、同期を開始したくなったらどうするか。
内部的には IP アドレスとプロトコルを指定すればつながるようだ。お手軽。

drbdsetup MINOR net ipv4:LOCAL ipv4:REMOTE PROTOCOL --set-defaults でいける。

$ sudo drbdadm -d -c hoge.conf connect hoge
drbdsetup 1 net ipv4:10.1.5.214:7801 ipv4:10.1.5.213:7801 A --set-defaults --create-device 
$ sudo drbdadm -c hoge.conf connect hoge
$ cat /proc/drbd
version: 8.3.11 (api:88/proto:86-96)
srcversion: 71955441799F513ACA6DA60 

 1: cs:WFConnection ro:Primary/Unknown ds:UpToDate/DUnknown A r-----
    ns:0 nr:0 dw:115836 dr:1733 al:36 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:3145536

まだ反対側の peer を立ち上げていないと waiting for connection 状態になる。
反対側の peer も作ってみる。

反対側のpeer
$ dd if=/dev/zero of=/home/ymmt/meta bs=1M count=128
$ sudo losetup /dev/loop0 /home/ymmt/meta
$ sudo drbdadm -c hoge.conf create-md hoge
$ sudo lvcreate -L 3g -n hoge ubuntu
$ sudo drbdadm -d -c hoge.conf attach hoge
drbdsetup 1 disk /dev/ubuntu/hoge /dev/loop0 0 --set-defaults --create-device --on-io-error=pass_on --no-disk-barrier --no-disk-flushes 
$ sudo drbdadm -c hoge.conf attach hoge
$ cat /proc/drbd 
version: 8.3.11 (api:88/proto:86-96)
srcversion: 71955441799F513ACA6DA60 

 1: cs:StandAlone ro:Secondary/Unknown ds:Inconsistent/DUnknown   r-----
    ns:0 nr:0 dw:0 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:d oos:3145472

$ sudo drbdadm -d -c hoge.conf syncer hoge
drbdsetup 1 syncer --set-defaults --create-device --rate=16M --c-min-rate=16M --al-extents=3000 
$ sudo drbdadm -c hoge.conf syncer hoge

$ sudo drbdadm -d -c hoge.conf connect hoge
drbdsetup 1 net ipv4:10.1.5.213:7801 ipv4:10.1.5.214:7801 A --set-defaults --create-device --max-buffers=8000 --max-epoch-size=8000 --sndbuf-size=0 --data-integrity-alg=crc32c 
$ sudo drbdadm -c hoge.conf connect hoge

$ cat /proc/drbd
version: 8.3.11 (api:88/proto:86-96)
srcversion: 71955441799F513ACA6DA60 

 1: cs:SyncTarget ro:Secondary/Primary ds:Inconsistent/UpToDate A r-----
    ns:0 nr:128128 dw:128000 dr:0 al:0 bm:7 lo:1 pe:0 ua:1 ap:0 ep:1 wo:d oos:3017728
        [>....................] sync'ed:  4.3% (3017728/3145728)K
        finish: 0:02:44 speed: 18,284 (18,284) want: 16,384 K/sec

無事 initial full sync が走り出す。

Disconnect during initial synchronization

初期同期中にあえて接続を切ると、最初からやり直しになる。
この場合は checksum-based synchronization を有効にすれば少しは速くなるだろう。

なお、disconnect した後は同期速度の設定が消えるので、connect の前に drbdadm syncer の設定をやり直さないといけない。

Change synchronization rate temporarily

デフォルト 10MiB/s を 30MiB/s にする。

$ sudo drbdsetup 1 syncer -r 30M

なお、-m/--c-min-rate の制限は secondary 側でしか有効にならないので、注意。
--c-min-rate=1 だとまったく速度でないので、-r と同じ設定値に secondary 側でするのが吉。

Disconnection and detach

接続を切って underlying block device を detach する。
Primary デバイスは disconnect の後、secondary にしてやらないと detach ができない仕様。

$ sudo drbdadm -d -c hoge.conf disconnect hoge
drbdsetup 1 disconnect
$ sudo drbdadm -d -c hoge.conf detach hoge
drbdsetup 1 detach
$ sudo drbdadm -c hoge.conf detach hoge
1: State change failed: (-2) Need access to UpToDate data

(失敗するので secondary にしてやり直し)

$ sudo drbdadm -c hoge.conf secondary hoge
$ sudo drbdadm -c hoge.conf detach hoge
$ cat /proc/drbd
version: 8.3.11 (api:88/proto:86-96)
srcversion: 71955441799F513ACA6DA60

 1: cs:Unconfigured

Internals

Initial sychronization

初期同期中は、アプリケーションへの書き込みは primary で保持するのではなく、
同期的に secondary に転送する。このため、secondary で MD sync やバックアップを
すると primary への書き込みがブロックして障害につながる。

また初期同期が途中で切れると、再度頭からやり直しとなる。

--c-min-rate でのアプリの I/O 検出は、secondary でアプリケーションへの
書き込みリクエスト数をカウントすることで行っている。primary で設定しても効果は
ないので注意。

Device status

drbdsetup MINOR sh-status すると以下のような情報が得られる。

$ sudo drbdsetup 1 sh-status
_minor=1
_res_name=UNKNOWN
_known=Configured

_cstate=StandAlone
_role=Secondary
_peer=Unknown
_disk=UpToDate
_pdsk=DUnknown

_flags_susp=
_flags_aftr_isp=
_flags_peer_isp=
_flags_user_isp=

_resynced_percent=

_sh_status_process

接続情報や known の値にどんなのが入るかは、この辺みるとわかる。
このデータは Python の shlex.split(data, True) で楽に解析できる。

残念ながら、backing device 情報をカーネルから取得する手段は提供されていない。
udev が作るシンボリックリンクはあるが、detach しても削除されないのでいまいち。

イベントハンドラー

DRBD はカーネルモジュールなので、種々のイベントを通知するのはユーザーランドのプログラムを呼び出すことになる。
で、この呼び出す仕組みが少々曲者で、drbdsetup で登録するのではなく、カーネルが直接 drbdadm
呼び出して、設定ファイルに記述されたハンドラープログラムをさらに起動するという形式になっている。

設定ファイルではなく直接 drbdsetup だけで操作しようという Square のようなプログラムには厄介なことに、
例えば before-resync-target ハンドラーを呼び出そうとして設定ファイルがないのでピアとの通信を諦めて
しまうという問題が発生してしまう。

回避策としては、drbd.ko のモジュールパラメーター usermode_helperdrbdadm 以外のプログラム
を指定すると、起動するプログラムを変えることができるのでそうする。

/etc/modules
drbd usermode_helper=/bin/true

もしくは、

$ sudo modprobe drbd usermode_helper=/bin/true

確認方法:

$ cat /sys/module/drbd/parameters/usermode_helper
/bin/true