September 22, 2015

Nginx を使って OpenLDAP の負荷分散と高可用性をシンプルに実現する

OpenLDAP と言えば、マルチマスタ構成が組めると書いてあったり、試してみると実はシビアな環境だとトランザクションないからダメだったり、Berkeley DB を RDBMS に置き換えれば行けるんじゃねと思ったら、案の定罠満載だったりするのだけど、同様に高可用性の設計が以外と難儀する。

一般的にはミラーにして、片方をマスタとして読み書きし、スレーブ側を読込専用で使うような、要は MySQL のマスタースレーブライクに使うパターンが多い模様。そして負荷分散に関しては LB 挟んでフェイルオーバーが一般的な模様。

ざっくり言うと、gihyo の連載(ちょっと古いが)に載っている。

gihyo.jp: 図2 更新時の冗長化,検索時の負荷分散を実現


閑話休題、今回はここで紹介されている書込用ポートと読込用ポートを分けて、読込を負荷分散し可用性も担保出来る構成を Nginx を使って作りたいと思う。

POC

この構成のポイントは次の通り。

  • Nginx を利用した LDAP の冗長・負荷分散構成
    • 高価な箱物ロードバランサを必要としない
    • LDAP と Nginx は別筐体にも勿論出来るが、少ない台数でシンプルに要件を満たすべく同居させた
    • Nginx の TCP LB (L4) 機能を利用する
    • keepalived で VIP のフェイルオーバーを行う
    • LVS を利用しても似た様なことを実現できるが、Nginx の場合同時に HTTP も扱えるし、L7 LB にもなれるし、SSL (HTTPS および TCP) の紐解きをアクセラレートできる

なお、本構成はコミュニティ版 Nginx でも実現できるが、NGINX Inc. の製品版 NGINX Plus 使うとバックエンドになる LDAP のアクティブチェックと、専用 keepalived (nginx-ha-keepalived) も利用できてお得感溢れる構成になる。

keepalived 構築

Nginx (Community) でやる場合

EPEL からパッケージインストールするのが良いだろう。

設定ファイルは自分でナントカする必要がある。テンプレ的には次の様な感じか、まともに使うなら実際にはもう少し捻る必要がありそう。

/etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
	interface ethX
	state BACKUP
	priority 101
	virtual_router_id 51
	advert_int 1
	unicast_src_ip XXX.XXX.XXX.XXX
	unicast_peer {
		YYY.YYY.YYY.YYY
	}
	authentication {
		auth_type PASS
       auth_pass XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	}
	virtual_ipaddress {
		ZZ.ZZ.ZZ.ZZ
	}
}

NGINX Plus でやる場合

Plus の場合、製品になっているだけあって至れり尽くせり。サブスクリプションをアクティベートして nginx-plusnginx-ha-keepalived を入れる。

なお、AWS や Azure に代表されるクラウドサービスは、ARP というか VRRP というか、ユニキャストが基本的に通らないので、nginx-ha-keepalived は正式にはサポートされない。(ファンクションがキックするスクリプトにちょいと手を加えて API 叩けば CDP の Floating IP パターンモドキに出来る、が自己責任で。)

High Availability on AWS EC2

The HA solution based on keepalived is not supported for NGINX Plus on Amazon Elastic Cloud Compute (EC2). You can use Amazon Elastic Load Balancing (ELB) as a front-end high-availability solution for NGINX Plus. If ELB is not an option, you can use the solution described in the NGINX Plus on AWS deployment guide, which uses Corosync and Pacemaker.

nginx-ha という Corosync と Pacemaker ベースのパッケージを利用する。逆にユニキャストで 素直に組めるクラウドとしては SoftLayer が挙げられる。

話が逸れたが、パッケージをインストールした後は、nginx-ha-setup というコマンドを両系で実施し対話式にセットアップを行う。詳しくは NGINX Keepalived - High Availability Support for NGINX Plus を。

  • 作業は両系同時に行う
    • あとセッション切れると厄介なので screen なり張っておくこと
  • Step 1
    • 各ノードの IP を指定する
    • 各ノード共に second node の IP は、 対向のノードの IP を指定すること
    • Active/Standby の意味の second node ではない
  • Step 2
    • cluster IP は VIP を
    • auth key をどちらかで作り(1)、もう片方のコンソールにコピペする(2)

以上で終了。

蓋を開けてみれば、/etc/keepalived/keepalived.conf を生成して、サービス自動起動設定、起動を行っただけとも言う。

Nginx TCP LB 構築

Nginx (Community) でやる場合

  • Stable version は現状まだ TCP LB の機能が入っていない
    • 201509 現在、そのうちバージョン繰り上がるのだろうが
    • SCL や EPEL にある Nginx は Stable version なので、Mainline version の yum レポを使う
    • nginx: Linux packages : Pre-Built Packages for Mainline version 参照
    • Nginx の作者 Igor さん曰く「Mainline がいちばん安定している」というのは有名な話

NGINX Plus でやる場合

  • NGINX Plus R5 以上を使う
    • といっても、実施のパッケージバージョンだと R いくつかわからない・・・
      • nginx-plus-1.9.4-1.el7.ngx.x86_64 みたいな規則
    • yum で入る最新版を使えば良い(キリッ
    • nginx -v 叩くと分かる
      • nginx version: nginx/1.9.4 (nginx-plus-r7) とか

以下設定の抜粋、Plus 前提。Nginx (Community) でやる場合は health_check などが利用できないため、nginx -t しながら不要なものを抜い欲しい。

stream {
    # upstream
    upstream ldap_backend_read {
        zone   ldap_backend_read 128k;
        least_conn;
        server node1:8389 max_fails=2 fail_timeout=30s;
        server node2:8389 max_fails=2 fail_timeout=30s;
    }
    upstream ldap_backend_write {
        zone   ldap_backend_write 128k;
        server node1:8389 max_fails=2 fail_timeout=30s;
        server node2:8389 max_fails=2 fail_timeout=30s backup;
    }

    # Listen
    server {
        listen     389;
        proxy_pass ldap_backend_read;
        health_check interval=10 passes=2 fails=3;
        health_check_timeout 5s;
        status_zone ldap_backend_read;
    }
    server {
        listen     10389;
        proxy_pass ldap_backend_write;
        health_check interval=10 passes=2 fails=3;
        health_check_timeout 5s;
        status_zone ldap_backend_write;
    }
    proxy_buffer_size 128k;
}

上記設定だと、以下のような動きとなる。なお、タイムアウト値や振分け形式などは適宜調整のこと。

  • 389/TCP で待ち受け(ldap_backend_read: 読込専用として使う)
    • 振分けは least_conn、アクティブな接続の数が最も少ないサーバーに渡される
    • バックエンドの LDAP は 8389/TCP で接続する事を期待している
      • 今回は Nginx と LDAP を同居させているので、かぶらないようにした
  • 10389/TCP で待ち受け(ldap_backend_write: 読書両用として使う)
    • 片系を backup として宣言している為、マスタ側に 10:0 で集約される
      • ホスト障害でマスタが落ちた場合、結局マスタの LDAP も落ちているハズなのでヘルスチェックで引っかかり backup に振り分ける
      • あまり無いケースだと思うが、マスタ側の LDAP だけ落ちた場合、VIP 移動は起こらず LDAP のみ backup に振り分ける
      • 逆にマスタの Nginx だけ落ちた場合、keepalived によって VIP は移動するが、LDAP の振分けはマスタ側に行く
    • バックエンドの LDAP は同様に 8389/TCP で接続する事を期待している
  • これらのフェイルオーバー動作は Plus のヘルスチェック機能に依存している部分が大きいので、Nginx (Community) でやる場合、keepalived のフックや監視などと組み合わせて自前でチェックする機能を持たせる必要がある

LDAP のミラー化とポート変更、Nginx の HTTP LB に関してはググれば山ほど情報が出てくるので今回は割愛。

© Kazuhisa Hara 2014-2018