UUIDから学ぶDRBDデータ同期の仕組み

はじめに

DRBD(Distributed Replicated Block Device)は、Linux環境における高可用性(HA)クラスタの構築に欠かせない、ブロックデバイスレベルのレプリケーションソフトウェアです。DRBDがデータを正確に同期し、障害発生時にデータを守るためには、UUID と呼ばれるメタデータが重要な役割を果たしています。

本記事では、このDRBDの「データの番人」であるUUIDが、ノード間の接続状態やデータ不整合(スプリットブレイン)の際にどのように変動し、DRBDの状態を決定しているのかを、具体的な検証ログに基づいて解説します。

DRBDのUUIDとは

DRBDにおけるUUIDは、データセットの状態と履歴を追跡するための4つの要素からなる識別子です。このUUIDをピアノード同士で比較することで、データの整合性や同期の方向を決定します。

要素名称役割
第1要素Current UUID現在のデータセットのユニークなID。データ更新があるたびに変化する。
第2要素Bitmap UUID前回同期(リシンク)が行われた時点の Current UUID。通常はゼロ。
第3要素History UUID自身のディスクが保持していた過去の Current UUID。
第4要素History UUIDピア(対向ノード)のディスクが保持していた過去の Current UUID。

UUIDの比較ルール: DRBDは、この4つのUUID要素を複雑なルールに基づいて比較し、「どちらのノードが最新か」「同期が必要か」「スプリットブレインか」を判断します。

単純な回線断の場合の例

ここでは、DRBDノード間の接続が一時的に切断され、その後再接続された際のUUIDの動作を確認します。

node1とnode2の2台のサーバのディスクをDRBDで同期して、node1をPrimaryにします。

操作:

  • node2でdrbdadm disconnect r0 を実行して、ただちに drbdadm connect r0を実行します。

結果:

node1がPrimary、node2がSecondaryの状態で接続が一時切断された後、両ノードでデータの更新がないまま再接続する。

ログの詳細:

node1の/var/log/messagesは次のようになりました。

Nov 12 11:04:29 node1 kernel: drbd r0 node2: Handshake to peer 0 successful: Agreed network protocol version 122
Nov 12 11:04:29 node1 kernel: drbd r0 node2: Feature flags enabled on protocol level: 0x7f TRIM THIN_RESYNC WRITE_SAME WRITE_ZEROES RESYNC_DAGTAG
Nov 12 11:04:29 node1 kernel: drbd r0: Preparing remote state change 2906424402: 0->1 role( Secondary ) conn( Connected )
Nov 12 11:04:29 node1 kernel: drbd r0/0 drbd0 node2: drbd_sync_handshake:
Nov 12 11:04:29 node1 kernel: drbd r0/0 drbd0 node2: self 7E42409FAE235AB0:0000000000000000:E210827503590C26:6A4DC2791796CB16 bits:0 flags:120
Nov 12 11:04:29 node1 kernel: drbd r0/0 drbd0 node2: peer 7E42409FAE235AB0:0000000000000000:6A4DC2791796CB16:2288D9C85D962AC2 bits:0 flags:1120
Nov 12 11:04:29 node1 kernel: drbd r0/0 drbd0 node2: uuid_compare()=no-sync by rule=reconnected
Nov 12 11:04:29 node1 kernel: drbd r0 node2: Committing remote state change 2906424402 (primary_nodes=2)
Nov 12 11:04:29 node1 kernel: drbd r0 node2: conn( Connecting -> Connected ) peer( Unknown -> Secondary ) [remote]
Nov 12 11:04:29 node1 kernel: drbd r0/0 drbd0 node2: repl( Off -> Established ) [remote]
Nov 12 11:04:29 node1 kernel: drbd r0/0 drbd0 node2: cleared bm UUID and bitmap 7E42409FAE235AB0:0000000000000000:E210827503590C26:6A4DC2791796CB16
Nov 12 11:04:29 node1 kernel: drbd r0/0 drbd0 node2: pdsk( Outdated -> UpToDate ) [peer-state]
Nov 12 11:04:29 node1 kernel: drbd r0 node2: helper command: /sbin/drbdadm unfence-peer
Nov 12 11:04:29 node1 kernel: drbd r0 node2: helper command: /sbin/drbdadm unfence-peer exit code 1 (0x100)

UUIDの動作:

  • 再接続時のハンドシェイクで、両ノードの Current UUID (第1要素) が一致していることを確認します。
  • データが更新されていないため、DRBDはデータの一貫性が保たれていると判断し、リシンクは発生しません。

ログが示すこと:

  • drbd r0/0 drbd0 node2: uuid_compare()=no-sync by rule=reconnected
  • このメッセージは、「UUID比較の結果、再接続ルールにより同期は不要である」と判定されたことを示しており、UUIDに変更がない状態からの正常な復旧であることを裏付けます。

スプリットブレインの場合の例

スプリットブレイン(Split Brain)は、ノード間の通信が途絶した間に、両ノードが Primary に昇格し、互いに独立してデータが更新されてしまう、最も危険な状態です。

1.スプリットブレインの発生

node1がPrimaryのまま、node2もPrimaryにしてみます。node2で強制昇格(--force primary)後にデータ更新を行い、UUIDを意図的に乖離させてから再接続を実行します。

操作:

以下の操作を行って、スプリットブレインを発生させます。

  • node2でdrbdadm disconnect r0 を実行します。
  • node2でdrbdam --force primary r0を実行します。
  • node2でデバイスをマウントします。(mount /dev/drbd0 /mnt)
  • マウントによりnode2のデータが更新されます。
  • node2でマウントを解除します。(umount /dev/drbd0)
  • node2でdrbdadm connect r0 を実行します。

ログの詳細:

この時のnode1の/var/log/messagesは次のようになります。

Nov 12 11:05:55 node1 kernel: drbd r0: Preparing remote state change 3623628693: 0->1 conn( Disconnecting ) disk( Outdated )
Nov 12 11:05:55 node1 kernel: drbd r0 node2: Committing remote state change 3623628693 (primary_nodes=2)
Nov 12 11:05:55 node1 kernel: drbd r0 node2: conn( Connected -> TearDown ) peer( Secondary -> Unknown ) [remote]
Nov 12 11:05:55 node1 kernel: drbd r0/0 drbd0 node2: pdsk( UpToDate -> Outdated ) repl( Established -> Off ) [remote]
Nov 12 11:05:55 node1 kernel: drbd r0 node2: Terminating sender thread
Nov 12 11:05:55 node1 kernel: drbd r0 node2: Starting sender thread (peer-node-id 0)
Nov 12 11:05:55 node1 kernel: drbd r0 node2: Connection closed
Nov 12 11:05:55 node1 kernel: drbd r0 node2: helper command: /sbin/drbdadm disconnected
Nov 12 11:05:55 node1 kernel: drbd r0 node2: helper command: /sbin/drbdadm disconnected exit code 0
Nov 12 11:05:55 node1 kernel: drbd r0 node2: conn( TearDown -> Unconnected ) [disconnected]
Nov 12 11:05:55 node1 kernel: drbd r0 node2: Restarting receiver thread
Nov 12 11:05:55 node1 kernel: drbd r0 node2: conn( Unconnected -> Connecting ) [connecting]
Nov 12 11:06:58 node1 kernel: drbd r0 node2: Handshake to peer 0 successful: Agreed network protocol version 122
Nov 12 11:06:58 node1 kernel: drbd r0 node2: Feature flags enabled on protocol level: 0x7f TRIM THIN_RESYNC WRITE_SAME WRITE_ZEROES RESYNC_DAGTAG
Nov 12 11:06:58 node1 kernel: drbd r0: Preparing remote state change 1720572940: 0->1 role( Secondary ) conn( Connected )
Nov 12 11:06:58 node1 kernel: drbd r0/0 drbd0 node2: drbd_sync_handshake:
Nov 12 11:06:58 node1 kernel: drbd r0/0 drbd0 node2: self 7E42409FAE235AB0:0000000000000000:E210827503590C26:6A4DC2791796CB16 bits:0 flags:120
Nov 12 11:06:58 node1 kernel: drbd r0/0 drbd0 node2: peer 27E90B577DB7AA86:7E42409FAE235AB0:6A4DC2791796CB16:2288D9C85D962AC2 bits:514 flags:1120
Nov 12 11:06:58 node1 kernel: drbd r0/0 drbd0 node2: uuid_compare()=target-use-bitmap by rule=bitmap-peer
Nov 12 11:06:58 node1 kernel: drbd r0/0 drbd0 node2: I shall become SyncTarget, but I am primary!
Nov 12 11:06:58 node1 kernel: drbd r0 node2: Aborting remote state change 1720572940
Nov 12 11:06:58 node1 kernel: drbd r0 node2: conn( Connecting -> Disconnecting ) [receive-disconnect]
Nov 12 11:06:58 node1 kernel: drbd r0 node2: Terminating sender thread
Nov 12 11:06:58 node1 kernel: drbd r0 node2: Starting sender thread (peer-node-id 0)
Nov 12 11:06:58 node1 kernel: drbd r0 node2: Connection closed
Nov 12 11:06:58 node1 kernel: drbd r0 node2: helper command: /sbin/drbdadm disconnected
Nov 12 11:06:58 node1 kernel: drbd r0 node2: helper command: /sbin/drbdadm disconnected exit code 0
Nov 12 11:06:58 node1 kernel: drbd r0 node2: conn( Disconnecting -> StandAlone ) [disconnected]
Nov 12 11:06:58 node1 kernel: drbd r0 node2: Terminating receiver thread

UUIDの動作:

強制昇格により、node2は自身の Current UUID (第1要素) を新しい値に更新しました。この状態で再接続すると、両ノードの Current UUID が異なってしまうため、DRBDはどちらのデータセットが正当か判断できません。

ログが示すこと:

  • drbd r0/0 drbd0 node2: self 7E42409FAE235AB0:... (node1のCurrent UUID)
  • drbd r0/0 drbd0 node2: peer 27E90B577DB7AA86:... (node2の新しいCurrent UUID)
  • drbd r0/0 drbd0 node2: I shall become SyncTarget, but I am primary!
  • このログは、「お互いのUUIDが食い違っており、どちらを同期元/同期先とするか判断できない」という矛盾(Primary/Primary 判定)が生じたため、接続を断ち切ったことを示しています。

2.スプリットブレインの解消とUUIDの同期

スプリットブレインを修復するために、node2のデータを破棄してnode1のデータで上書きします。

操作:

  • node1のデータを正として、node2で drbdadm connect --discard-my-data r0 を実行。
  • node1(Primary)からnode2(Secondary)へ強制的にリシンクが開始される。

ログの詳細:

この時のnode1の/var/log/messagesは次のようになります。

Nov 12 11:28:14 node1 kernel: drbd r0 node2: conn( StandAlone -> Unconnected ) [connect]
Nov 12 11:28:14 node1 kernel: drbd r0 node2: Starting receiver thread (peer-node-id 0)
Nov 12 11:28:14 node1 kernel: drbd r0 node2: conn( Unconnected -> Connecting ) [connecting]
Nov 12 11:28:15 node1 kernel: drbd r0 node2: Handshake to peer 0 successful: Agreed network protocol version 122
Nov 12 11:28:15 node1 kernel: drbd r0 node2: Feature flags enabled on protocol level: 0x7f TRIM THIN_RESYNC WRITE_SAME WRITE_ZEROES RESYNC_DAGTAG
Nov 12 11:28:15 node1 kernel: drbd r0: Preparing remote state change 213972305: 0->1 role( Secondary ) conn( Connected )
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: drbd_sync_handshake:
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: self 7E42409FAE235AB0:0000000000000000:E210827503590C26:6A4DC2791796CB16 bits:0 flags:120
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: peer 27E90B577DB7AA86:7E42409FAE235AB0:6A4DC2791796CB16:2288D9C85D962AC2 bits:514 flags:1121
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: uuid_compare()=target-use-bitmap by rule=bitmap-peer
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: Resync direction reversed by --discard-my-data. Reverting to older data!
Nov 12 11:28:15 node1 kernel: drbd r0 node2: Committing remote state change 213972305 (primary_nodes=2)
Nov 12 11:28:15 node1 kernel: drbd r0 node2: conn( Connecting -> Connected ) peer( Unknown -> Secondary ) [remote]
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: pdsk( Outdated -> Consistent ) repl( Off -> WFBitMapS ) [remote]
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: send bitmap stats [Bytes(packets)]: plain 0(0), RLE 23(1), total 23; compression: 100.0%
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: pdsk( Consistent -> Outdated ) [peer-state]
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: receive bitmap stats [Bytes(packets)]: plain 0(0), RLE 28(1), total 28; compression: 100.0%
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: helper command: /sbin/drbdadm before-resync-source
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: helper command: /sbin/drbdadm before-resync-source exit code 0
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: pdsk( Outdated -> Inconsistent ) repl( WFBitMapS -> SyncSource ) [receive-bitmap]
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: Began resync as SyncSource (will sync 2056 KB [514 bits set]).
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: Resync done (total 1 sec; paused 0 sec; 2056 K/sec)
Nov 12 11:28:15 node1 kernel: drbd r0/0 drbd0 node2: pdsk( Inconsistent -> UpToDate ) repl( SyncSource -> Established ) [resync-finished]
Nov 12 11:28:15 node1 kernel: drbd r0 node2: helper command: /sbin/drbdadm unfence-peer
Nov 12 11:28:15 node1 kernel: drbd r0 node2: helper command: /sbin/drbdadm unfence-peer exit code 1 (0x100)

node2の方は次のようになります。

Nov 12 11:27:59 node2 kernel: drbd r0 node1: conn( StandAlone -> Unconnected ) [connect]
Nov 12 11:27:59 node2 kernel: drbd r0 node1: Starting receiver thread (peer-node-id 1)
Nov 12 11:27:59 node2 kernel: drbd r0 node1: conn( Unconnected -> Connecting ) [connecting]
Nov 12 11:28:15 node2 kernel: drbd r0 node1: Handshake to peer 1 successful: Agreed network protocol version 122
Nov 12 11:28:15 node2 kernel: drbd r0 node1: Feature flags enabled on protocol level: 0x7f TRIM THIN_RESYNC WRITE_SAME WRITE_ZEROES RESYNC_DAGTAG
Nov 12 11:28:15 node2 kernel: drbd r0: Preparing cluster-wide state change 213972305: 0->1 role( Secondary ) conn( Connected )
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: drbd_sync_handshake:
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: self 27E90B577DB7AA86:7E42409FAE235AB0:6A4DC2791796CB16:2288D9C85D962AC2 bits:514 flags:121
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: peer 7E42409FAE235AB0:0000000000000000:E210827503590C26:6A4DC2791796CB16 bits:0 flags:1120
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: uuid_compare()=source-use-bitmap by rule=bitmap-self
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: Resync direction reversed by --discard-my-data. Reverting to older data!
Nov 12 11:28:15 node2 kernel: drbd r0: State change 213972305: primary_nodes=2, weak_nodes=FFFFFFFFFFFFFFFC
Nov 12 11:28:15 node2 kernel: drbd r0: Committing cluster-wide state change 213972305 (21ms)
Nov 12 11:28:15 node2 kernel: drbd r0 node1: conn( Connecting -> Connected ) peer( Unknown -> Primary ) [connected]
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0: disk( UpToDate -> Outdated ) [connected]
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: pdsk( Outdated -> UpToDate ) repl( Off -> WFBitMapT ) [connected]
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0: Disabling local AL-updates (optimization)
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0: Setting exposed data uuid: 7E42409FAE235AB0
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: receive bitmap stats [Bytes(packets)]: plain 0(0), RLE 23(1), total 23; compression: 100.0%
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: send bitmap stats [Bytes(packets)]: plain 0(0), RLE 28(1), total 28; compression: 100.0%
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: helper command: /sbin/drbdadm before-resync-target
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: helper command: /sbin/drbdadm before-resync-target exit code 0
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0: disk( Outdated -> Inconsistent ) [receive-bitmap]
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: repl( WFBitMapT -> SyncTarget ) [receive-bitmap]
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: Began resync as SyncTarget (will sync 2056 KB [514 bits set]).
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: Resync done (total 1 sec; paused 0 sec; 2056 K/sec)
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: updated UUIDs 7E42409FAE235AB0:0000000000000000:7E42409FAE235AB0:E210827503590C26
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0: disk( Inconsistent -> UpToDate ) [resync-finished]
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: repl( SyncTarget -> Established ) [resync-finished]
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: helper command: /sbin/drbdadm after-resync-target
Nov 12 11:28:15 node2 kernel: drbd r0/0 drbd0 node1: helper command: /sbin/drbdadm after-resync-target exit code 0

UUIDの動作:

--discard-my-data オプションは、ローカルノード(node2)の全てのデータとUUIDを破棄し、ピアノード(node1)のUUIDを強制的に受け入れることを指示します。これにより、DRBDは同期方向を決定し、リシンクが始まります。

ログが示すこと:

  • drbd r0/0 drbd0 node2: Resync direction reversed by --discard-my-data. Reverting to older data!
  • drbd r0/0 drbd0: Setting exposed data uuid: 7E42409FAE235AB0 (node1のUUIDに合わせる)
  • drbd r0/0 drbd0 node1: updated UUIDs 7E42409FAE235AB0:0000000000000000:7E42409FAE235AB0:E210827503590C26
  • 同期完了後、node2のUUIDは完全にnode1のUUID値に上書きされ、両ノード間でUUIDの一貫性が回復しました。

まとめ

DRBDの運用において、データの信頼性はUUIDによって担保されています。

  • 正常な運用: UUIDが一致していることを確認し、リシンクなしで接続を維持します。
  • 障害・不整合時: UUIDの比較結果に基づいて、同期の必要性や方向を決定します。
  • スプリットブレイン時: UUIDの乖離が検出された場合、手動で --discard-my-data コマンドを使用し、どちらのUUIDを「正」とするかを明示的に指定しなければ、システムは復旧できません。

このUUIDの動作原理を理解することで、HAクラスタ運用時のトラブルシューティングがより的確に行えるようになります。