最初に
前回「基本形」としてお伝えしていたOpenLDAPのsyncrepl機能ですが、まずは記事の内容で少し補足です。
これまで「マスターとなるサーバはサプライヤ、スレーブとなるサーバはコンシューマ」と記述していましたが、OpenLDAPの場合はそれぞれ、プロバイダ、コンシューマと呼ぶのが一般的でした。サプライヤという用語も間違いでは無いのですが、こちらは主にSun Java Directory ServerやRed Hat Directory Serverなどで使用されている用語ですので、OpenLDAPに合わせるためここからは「プロバイダ」に統一するようにします。
syncreplが提供するレプリケーション
一般的なデータベースの冗長構成ではデータの完全同期が期待さることが多いかもしれません。たとえば、2台のDBサーバが存在したとして、完全同期のレプリケーションでは、クライアントが一方のサーバに新規データを登録した瞬間に最新のデータが2台のDBサーバ上に作成されていると保証されますので、すべてのクライアントはどのDBサーバに接続しても常に同じ結果を得ることができます。
一方、非同期のレプリケーションでは常に同じデータが全サーバに保存されているとは限らないため、DB登録の1分後に最新データがもう一方のDBサーバに反映される可能性もあります。
OpenLDAPのsyncrepl機能もまた非同期のレプリケーションですが、設定次第で
- 極力リアルタイムにレプリケーションを行う
- 適度なインターバルをもってレプリケーションを行う
といったことが可能です。通常の用途では前者を選択することが多いと思いますが、たとえばデータの更新頻度が少ない環境で、1時間など長めのインターバルを持たせておくことにより、サーバ負荷の軽減につながるのはもちろんのこと、誤ってプロバイダ側のデータを削除してしまった場合でも、最大1時間以内であればコンシューマ側に以前のデータが残っているため随時ロールバックを行うことができるようになるという特徴もあります(運悪く、事故発生直後に同期が行われてしまうとロールバックもできませんが…)。
refreshOnlyとrefreshAndPersist
前述の2通りのレプリケーションですが、具体的にはslapd.conf中のrefreshOnlyとrefreshAndPersistという項目が設定の鍵になります。
refreshOnlyを指定する場合、コンシューマは定期的にプロバイダの389/tcpにTCPセッションを張ることでデータの同期を試みます(polling方式)。
一方、refreshAndPersistの場合は文字通り接続の維持が行われるため、コンシューマはプロバイダの389/tcpにセッションを張り続け、更新データの有無をリアルタイムにプロバイダから受け取ることができます(listening方式)。では、それぞれの具体的な設定を見ていきましょう。
まず、プロバイダ側で準備する内容はリスト1の通りで、それほど特別な設定は必要ありません。
次に、コンシューマ側はリスト2、3のようになります(わかりやすいよう各所にコメントを入れていますが、実際にはこの場所にコメントは許されていないため注意してください)。
これらの挙動の違いをさらに理解するために、tcpdumpによりパケットをキャプチャしつつプロバイダ側に新規データを登録し、一連の様子を見てみます。
コンシューマ側のslapdを起動すると、まずは最新データ同期のためにプロバイダへの接続が行われます(赤字部分)。この最終行を見ると、FINフラグによりTCP接続が一旦切られていることが分かります。その後intervalで指定した5分が経過すると、コンシューマはプロバイダに対して新たなTCPセッションを確立し、データの同期確認と要求を行っています。このように、同期確認の度にTCPセッション張り直すのがrefreshOnlyの特徴です。
次はrefreshAndPersist時の結果です。
まず、OpenLDAPを起動すると先ほど同様、最新データ同期のため赤字部分の通信が発生します。しかし赤字部分の最終行を見れば分かるとおり、389/tcpへの接続は維持されたままです。
次に、プロバイダ側に新規データを2件追加すると、青字部分のパケットが発生します。プロバイダからコンシューマに向けてデータがプッシュされていることが分かるはずです。最後に、コンシューマ側のslapdを停止させると、レプリケーションのために接続維持されていたプロバイダ側への接続が緑時部分で失われています。
注意点としては、接続維持中にプロバイダ側を再起動してしまうと、コンシューマの接続維持が失われてしまい、それ以降データの同期が行われなくなってしまいます。そのためには、retryパラメータを使って定期的にリトライを行うようにします。この例では5秒間隔で10回の接続を試行し、それでも接続できない場合は300秒(5分)おきにリトライを繰り返すようになります。
このように、常にコンシューマがプロバイダに対してセッションを張りっぱなしにしておくのがrefreshAndPersistの特徴です。
同期対象エントリの把握
2通りのレプリケーションがあることはわかりましたが、それぞれの方法でコンシューマはどのようにして同期対象のデータを把握し取得するのか見ていきましょう。
まずキーワードとなるのはentryCSN(Change Sequence Number)というLDAP属性です。この値は、通常ldapsearchコマンドで見かけることは無いかもしれませんが、次のように属性を指定すればクライアントから確認することができます。
内容から容易に想像できるように、entryCSN属性は日付をベースにした識別値です。この値はエントリを追加した際、または更新した際に新たに付与されるもので、他のDNにはないユニークな値となっています。
まずは、コンシューマ側に何もデータが無い状態で、これから初めてレプリケーションを行うものとします。コンシューマはまず、slapd.confに指定されたバインドDNやパスワード、検索ベースなどを用いて、プロバイダ側に接続し、エントリを全て取得し、ダウンロードします。
このとき、コンシューマはプロバイダ側から得られた最新のentryCSNを取得、記憶しておきます。そして次回以降の同期要求の際には、バインドDN、パスワード、検索ベースなどと同様、このentryCSNを検索条件に加え、前回取得したCSNよりも大きな値が見つかった場合にはそのエントリを取得し同期する(エントリ削除の場合は削除メッセージを受け取る)、という挙動でレプリケーションを実現しています。
コンシューマに対する更新要求
同期のタイミングを考慮しないものとすれば、基本的に両サーバにはいつでも最新のデータが保存されているため、クライアントはプロバイダ、コンシューマ、どちらに接続しても同じ検索結果を得ることができます。つまり、複数のクライアントを複数のサーバに接続させることで、検索の負荷分散を行うことができるようになっています。しかしデータの更新についてはどうでしょうか?コンシューマに無理矢理データを登録しようとすると次のようなエラーが発生します。
これは当然のことで、仮にプロバイダではなくコンシューマのデータだけが更新されてしまうと、データの整合性が保たれなくなってしまいます。従って、更新要求が発生した場合には、正しい更新先をupdate referralという形でクライアントに伝える必要があるのです。
そのため、slapd.confに次のような設定を加え、コンシューマへの更新要求はプロバイダにリダイレクトするようにしておきます。
こうすることによってエラー内容が次のように変わります。
やはりエラーにはなってしまうのですが、その中には「ldap://10.0.100.21:389/でデータの登録を行うように」と指定されているため、賢いクライアントであれば、ユーザにエラーを表示することなくプロバイダ側のデータを更新することができます。
ちなみに、コンシューマから返されたこのメッセージをエラーとしてユーザに見せるのか、または親切にリダイレクト先を更新するのか、はクライアント側のソフトウェアの実装次第です。
セキュアなレプリケーション用DN
refreshOnly、refreshAndPersist、どちらを選択するにせよ、Web上の多くの文献では、
「レプリケーション用にcn=Manager,dc=example,dc=comを使用していますが、実際にはセキュリティを考慮してレプリケーションの専用DNを使ってください」
といった記述を見かけます。事実筆者も今まで多くの場合このような解説をしてきました。ですが、たまには実用性を考慮して、専用DNを作成することでセキュリティの向上につなげたいと思います。今回は
というDNをレプリケーション専用のDNとすることにしましょう。まずはプロバイダ側でエントリの作成です(userPasswordの値はabc123という文字列を暗号化したもので、slappasswdコマンドによって作成しています)。
ではいつものようにエントリを登録します。
次に、このDNを用いてレプリケーションを行うことができるよう、プロバイダ側のslapd.confで適切なACLを設定します。単に
としてしまっては、万が一レプリケーション用のパスワードが漏洩した場合にプロバイダのエントリが漏洩、または改ざんされてしまう可能性もありますので、
- cn=replication用の権限は全エントリの閲覧のみ(データ更新の必要はない)
- cn=replicationはコンシューマからのみ利用される(接続元を指定)
を実現することで、レプリケーション用DNはコンシューマからのみ安全に利用できるようになります。
まとめ
今回はOpenLDAPの基本形となる2通りのレプリケーション方法を解説してきました。2台以上のLDAPサーバを立て、クライアントが複数のサーバを参照することで、障害時の冗長性はもちろんのこと、サービスの負荷分散を実現することができます。
しかし実際には様々な課題が存在します。まずはこの方式で冗長性を実現させるためには、クライアントに複数のLDAPサーバを指定する必要があります。たとえば、Linux OSでLDAP認証を実現させるための/etc/ldap.confでは
uri ldap://接続先1/ ldap://接続先2/
というフォーマットが許可されています。しかし、ソフトウェアによっては接続先が1つか指定できない場合があります。その場合にはクライアントが複数のサーバを選択するクライアント方式ではなく、ロードバランサ+仮想IPを用いてサーバ側で接続先のコントロールを行う必要があります。
それ以外でも、更新頻度の多いLDAPサーバの場合は書き込みの負荷分散や冗長性を保つことができないため、さらなる対策が必要です。これらの課題については次回以降触れていくことにしましょう。
設定ファイル
今回の設定ファイル全体を貼り付けておきますので、参考にしてみてください。