27
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Amazon RDS Proxy with AWS Lambda を試してみる

Last updated at Posted at 2019-12-12

先日の re:Invent で RDS Proxy がプレビュー版ですが発表されました。

AWS Lambda は水平スケールするため RDS と相性が良くないと常々言われてきましたが、この機能でそれが解消されるのでしょうか?というわけで実際機能を試してみようと思います。

リソース作成

テスト用のVPC、RDS、Lambda Function、RDS Proxy を作成します

proxy-test.png

VPC, RDS 作成

今回 VPC、RDS はさくっと CloudFormation で作成
https://github.com/kobarasukimaro/rds-proxy-test/blob/master/cfn/rds-proxy-sample.yml

RDS の設定

パラメータグループで max_connection を 20 に設定しています。

RDS Proxy 作成

RDS Proxy は画面から作成します。 awscli でも 1.16.300 以上でしたら作成できます。

以下のような感じで作成しました。サブネットが空になってますがちゃんと設定しています。関連付けるデータベースのサブネットが自動的にサジェストされるのであまり迷うことはないかと思います。
シークレットはこの画面から作成しても候補に出てこないで予め作っておいたほうが良いかも。

スクリーンショット 2019-12-12 8.23.20.png スクリーンショット 2019-12-12 8.23.27.png スクリーンショット 2019-12-12 8.25.35.png スクリーンショット 2019-12-12 8.26.06.png スクリーンショット 2019-12-12 8.25.59.png

Lambda 作成

Lambda は Serverless Framework で作成します。

sls deploy --stage xxx

テスト用コード

Python で簡単に作成

lambda
import mysql.connector

DB_USER = os.environ["DB_USER"]
DB_PASSWORD = os.environ["DB_PASSWORD"]
DB_HOST = os.environ["DB_HOST"]
DB_NAME = os.environ["DB_NAME"]

def execute_query(event):
    config = {
        'user': DB_USER,
        'password': DB_PASSWORD,
        'host': DB_HOST,
        'database' : DB_NAME,
    }

    cnx = mysql.connector.connect(**config)
    cursor = cnx.cursor()
    query = ("SELECT SLEEP(10)")
    cursor.execute(query)

def lambda_handler(event, context):
    execute_query(event)

Lambda Function に RDS Proxy を登録

しなくても Proxy に接続できました。
↓に Proxy を登録すると IAM ロールが作られるので、 IAM 認証必須の場合に必要なのかもしれません。

スクリーンショット 2019-12-12 13.06.41.png

試してみる

最大コネクションが7個ある RDS で、 10秒スリープする Lambda Function を同時に 10並列で実行してみます

実行の前に

RDS に設定した max_connection は 20 ですが、
RDS 自体がコネクション 4個と RDS Proxy が 8個確保していて、さらにテスト中はコネクションの様子を見るためにコンソールから1個コネクションを使っているため実質的に Lambda が使えるコネクションは 7個となります。

processlist
MySQL [test]> show processlist;
+-----+---------------+---------------------+------+---------+------+-------------+------------------+
| Id  | User          | Host                | db   | Command | Time | State       | Info             |
+-----+---------------+---------------------+------+---------+------+-------------+------------------+
|   2 | rdsproxyadmin | xxx.xxx.xxx.xxx:xxxx | NULL | Sleep   |    0 | cleaning up | NULL             |
|   3 | rdsproxyadmin | xxx.xxx.xxx.xxx:xxxx  | NULL | Sleep   |    0 | cleaning up | NULL             |
|   4 | rdsproxyadmin | xxx.xxx.xxx.xxx:xxxx  | NULL | Sleep   |    0 | cleaning up | NULL             |
|   5 | rdsproxyadmin | xxx.xxx.xxx.xxx:xxxx  | NULL | Sleep   |    0 | cleaning up | NULL             |
|   6 | rdsproxyadmin | xxx.xxx.xxx.xxx:xxxx | NULL | Sleep   |    0 | cleaning up | NULL             |
|   9 | rdsadmin      | localhost           | NULL | Sleep   |    1 | cleaning up | NULL             |
|  11 | rdsadmin      | localhost           | NULL | Sleep   |    0 | cleaning up | NULL             |
|  55 | rdsadmin      | localhost           | NULL | Sleep   |   14 | cleaning up | NULL             |
|  56 | rdsproxyadmin | xxx.xxx.xxx.xxx:xxxx | NULL | Sleep   |    0 | cleaning up | NULL             |
|  57 | rdsproxyadmin | xxx.xxx.xxx.xxx:xxxx | NULL | Sleep   |    0 | cleaning up | NULL             |
|  67 | rdsadmin      | localhost           | NULL | Sleep   |   34 | cleaning up | NULL             |
| 122 | rdsproxyadmin | xxx.xxx.xxx.xxx:xxxx | NULL | Sleep   |    0 | cleaning up | NULL             |
| 124 | admin         | xxx.xxx.xxx.xxx:xxxx  | test | Query   |    0 | starting    | show processlist |
+-----+---------------+---------------------+------+---------+------+-------------+------------------+
13 rows in set (0.01 sec)

Proxy なし

まずは今まで通り、普通に RDS に接続した動作を試してみます

実行

Serverless Framework でローカルから10回実行

lambdainvoke
$ for ((i=0; i<10; i++)); do serverless invoke --function proxy-test --stage xxx -r ap-northeast-1 &; done

結果

CloudWatch で結果を見てみると、10回中 3回失敗しているのを確認できます
スクリーンショット 2019-12-10 9.58.55.png

CloudWatch Logs を見ると Too many connections が発生していたので想定通りの挙動となります

error
$ aws logs get-log-events --log-group-name /aws/lambda/xxxxxxxxx  --log-stream-name "2019/12/10/[\$LATEST]xxxxxxxxxxxx"
<中略>
[ERROR] OperationalError: 1040 (08004): Too many connections
<中略>

Proxy あり

DB の接続先を RDS Proxy に変更して試してみます

実行

先ほどと同じように Serverless Framework でローカルから10回実行

lambdainvoke
$ for ((i=0; i<10; i++)); do serverless invoke --function proxy-test --stage xxx -r ap-northeast-1 &; done

結果

CloudWatch で結果を見てみると、10回中 全て成功しているのを確認できます
スクリーンショット 2019-12-10 13.20.34.png

Duration を見てみると Max が約 20秒、 Min が約 10秒となってるので、max connection からあぶれたコネクションは空きが出るまで待っているものと思われます
スクリーンショット 2019-12-10 13.22.31.png

ログも見てみましたが、 20秒かかっている実行はコネクションで待っているのが確認できます(赤枠がコネクション待ち)
スクリーンショット 2019-12-10 13.25.50.png

RDS Proxy が想定通りの役割を果たしてくれています。 Lambda + RDS 使う場合の大きな心配事が一つ解消されるので GA になるのが楽しみですね。

ハマりどころ

CloudFormation から作成したシークレットだとエラー

最初 CloudFormation から RDS 用のシークレットを作成して RDS Proxy で読み込んでみたのですが、

Client authentication failed for user "{DBユーザー名}" with auth-plugin "mysql_native_password" and TLS on. Reason: Invalid credentials.

というエラーが出てコネクションが張れませんでした。
画面からシークレットを作成した場合は上手くいったので、 CloudFormation 側で何か足りないものがあったとは思いますが原因は今の所不明です。

Lambda でコネクションするときに Too many connections が出てしまう

これはテスト用にコネクション数の設定をいじっていたのが原因だったのですが max_connection を 1 や 10 に設定したところ、 RDS と RDS Proxy がコネクションを確保するのに気づかなかったので、そっちの確保が優先されて Lambda からコネクションが張れませんでした。 RDS のデフォルトのコネクション数で使う場合には全く問題はないかと思います。

設定は正しいのに Lost connection が出てコネクションが張れない

リソースは全て構築したしコネクション数の設定も問題ないし、さあ試そうと思ったところ、 Lambda で

[ERROR] InterfaceError: 2055: Lost connection to MySQL server at '{RDS Proxy のDNS}:3306', system error: 8 EOF occurred in violation of protocol (_ssl.c:1076)

というエラーが出てコネクションできませんでした。
どうやら mysql-connector-python のバージョンが原因で、 8.0.17 だとこのエラーが発生するようです。
また、Python 3.7 でこの事象を確認してます。 3.8 では未確認です。

やり残し

コード

今回試したコード一式はこちら
https://github.com/kobarasukimaro/rds-proxy-test

27
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?