【AWS】RDS Proxyを使用してLambdaからAuroraに接続する

プログラミング

こんにちは。いっちー(@tetestkake_blog)です!

AWSを使っていると、LambdaからRDBへのアクセスで難儀することがあります。

それを解決するRDS ProxyがGA(一般提供)になりましたね!(ずいぶん前ですが)

この記事ではRDS Proxyを使用してLambdaからAuroraに接続する方法を解説します。

本文中やgithubにサンプルプログラムも載せておいたので、実装の際の参考にしてみてください。

いっちー
いっちー

RDS Proxyを使用することによりLambdaからRDSへのアクセスが改善されます!

スポンサーリンク

LambdaからRDSへのアクセスはアンチパターン

LambdaからRDSへのアクセスはアンチパターンと言われています。(調べるまで知りませんでした)

その理由は、Lambdaはリクエストごとに起動し、その都度RDSに対してコネクションを張ろうとするため、

コネクション上限を超えたLambdaからのアクセスはエラーとなってしまうためです。

いっちー
いっちー

Lambda起動の頻度が高いほどコネクションが多くなってしまうのですね。。。

RDS Proxyはコネクションをいい感じに調整してくれる

そこで登場するのがRDS Proxyです。公式ドキュメントには以下のように説明があります。

Amazon RDS Proxy を使用すると、データベース接続のプーリングと共有をアプリケーションに許可してスケーラビリティを向上させることができます。RDS Proxy は、アプリケーション接続を維持しながら、スタンバイ DB インスタンスに自動的に接続することで、データベース障害に対するアプリケーションの耐障害性を高めます。RDS Proxy を使用すると、データベースに AWS Identity and Access Management (IAM) 認証を適用し、AWS Secrets Manager に認証情報を安全に保存することもできます。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/rds-proxy.html

ざっくりまとめると、RDS Proxyはコネクションをいい感じに調整してくれるサービスです。

今回は以下の手順でLambda + RDS Proxy + Auroraの接続を確認します。

  1. 検証用Auroraを立てる
  2. Auroraに接続し、データをSELECTするLambda関数を作成
  3. RDS Proxyを作成
  4. RDS ProxyをLambdaに追加し、同じくSELECTできるか確認する

検証用Auroraを立てる

まずは検証用のAuroraを立てます。検証用なのでスペックは低めです。

Amazon RDS > データベースの作成

からデータベースを作成します。

項目は以下を設定しました。

設定項目
  • データベース作成方法を選択
    • 標準作成
  • エンジンのオプション
    • エンジンのタイプ
      • Amazon Aurora
    • バージョン
      • Aurora (MySQL 5.7) 2.07.2
  • DB インスタンスサイズ
    • db.t2.small
      • 今回はサンプルなので最小で

作成を押してしばらく待つとRDSのステータスが「作成中」-> 「利用可能」に変わったので、作成成功です。

DBにはサンプルテーブルを作成してサンプルの値を入れておきます。(後からLambdaで参照する際に使用)

CREATE TABLE `fruits` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
​
INSERT INTO `fruits` (`id`, `name`)
VALUES
	(1,'orange'),
	(2,'apple'),
	(3,'banana');

Auroraに接続し、データをSELECTするLambda関数を作成

AWS Lambda > 関数の作成

からLambda関数を作成します。

設定項目
  • ランタイム
    • Python 3.8
  • 詳細設定
    • VPC、サブネット、セキュリティグループはAuroraと同じものを指定

コードは特定のテーブルから内容をSELECTするシンプルなPythonスクリプトを作成しました。

lambda_function.py

import sys
import json
import pymysql

# rds settings
# お試しなので直書き
# secrets managerなどから値を取得する方がベター
# インスタンス直
host = "<インスタンス直のエンドポイント>"
user = "<ユーザー名>"
passwd = "<パスワード>"
db_name = "<DB名>"

def lambda_handler(event, context):
    print("start!!")
    
    try:
        conn = pymysql.connect(host=host, user=user, passwd=passwd, db=db_name, connect_timeout=5)
    except Exception as e:
        print("error")
        print(e)
    
    with conn.cursor() as cur:
        cur.execute("SELECT * FROM fruits")
        for row in cur:
            print(row)
    
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

pymysqlはLambdaエディタかimportできないので、zip化してアップロード

DB接続用に使用しているpymysqlは、Lambdaのエディタからimport文を書いただけでは使えず、

以下の手順を踏む必要があります。

  1. pymysqlをローカルにインストール
  2. zip化してLambdaにアップロード

pymysqlをローカルにインストールするには以下のコマンドを使用します。

# カレントにインストール
pip3 install pymysql -t .

zipにまとめるには以下のコマンドを使用します。この際に作成したlambda_function.pyも含めます。(同じディレクトリに配置)

# zipにまとめる
zip -r app.zip /path/to/workdir/*

※仮にapp.zipという名前をつけてあります

こうしてできたzipファイルをコンソールからアップロードします。

Lambdaの該当関数 > コードタブ > アップロード元 > .zipファイル

からアップロードします。

「Test」ボタンから関数を実行し、以下の実行結果が表示されたらOKです。

start!!
(1, 'orange')
(2, 'apple')
(3, 'banana')
いっちー
いっちー

これでLambda -> RDSの接続は完成です。

この後は、間にRDS Proxyを挟んでLambda -> RDS Proxy -> RDSとします。

RDS Proxyを作成

さて、本題であるRDS Proxyを作成していきます。

手順は以下です。

  1. Secrets ManagerにRDSの接続情報を保存
  2. RDS Proxyがシークレットを読み取ることができるIAMロールを作成
  3. プロキシをLambda関数にアタッチ

Secrets ManagerにRDSの接続情報を設定

RDS ProxyがRDSにアクセスするために、Secrets ManagerにRDSの接続情報を設定します。

AWS Secrets Manager > シークレット > 新しいシークレットを保存する

から作成します。

設定項目
  • シークレットの種類を選択
    • RDSデータベースの認証情報
  • 暗号化キー
    • DefaultEncryptionKey
  • 使用するDBの選択、ユーザー名、パスワードなどは先ほど作成したAuroraの情報を指定

ARNの値は後から使用するので控えておきます。

RDS Proxyがシークレットを読み取ることができるIAMロールを作成

RDS Proxyがシークレットを読み取ることができるIAMロールを作成していきます。

Identity and Access Management (IAM) > ポリシーの作成

から、ロールに紐付けるポリシーを作成します。

以下サンプルです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetResourcePolicy",
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret",
                "secretsmanager:ListSecretVersionIds"
            ],
            "Resource": [
                "<SecretsManagerのARN>"
            ]
        }
    ]
}

次に、

IAM > ロールの作成

からロールを作成します。

先ほど作成したポリシーをロールにアタッチします。また、以下の信頼関係を定義します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "rds.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
いっちー
いっちー

信頼関係を定義しないと、RDS Proxyの設定画面にて作成したIAMが候補に上がってこないので注意が必要です!(私は小一時間ハマりました、、、)

RDS ProxyをLambda関数にアタッチ

RDS ProxyをLambda関数にアタッチします。いよいよですね。

lambda > 設定 > データベースプロキシ > データベースプロキシの追加

からRDS Proxyを追加します。

設定項目
  • RDS DB インスタンス
    • Proxyを噛ませたいDBインスタンスを指定
  • シークレット
    • 先ほど作成したシークレットを指定
  • IAM ロール
    • 先ほど作成したIAMロールを指定
  • 認証
    • この例では「パスワード」を選択

作成後にはプロキシ経由のエンドポイントが払い出されるのでメモしておきます。

いっちー
いっちー

構築物はここまででひとまず完了です!

RDS Proxyを経由したリクエストの確認

それでLambdaを実行してDBにアクセスできるか確認します。

その前にRDS Proxy経由のエンドポイントが払い出されたので、Lambdaのコードを変更します。

# インスタンス直
# host = "<インスタンス直のエンドポイント>"
# proxy経由
host = "<proxyのエンドポイント>"

これでLambdaを実行してみると、、、

(<class 'pymysql.err.OperationalError'>, OperationalError(2013, 'Lost connection to MySQL server during query'), <traceback object at 0x7ff4d20dba40>)
[ERROR] UnboundLocalError: local variable 'conn' referenced before assignment

上記のエラーが出ました。

こちらのエラーは何故かDBを再起動(停止→起動)したら治りました。

原因はわかっていません。。。(自分だけ!?)

繋がるようになったので、Lambdaを実行してみます。以下の結果が表示されたらOKです。

start!!
(1, 'orange')
(2, 'apple')
(3, 'banana')
いっちー
いっちー

PDS Proxy経由でRDSにアクセスできましたね!お疲れ様でした!

結局RDBを再起動する前の接続エラーの原因は不明でした。。。

まとめ

この記事ではRDS Proxyを使用してLambdaからAuroraに接続する方法に関して紹介しました。

RDS Proxyを使用するとコネクション周りを自動でいい感じに調整してくれるので、

LambdaからRDBに接続する際の参考にしてみてください!

参考資料

AWS学習おすすめ書籍

コメント

タイトルとURLをコピーしました