Skip to main content

ELBのNLBとRDSを組み合わせ使う場合の設定

Elastic Load Balancer (ELB) のNetwork Load Balancer (NLB) を経由してAmazon Relational Database Service (RDS) を使うことになった。

VPC間での接続をしようとすると、NLBが必要になるというのが理由。

このとき懸念となるのは、NLBのタイムアウト。
例えばNLB経由でRDSに対して、とても重いクエリを投げるとする。それが350秒を何らかの理由で超過すると、RDSとしては応答を返しているにもかかわらず、NLBがタイムアウトとなってクライアント側に応答が返らなくなってしまう。

Elastic Load Balancing sets the idle timeout value for TCP flows to 350 seconds. You cannot modify this value. Clients or targets can use TCP keepalive packets to reset the idle timeout.

というわけで、この課題を解くべく、いくつかの対応を施した。
解決策はいくつもあり、例えばAWSのドキュメントにもあるように、クライアント側でkeepaliveのパケットを送る設定にすればいいし、実際多くのクライアントがそれに対応している。
が、クライアントの設定に依拠せずに対応したい。なので、RDS側での設定を前提とする。


NLBからRDSへの接続

まずNLBからRDSへの接続について。ここでは、Target GroupsとListenerを設定する。

Target GroupsはNLBからのOutboundで、どのインスタンス・IPアドレス・Lambdaに飛ばすかを設定できる。
今回だとRDSに飛ばすために、IPアドレスを選び、アドレスはRDSのクラスターのIPアドレスを指定する。(digやhostで調べた)

ListenerはNLBへのInboundで、TCP, TLS, UDP, TCP/UDPの4つ+対象ポートを選ぶことができる。(TLSの場合はHTTPのヘッダーバージョンも指定できる)
このListenerにはアクションも指定するので、この時にTarget GroupsへForwardする設定を選ぶ。

これで例えば、次のようにすれば、NLBのDNS nameにアクセスがきたらそれが後ろのRDSに繋がれることになる。
* Target GroupsにmyRdsGroupとしてIPアドレス10.xxx.xxx.xxx:5432と指定
* ListenerとしてTCP:5432 -> myRdsGroup

ここで、早速タイムアウトについて確認するために、次のようなクエリを作って、long_query.sqlとして定義してみる。

SELECT NOW();
SELECT pg_sleep(349);
SELECT NOW();
SELECT pg_sleep(351);
SELECT NOW();

実行は次のようなコマンドでpsqlコマンドをインストールしたEC2から実行した。

psql postgres://xxxx@xxxx-private-nlb-xxxx.elb.ap-northeast-1.amazonaws.com/mydb -f long_query.sql

設定変更前なので、1つ目のsleepは応答するが、2つ目のsleepは応答がないままになってしまうはずだが、実行自体はできているのでここまでは想定通り。


RDSの設定変更

自分がRDSの設定を変更するのは初めてだったのでパラメーターを変更する方法を知らなかった。
調べていくとどうやら、parameter groupというものが見つかる。
何も特段の設定をしていないと、デフォルトのgroupが適用されているので、変更したい場合は新しくparameter groupを作る。

AWSのサポートからの情報で事前に、今回設定すべきパラメーターはtcp_keepalives_idleだと情報を得ていたので、既存の設定を確認し、パラメーターグループを適用する。
設定の確認はAWS上でもできるが、DB自体にそのパラメーターが設定されているかを確認するにはDBへのSQLで確認するのが確実。次のようにすると、回答が得られる。(事前に表示形式変更適用済み)

SELECT * FROM pg_settings WHERE name = 'tcp_keepalives_idle';

-[ RECORD 1 ]---+--------------------------------------------
name            | tcp_keepalives_idle
setting         | 7200
unit            | s
category        | Client Connection Defaults / Other Defaults
short_desc      | Time between issuing TCP keepalives.
extra_desc      | A value of 0 uses the system default.
context         | user
vartype         | integer
source          | default
min_val         | 0
max_val         | 2147483647
enumvals        |
boot_val        | 0
reset_val       | 0
sourcefile      |
sourceline      |
pending_restart | f


上記リンクにもあるが、新しいパラメーターグループに切り替える場合や、設定項目の中のDynamicとの指定がないものについては、適用するためにリブートが必要になる。

This means that the parameter group is applied, but the parameter changes haven't been applied yet. After a manual reboot of the RDS DB instance, the parameter changes are applied and the DB parameter group status for the instance changes from pending-reboot to in-sync.

ちなみに、一瞬ではわからなかったのだけど、パラメーターグループの切り替えは、対象DBを選んだ状態で画面上部の "Modify" 選択後に現れる画面にて。
またパラメーターグループ自体の取り扱いは、次のリンクが詳しい。

また、各パラメーターの意味の説明は、次のページ内の Working with PostgreSQL parameters がわかりやすかった。
この内容はSELECTで取り出したpg_settingsにあるshort_descと同じものではあるし、パラメーターグループの設定画面にも書いてあるけれど。

というわけで、新しく作ったパラメーターグループのtcp_keepalives_idleをデフォルトから300に変更。(350でタイムアウトするからそれ未満ならいくつでもいいとは思う)
リブートすると、先ほどのsettingの値とsourceの値が変わっているのがわかる。

SELECT * FROM pg_settings WHERE name = 'tcp_keepalives_idle';

-[ RECORD 1 ]---+--------------------------------------------
name            | tcp_keepalives_idle
setting         | 300
unit            | s
category        | Client Connection Defaults / Other Defaults
short_desc      | Time between issuing TCP keepalives.
extra_desc      | A value of 0 uses the system default.
context         | user
vartype         | integer
source          | configuration file
min_val         | 0
max_val         | 2147483647
enumvals        |
boot_val        | 0
reset_val       | 300
sourcefile      | /rdsdbdata/config/postgresql.conf
sourceline      | 4
pending_restart | f


これでタイムアウト確認のクエリを再度投げると、今回は2回目の351秒のpg_sleepも乗り越えて応答を返してきた。
他にもベストプラクティスが公開されているので、一定のタイミングで通しで確認したい。