レプリケーションの設定

389 Directory Server では、ひとつのユーザデータベースに対して最大 4台までのマスターを置くことができる。コンシューマーの数には制限はない。レプリケーションの単位はデータベースで、特定のサブツリーのみをレプリケーションの対象とすることはできない。ひとつのサフィックスが複数のデータベースにまたがっているとレプリケーションは不可能なので、ディレクトリサーバの構成時に注意が必要だ。

まず、基本的な用語についてクリアにしておく;

レプリカ (Replica)
レプリケーションに参加している各データベースのこと。
サプライヤー (Supplier)
レプリケーションでデータを送る方のデータベースを持つサーバインスタンス。
コンシューマー (Consumer)
データの更新を受け取る方のデータベースを持つサーバインスタンス。
レプリケーション合意 (Replication Agreement)
サプライヤーとコンシューマーとの間で締結する、レプリケーションの取り決め。どのデータベースを、いつ、どんなプロトコル (SSLTLSか非暗号化通信か) で同期するかなどを決める。レプリケーション合意はサプライヤーからコンシューマーに向かって設定する。

レプリケーションにおけるサーバの役割には以下のものがある;

シングルマスター (Single Master)
更新データを送ることを専門とするレプリカ。LDAPサーバとしてのデータ登録や更新/削除などの操作を受けることが可能だが、相手 (コンシューマー) はリードオンリーとなる。
マルチマスター (Multiple Master)
更新データを送り、且つ受け取ることのできるレプリカ。マルチマスター構成では、対となるサーバの両方がサプライヤーであると同時にコンシューマーでもある、つまり、自身、相手ともにデータ書き込みが可能で、互いに更新を同期し合う。
ハブ (Hub)
サプライヤーから更新データを受け取り、それによって更新された自身の変更をさらに別のコンシューマーへ同期する。LDAPサーバとしてはリードオンリーとなる。このようにリレー式に更新を伝えていく構成を、389 DS では カスケードレプリケーション (Cascading Replication) と呼んでいる。
専従コンシューマー (Dedicated Consumer)
更新データを受け取ること専門のレプリカで、リードオンリー。もう少し正確に言うと、データ書き換え要求を受けると、専従コンシューマーはクライアントにサプライヤーを紹介し (リフェラル: RFC3296)、サプライヤー上のデータが更新されてそれがレプリケーションによって逆輸入されるという構図になる。そのため、サプライヤーがダウンしている時にはデータ書き換えができない。

レプリケーション設定手順

例として、主をマシン centos5u.hoge.com 上のディレクトリサーバインスタンス centos5u、もう 1台が centos5u2.hoge.com 上の centos5u2 インスタンスとする。

ディレクトリサフィックスの作成

コンシューマー側にも、サプライヤーと同じディレクトリサフィックス (これからレプリケーション対象とするもの) があらかじめ存在していなければならない。ただし中身のデータや ACL は整えておく必要はない。インスタンス名はサプライヤー側と異なっていても構わない。

サプライヤーバインドDN の作成

389 DS のレプリケーションはサプライヤー主導型 (Supplier-initiated Replication)。更新データは、コンシューマーが取りに行くのではなく、いつもサプライヤーからコンシューマーにプッシュされる。その時に更新権限の確保に使われるユーザエントリが、サプライヤーバインドDN だ。サプライヤーバインドDN は、コンシューマー側のサーバインスタンス上に作る。

この DN は、レプリケーション対象となるデータベース自体の中にあってはいけない。まだ空の状態のコンシューマーデータベースをサプライヤーからイニシャライズ(初期同期)する時のことを考えれば理由は飲み込めるだろう。通例、設定管理ディレクトリツリー cn=config の直下に作ることが多いようだ。作成方法を以下に示す。

1. サーバインスタンスの停止

コンシューマーのディレクトリサーバインスタンスを停止する。

root# service dirsrv stop centos5u2
2. dse.ldif にエントリを追加

コンシューマー側の /etc/dirsrv/slapd-centos5u2/dse.ldif に下記の定義を追加する。ファイル末尾に追加すればよい。後でサーバインスタンスを再開するとファイルが再パースされて然るべき位置に納まる。

dn: cn=Replication Manager,cn=config
objectClass: inetorgperson
objectClass: person
objectClass: top
cn: Replication Manager
sn: RM
userPassword: password
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0

passwordExpirationTime の数値はパスワードを無期限にするもの。nsIdleTimeout はレプリケーション中にアイドルセッションが切られないようにするため。

3. サーバインスタンスを起動
root# service dirsrv start centos5u2

SSL/TLS の設定

レプリケーションデータの流れを暗号化したい場合には、前ページを参照して SSL/TLS の設定を済ませておく。

その他の準備

対象データベースの一部パラメータはレプリケーションで同期されないように見える。筆者が気付いたのは、データベースのパスワード格納形式だった。デフォルトの SSHA から変更している場合は、コンシューマー側データベースのパスワードハッシュ形式をあらかじめサプライヤーと合わせておいたほうがよさそうだ。

レプリケーション設定の実行

実施する作業項目を、サーバの役割毎に分類するとこうなる。

ハブ以外の場合 ハブの場合
サーバA シングルマスター マルチマスター
1-1
1-2
1-1
1-2
1-3
Rep合意
サーバB 専従コンシューマー マルチマスター
1-2
1-3
1-1
1-2
1-3
サーバB シングルマスター  
1-1
1-2
Rep合意  
サーバA ハブ
(コンシューマーとして)
1-3
(サプライヤーとして)
1-1
1-2 (Hubとして)
Rep合意  
サーバC   専従コンシューマー
1-2
1-3
1. 各インスタンスのレプリケーション設定
1-1.
当該インスタンスの Configuration タブを開き、左ペインのツリーで Replication をハイライトし、右ペインの Supplier Settings タブを開く。ここはサプライヤーとしての設定だ。
Enable Changelog にチェック。チェンジログ(更新ログ) とはデータベースで言うところのトランサクションログにあたり、レプリケーションは、そこに記録されたトランザクションをコンシューマー側で再現することによって行われる。
Changelog database directory 欄に更新ログ保存ディレクトリパスを入力 (Use default ボタンを押せばデフォルトのパスが自動的に入る)。
更新記録が野放図に膨張しないよう、レコード数か期間のいずれかで記録の破棄を設定する。

必要事項がそろったら Save ボタンで保存する。
1-2.
当該インスタンスの Replication ツリー配下のユーザデータベース (例では 1つしかないので UserRoot) をハイライトし、右ペインで、
Enable Replica にチェック。
Replica Role のからこのサーバの役割を選択。
Replica ID は、レプリケーションペアを識別するための番号 ( 1~65534) で、このデータベースがマスターである (つまりシングルマスターかマルチマスター) 時だけ指定が必要。ひとつのディレクトリサフィックスのレプリケーションに関わるサーバ群の中では、重複のないように割り振らなければならない。マルチマスター構成の場合、サーバどうしは主->副と副->主の2本のレプリケーション合意を確立するわけで、片方の Replica ID が 1 なら、もう一方では 2 など違った値にする。
Purge delay は、更新履歴データベースに古いステートレコードをどれだけの期間保持しておくかの設定。
1-3.
前画面をスクロールすると Update Settings 設定パネルがある。ここはコンシューマーとしてのパラメータ群なので、送り手専門であるシングルマスターの時はグレーアウトしている。

サプライヤーDN 欄には、自サーバ上に登録しておいたサプライヤーバインドDN を入れる。
その下のリフェラル(referral)欄には、相手サプライヤーの LDAP URL を入れる。ただし、ハブからレプリケーションを受けるコンシューマーの場合は、ハブでなくその上位のサプライヤーを指定する。LDAP におけるリフェラル (RFC3296) とは、問い合わせを発したクライアントに対して検索結果の代わりに「それについては**サーバに問い合わせなさい」という返事を返すもので、クライアントは (リフェラルを辿る設定になっていれば) 紹介された URL に問い合わせをしなおす。この欄には ldap://centos5u.hoge.com:389 といったフォーマットで URL を入力。リファー先への問い合わせをセキュアLDAPプロトコルで行わせたければ ldaps://centos5u.hoge.com:636 のように指定すればいい。この設定によって、ディレクトリサフィックスのリフェラル設定が連動して切り替わる。
最後に Save ボタンで保存するのを忘れずに。
2. レプリケーション合意の締結

全てのレプリケーションサーバで上記の設定が終わったら、レプリケーション合意を締結する。カスケード構成の場合は、先にシングルマスターからハブへ向けて合意を結び、次にハブから専従コンシューマーへ締結する。

2-1.
サプライヤー側の Configuration タブ のツリーで Replication を展開してレプリケーション対象のデータベースを右クリックし、New Replication Agreement を選択する。
2-2.
レプリケーション合意の定義名と説明を入力。
2-3.
Consumer 欄で相手のコンシューマーを指定。相手が同じ物理サーバ上にあるインスタンス (つまりマルチインスタンス) ならドロップダウンメニューで選べるが、そうでない場合は Other ボタンを押してホスト名とポート番号を打ち込む。ここで表示に上がるポート番号は対象インスタンスを特定するための便宜的なものなので、レプリケーションに関わる通信を SSL で行う予定だとしても標準ポートのほうで問題ない。
Connection 設定グループでは、通信を暗号化するかどうかと、認証方法を決める。SSLTLS を使う場合は、あらかじめ SSL/TLS の設定を完了させておかなければならない。認証メカニズムでは、Simple を選択し、Bind as に相手コンシューマー上のサプライヤーバインドDNPassword にそのパスワードを入力する (389 DS で証明書ベースの認証をセッティングするのは非常に面倒くさい)。
2-4.
Fractional Replication (部分レプリケーション) の設定。有効にすれば、特定の属性をレプリケーション対象から除外することができる。除外指定した属性は、実際はレプリケーションはされるのだが、値をヌルとみなしてコピーするので、結果として、コンシューマーに LDAP検索を掛けてもヒットしなくなる。
2-5.
同期タイミングの指定。通常は Always keep ... つまり常時同期を選択するが、曜日と時間でスケジュールすることも可能なようだ。
2-6.
レプリケーション合意締結と同時にコンシューマーデータベースのイニシャライズも実施するかどうか。イニシャライズでは、サプライヤーのデータベースがコンシューマーへフルコピーされる。
イニシャライズはサプライヤー/コンシューマーのペアに対して一度だけ行う。つまり、マルチマスターどうしでは、サーバA からサーバB へレプリケーション合意を結ぶ時にはイニシャライズを実行するが、B から A へ結ぶ際にはしてはいけない。
カスケードレプリケーション構成の場合は、シングルマスターからハブ、ハブから専従コンシューマーの両方でイニシャライズを実行する。

マルチマスターレプリケーションに必要な諸調整

競合解決エントリがヒットしないようにする

マルチマスター構成の場合、同一のディレクトリエントリ (例えばユーザ) がほぼ同時に両方のマスターに対して登録されると、レプリケーションの競合が発生することがある。そういった場合には手動でどちらかのエントリ削除してやらなければならないのだが、例えば mail 属性あるいは uid 属性の値の一意性に依存しているメールサーバなどの場合、競合の発生した途端にそのユーザはメールの送受信ができなくなってしまう。

競合発生時、389 DS のレプリケーションエンジンは、時間的に後で作られた方のエントリに nsds5ReplConflict という属性を加える。少なくとも業務が続けられるようにするには、この属性の付いているエントリが LDAP検索にヒットしないようにしておくとよい。

具体的には、ユーザ検索を代表ユーザだけで行っている場合は、その検索権限を規定している ACI (前述の ACL設定解説で言えば ou=People,dc=hoge,dc=com に掛けた ACI "UserDataAdminRetrieve") に、

(targetfilter = (!(nsds5ReplConflict=*)))

というターゲットフィルタを追加してやる。389 Management Console でやるなら、上図のように Filter for sub-entries にフィルタ部分だけを書いてやればいい。読み取りや検索をアノニマスバインドでやらせているディレクトリなら、デフォルトでディレクトリトップに規定されている "Enable anonymous access" ACI に同様の処置をしておけばよい。

単一マスターによるコンシューマー独占の予防

サプライヤーは、コンシューマーを同期する時に相手レプリカに排他ロックを掛ける。マルチマスター構成では、更新の非常に頻繁なひとつのサプライヤーがコンシューマーへのロックを独占し続けてしまい、他のサプライヤーがレプリケーションを実行できなくなることがあるらしい。これを予防するには、各マスターレプリカのレプリケーション合意の持つ 2つの属性を調整する必要がある。この現象と対処方法は、Red Hat Directory Server Administration Guide の "Preventing Monopolization of the Consumer in Multi-Master Replication" に書かれている。

ここでは、ldapmodify コマンドでオンラインのままパラメータを変更することにする。dse.ldif に直接書いてもいいのだが、それだとサーバインスタンスを再起動しなければならない。オンラインで変更すると、次回の同期から有効になる。

まず、当該マスターサーバのアプリケーション合意の DN を調べる。

user$ ldapsearch -T -D "cn=Directory Manager" -w - \
    -b "cn=config" objectclass=nsds5ReplicationAgreement
version: 1
dn: cn=RepAgree, cn=replica, cn="Suffix", cn=mapping tree, cn=config
objectClass: top
objectClass: nsDS5ReplicationAgreement
...

dn: に出力されたものが目的の DN だ。それを元に、下記の LDIF ファイルを作る (ldapmodify で直接投入しても構わないが)。

dn: <上記検索で得たDN>
changetype: modify
replace: nsds5ReplicaBusyWaitTime
nsds5ReplicaBusyWaitTime: 3
-
replace: nsds5ReplicaSessionPauseTime
nsds5ReplicaSessionPauseTime: 4

nsds5ReplicaBusyWaitTime は、相手コンシューマーがロックされていた場合に再度レプリケーションを試みるまでの間隔。nsds5ReplicaSessionPauseTime は、ひとつの同期処理が終わってから次の同期処理を開始するまでの間隔。暗黙のデフォルトは前者が 3秒で、後者は 0秒。独占を防ぐには、SessionPauseTimeBusyWaitTime よりも最低 1秒は長くする。

上記内容を、例えば anti-monopolize.ldif として保存したとする。ldapmodify で読み込ませて投入するには;

user$ ldapmodify -D "cn=Directory Manager" -w - \
    -f anti-monopolize.ldif