Amazon Web Services ブログ

サーバレスアプリケーションから AWS データベースをクエリする

AWS データベースの顧客は、Amazon Elastic Compute Cloud (Amazon EC2) ホストを仮想プライベートクラウド (VPC) で起動してデータベースクライアントとして使用する必要がないため、費用を節約し、シームレスに拡張し、高可用性の実現します。EC2 ホストのプロビジョニングまたは管理を必要とせず実行するアプリケーションを、サーバーレスアプリケーションと呼んでいます。

この記事では、データベースが公開されていない場合でも、URL から AWS データベースを照会する方法について示します。この方法は、serverless-to-serverless クエリのために Amazon Aurora Serverless データベースに対しても実行が可能です ! Amazon NeptuneAmazon Relational Database Service (Amazon RDS) for MySQL 、または Amazon RDS for PostgreSQL の各 Python スクリプトの選択を提供しています。また、次の 2 つのコンポーネントを設定して作成する方法についても説明します。

  • バックエンドデータベースに対してクエリを実行する AWS Lambda 関数
  • URLにアクセスする際に Lambda 関数を呼び出す API Gateway REST API

これらのコンポーネントを簡単に可能な AWS CloudFormation テンプレートを提供します。また、厳密には Key-Value ストアではない Amazon VPC のデータベースを照会するための具体的なティップストラブルシューティング のセクションも含みます。

Python のコードサンプルは、他のデータベースエンジンに容易に適応可能なベストプラクティスを適用します。Neptune では、SPARQL の例を Gremlin に適応させることができます。

Python コードと AWS CloudFormation テンプレートは、awslabs/rds-support-tools GitHub リポジトリからダウンロードが可能です。

この記事は、AWS Lambda 、Amazon API Gateway 、AWS CloudFormation の経験がなく、Python の経験が少ない AWS データベースユーザーを対象としています。ステップ完了までの所要時間は約 30 分です。

始める前に

サーバーレスアプリケーションが適切かどうかを確認するには、「AWS Lambda の制限」「API Gateway の制限」をご確認ください。特に、ランタイム制限はクエリに適用されるためご注意ください。API Gateway が Lambda 関数を同期で呼び出すと、 API Gateway ランタイム制限が適用されます。非同期の場合、Lambda ランタイム制限が適用されます。別な呼び出し方法 を選択したも、Lambda ランタイム制限が適用されます。この例では、Lambda 関数を同期で呼び出します。

前提条件

  • VPC で AWS データベースインスタンスを起動しました。好ましくは NeptuneRDS for MySQL 、RDS for PostgreSQL を使用します。
  • セットアップする目的で、一時的な Amazon EC2 クライアントホスト 、好ましくは Amazon Linux をデータベースと同じ VPC および AWS リージョンに立ち上げました。
  • EC2 クライアントホストのサブネット ID とセキュリティグループ ID が分かっています。
  • クライアントホストのセキュリティグループ ID (CIDRではない) は、データベースセキュリティグループのインバウンドルールのソースとして許可されています。ポート範囲にはデータベースポートが含まれます。
  • 優先データベースクライアントソフトウェアがクライアントホストにインストールされ、このホストからデータベースへの接続が確立されています。データベースユーザーは、データベースオブジェクトを作成し、このデータベースに挿入したり、このデータベースからクエリを実行する権限が与えられます。
  • Python 3.6 がクライアントホストにインストールされています。virtualenv を使用して Amazon Linux ホストに Python 3.x をインストールする方法については、5 分間による AWS ナレッジセンター動画の「How do I create an isolated Python 3.4 environment」 ( Python 3.4 の代わりに Python 3.6 をインストール方法)をご覧ください。
  • AWS ユーザーには、IAM ロール、Lambda 、API Gateway 、および AWS CloudFormation スタック の作成と管理、ならびに Amazon CloudWatch Logs を表示する権限が与えられます。
  • 完全なコンソールアクセス権限が与えられます。また、クライアントホスト上で AWS CLI を設定し、権限エラーなしで次のコマンドを実行することも可能です。
    aws s3 ls
  • GitHub をご使用でない場合は、「these instructions for downloading awslabs/rds-support tools」でツールのダウンロード方法をご覧ください。

ステップ

以下のセクションでは、サンプルの設定とテストについて説明します。

  1. ソリューションの設定
  2. サンプルデータを読み込む
  3. クライアントホストのコマンドラインから提供された Python コードをテストする
  4. Python ソースコードを Python パッケージと一緒に圧縮する
  5. Lambda 関数と API Gateway REST API を作成する
  6. URL からデータベースを照会する
  7. 次のステップ
  8. レビューのティップスとトラブルシューティング
  9. まとめと最終ステップ

ステップ 1 : ソリューションの設定

  1. クライアントホストで、データベースのプロジェクトディレクトリを例のように作成します。
    mkdir ~/svls 
    cd ~/svls 
  2. GitHub リポジトリからクライアントホストにスクリプトと AWS CloudFormation JSON テンプレートをダウンロードします。
  3. データベースエンジンの Python スクリプト名を serverless-query.py に変更します。AWS CloudFormation テンプレート名の serverless-query-cfn.json をそのまま使用します。
  4. dos2unix をインストールし、それによってすべてのファイルを実行します。
    sudo yum install -y dos2unix
    dos2unix *
  5. Python 3.6 環境がアクティブになっていることを確認します。
    which python
    ~/venv/python36/bin/python
  6. Python 3.6 virtualenv 環境の中から、コードサンプルにインポートされたデータベースクライアント用のモジュールを pip3 がインストールします。
    データベース pip3 を使用してインストールするモジュール
    MySQL pip3 install pymysql
    PostgreSQL pip3 install psycopg2-binary
    Neptune/SPARQL pip3 install SparqlWrapper
  7. 必要な権限を設定します。
    chmod a+r ~/svls
    chmod 744 serverless-query.py
    chmod 444 serverless-query-cfn.json
  8. pip3 を使用してインストールしたパッケージを見つけ、そこにグローバルリードアクセス権限があることを確認します。virtualenv を使用している場合は、次のいずれかのディレクトリ内にある必要があります。
    ls $VIRTUAL_ENV/lib64/python3.6/site-packages
    sudo chmod a+r -R $VIRTUAL_ENV/lib64/python3.6/site-packages
    
    #or
    
    ls $VIRTUAL_ENV/lib/python3.6/site-packages
    sudo chmod a+r -R $VIRTUAL_ENV/lib/python3.6/site-packages

ステップ 2 : サンプルデータを読み込む

データベースエンジンに付属の挿入スクリプトを使用して、サンプルデータをデータベースに読み込みこみます。

ステップ 3 : クライアントホストのコマンドラインから提供された Python コードをテストする

  1. コマンドラインテストの環境変数を設定します。
    MySQL and PostgreSQL:

    export ENDPOINT='your-database-endpoint'
    export PORT='your-database-port'
    export DBUSER='your-database-user'
    export DBPASSWORD='your-database-user-password'
    export DATABASE='your-database-name'
    

    Neptune :

    export ENDPOINT='your-database-endpoint'
    export PORT='your-database-port'
    export DBUSER='None' 		
    export DBPASSWORD='None'
    export DATABASE='None' 
  2. マンドラインから Python スクリプトを実行します。
    ./serverless-query.py

    Neptune/SPARQL の期待される結果 :

    ['amzn://data/hello1 , amzn://data/world1', 'amzn://data/hello2 , amzn://data/world2']

    MySQL と PostgreSQL の期待される結果 :

    [('hello1', 'world1'), ('hello2', 'world2')]
  3. この print ステートメントをコメントアウトすることがきます。print ステートメントをそのままにすると、クエリ結果が CloudWatch Logs に出力されます。
    # print(results_list)

ステップ 4 : Python ソースコードを Python パッケージと一緒に圧縮する

  1. pip3 を使用してインストールしたデータベースクライアントの Python パッケージと一緒にスクリプトを圧縮します。詳細については、展開パッケージの作成をご参照ください。Python パッケージがインストールされた packages ディレクトリに移動し、ディレクトリの内容を圧縮します (ディレクトリそのものではありません) 。zip ファイルをプロジェクトフォルダに配置します。すべての隠しファイルを含めるには、zip -r9 オプションを使用します。virtualenv をご使用の場合、クライアントパッケージは次のいずれかのディレクトリ内にあります。
    cd $VIRTUAL_ENV/lib64/python3.6/site-packages
    zip -r9 ~/svls/serverless-query.zip *
    
    # or
    
    cd $VIRTUAL_ENV/lib/python3.6/site-packages
    zip -r9 ~/svls/serverless-query.zip *
  2. プロジェクトディレクトリから、serverless- query.py スクリプトを zip ファイルに追加します。
    cd ~/svls
    zip -g serverless-query.zip serverless-query.py

ステップ 5 : Lambda 関数と API Gateway REST API を作成する

  1. Amazon S3 バケットがない場合は、クライアントホストから Amazon S3 バケットを作ります。
    aws s3 mb s3://your-s3-bucket-name
  2. S3 バケットに zip ファイルを読み込みます。
    aws s3 cp serverless-query.zip s3://your-s3-bucket-name
  3. AWS CLI を使用して AWS CloudFormation スタックを作成します。
    aws cloudformation create-stack \
    --stack-name serverless-query-cfn \
    --template-body file://serverless-query-cfn.json \
    --region your-database-region \
    --capabilities CAPABILITY_NAMED_IAM \
    --parameters '[
       {"ParameterKey":"PEndpoint","ParameterValue":"your-database-endpoint"},
       {"ParameterKey":"PPort","ParameterValue":"your-database-port"},
       {"ParameterKey":"PDatabase","ParameterValue":"your-database-name"},
       {"ParameterKey":"PDbUser","ParameterValue":"your-db-user-name"},
       {"ParameterKey":"PDbPassword","ParameterValue":"your-db-user-password"},
       {"ParameterKey":"PS3Bucket","ParameterValue":"your-S3-bucket-name"},
       {"ParameterKey":"PSubnetIds","ParameterValue":"your-EC2-client-SubnetId"},
       {"ParameterKey":"PSecurityGroupIds","ParameterValue":"your-EC2-client-SecurityGroupId"}
     ]'

    Neptuneユーザーは、これらの変数を「None」に設定する必要があります。

       {"ParameterKey":"PDatabase","ParameterValue":"None"},
       {"ParameterKey":"PDbUser","ParameterValue":"None"},
       {"ParameterKey":"PDbPassword","ParameterValue":"None"}, 
  4. AWS CloudFormation コンソールダッシュボードから、serverless-query-cfn スタックの[Events] タブで進捗状況を確認します。スタックのステータスが ROLLBACK_COMPLETE の場合は、[Events ] タブで失敗の理由を確認します。詳細については、この記事の後半にあるトラブルシューティング変更に関するティップスをご参照ください。
  5. スタックステータスが CREATE_COMPLETE の場合、コマンドラインから Lambda 関数をテストします。
    rm -f invoke-lambda.out
    aws lambda invoke \
    --function-name ServerlessQuery \
    --region your-database-region \
    --log-type Tail invoke-lambda.out
    echo **Return Object** 
    cat invoke-lambda.out

    これらの問題を解決するには、CloudWatch Logs と、この記事の後半にあるトラブルシューティング変更に関するティップスをご参照ください。

    CloudWatch Logs は以下の手順で表示します。

    1. CloudWatch コンソールを開く。
    2. 左側のナビゲーションで Logs を選択する。
    3. /aws/lambda/ServerlessQuery を選択する。
    4. 最新の Last Event Time を持つログストリームを選択する。

    Python スクリプトから print(results_list) ステートメントをコメントアウトした場合を除き、クエリ結果もログストリームに表示されます。

ステップ 6URL からデータベースを照会する

データベースのクエリには、次の 2 つのオプションがあります。

オプション 1 : コマンドラインから URL を curl する。

api-id を入手する。

aws apigateway get-rest-apis

次に

curl https://api-id.execute-api.your-database-region.amazonaws.com/beta/query1

オプション 2 : URLをブラウザに貼り付ける。

API Gateway ダッシュボードに移動する。ベータステージの「 invoke URL 」をカットアンドペーストして、ブラウザの最後にリソース名 /query1 を追加する。

問題を解決するには、CloudWatch Logs と、トラブルシューティングとセクションの変更に関するのティップスを参照する。

ブラウザからのクエリ結果を表示する

ステップ 7 : 次のステップ

サーバーレスアプリケーションを設定してテストした後、ソリューションの最適化に役立ついくつかのベストプラクティスと管理タスクを示します。

  • AWS KMS キーを使用して Lambda 環境変数を暗号化することにより、データベースパスワードやその他の接続変数をさらに保護します。
  • Lambda 機能 VPC 設定に2番目のサブネットを追加して、可用性を向上させます。
  • S3 での静的ウェブサイトなどの外部サイトから API を呼び出す予定の場合は、API Gateway リソースに対してCORS (クロスオリジンリソース共有) を有効にする必要があります。ここで使用したプロキシ統合の統合応答が無効になっていることにご注意ください。これは、CORS の応答ヘッダーを設定する際に、 Access-Control-Allow-Origin ヘッダーを返すためにバックエンドに依存する必要があることを意味します。
  • サーバレス AWS CloudFormation テンプレートの管理を容易にするために、AWS Serverless Application Model (AWS SAM) をお試しください。

ステップ 8 : レビューのティップスとトラブルシューティング

この例における手順を完了したら、次のガイダンスを使用して、発生する可能性のある問題に対処してください。

長いクエリを実行に関するティップス

この例では、デフォルトのタイムアウト設定を使用して短いクエリを実行しています。より長いクエリを実行するには、次のいずれかを実行します。

  • API Gateway と Lambda 両方のタイムアウトパラメータを、それぞれのランタイム制限まで上げてください。同期呼び出しでは、API Gateway のタイムアウトよりもわずかに低い Lambda タイムアウトを設定して、 REST API が正常終了するようにする必要があります。
  • Lambda 制限のみが適用されるように、Lambda を非同期に呼び出します。
  • Lambda 関数を作成し、別な呼び出し方法を選択します。Lambda ランタイム制限が引き続き適用されます。
  • 作業をバックエンドにプッシュします。例えば、マテリアライズドビューまたはカスタムバッチジョブを使用してクエリをローカルおよび自動的に処理し、ランタイム制限内で Lambda 関数からサマリー結果をクエリできるようにします。
  • データが適切に索引付けされ、クエリがチューニングされていることを確認します。最小限のデータセットをクエリするには、パーティション化とフィルタリングを使用します。アプリケーションに適切な場合は結果をキャッシュします。
  • LIMIT 節または同等のものを使用してクエリの出力を制限します。

Python コードの実行に関するティップス

Python コードサンプルでは、Lambda 、API Gateway 、具体的には MySQL 、PostgreSQL 、Neptune (SPARQL) の 2 つのリレーショナルデータベースとグラフデータベースを使用してベストプラクティスを使用しています。これらのベストプラクティスは次のとおりです。

  • 変数を初期化し、他のブートストラップ操作を Lambda ハンドラ関数の外部から実行する。これにより、ブートストラップは Lambda 実行環境 (以前は 「コンテナ」) から1回だけ実行できます。実行環境がアクティブである限り、handler 関数内の操作のみ実行されます。
  • Lambda の一般的なベストプラクティスは、Lambda 実行環境でデータベース接続を開き、各 handler 呼び出しで再利用できるようにすることです。ある時点でデータベース接続が切断された場合、handler 内部から再接続します。handler 内でグローバル接続変数を使用すると、後続の呼び出しで新しい接続が開いた状態のままとなります。しかし、MySQL と PostgreSQL の場合、ロックされた問題を引き起こす可能性のあるオープン接続を介したセッション漏洩のリスクがあります。したがって、単純な例が読み取り専用のクエリであっても、この潜在的な問題を回避するために、各 handler 呼び出しで接続を開いたり閉じたりします。Neptune 利用者 : これは、永続化データベース接続を作成しないため、SPARQL サンプルコードには適用されません。
  • handler 内の戻りオブジェクトは、API Gateway で必要です。正常に終了するには、sys.exit() の代わりに使用します。そうしなければ、「 Malformed Lambda Proxy Response 」を報告する 502 のエラーが表示される場合があります。

レイテンシに関するティップス

AWS Lambda を初めて使用する場合は、Lambda 関連のレイテンシに関するクエリがあります。これらのレイテンシについて、ここで説明します。

  • 最初の Lambda 呼び出しには、後続の呼び出し (ウォームスタート) では実行されないブートストラップステップ (コールドスタート) が含まれているため、予想以上に時間がかかる場合があります。
  • 不応期間の後、Lambda 実行環境は終了します。このアクションは、次回 Lambda 関数が呼び出されたときにコールドスタートのパフォーマンスの低下を招くことがあります。このパフォーマンスペナルティはレイテンシスパイクとして現れます。
  • 同じクエリのウォームスタートとコールドスタートの経過時間の例を次に示します。
warm start cold start
time ./invoke-lambda.cli
real    0m0.389s
time ./invoke-lambda.cli
real    0m9.428s

変更に関するティップス

相互依存関係を持つ個々の AWS コンポーネントの削除や再作成は避けるのが最善です。以下のティップスは、必要な変更を行うのに役立ちます。

  • AWS CloudFormation スタックパラメータを変更するには、スタックを更新します。 GitHub のスクリプト例である update-stack.cli をご参照ください。
  • Python コードを変更するには、Lambda 関数を再圧縮して更新します。GitHub のスクリプト例である update-lambda.cli をご参照ください。
  • また、AWS CloudFormation スタック全体を削除してからやり直すことも可能です。ただし、この例で使用されているスタックには、elastic ネットワークインターフェイスを作成する VPC 設定が含まれています。ネットワークインターフェイスにはクリーンアップに時間がかかるため、削除に時間がかかることがあります。

VPC 対応 Lambda 関数の使用とスケーリングに関するティップス

VPC 対応 Lambda 関数は、特にスケーリングでは特別な課題があります。以下のティップスは、これらの課題に取り組む際の手引きです。

  • VPC 対応 Lambda 関数用に作成されたインターフェイスには、サブネット内のプライベート IP アドレスが割り当てられます。そのため、サブネット自体がパブリックであっても、Lambda 関数はインターネットにアクセスできません。Lambda 関数にインターネットアクセスが必要な場合、サブネットが関連付けられているルートテーブルインターネットゲートウェイではなく、ネットワークアドレス変換 (NAT) ゲートウェイへのルートを作成します。
  • サブネットには、Lambda 関数で期待される並列度で処理するのに十分な IP があることを確認してください。
  • ネットワークインターフェイスを要求できる速度には制限があるため、Lambda 関数に十分なネットワークインターフェイス容量があることを確認してください。Lambda 関数のメモリ使用量を監視し、未使用のメモリ割り当てを減らすことで、ネットワークインターフェイスの共有が向上します。
  • データベースがパブリックでアクセス可能な場合は、Lambda 関数を VPC に入れる必要はないでしょう。

さらなるティップス

この例のステップを実行しているときは、追加のガイダンスについては次のリソースをご参照ください。

トラブルシューティング

発生する可能性のある一般的な問題とその解決策についてご紹介します。

問題点 : Python コードはコマンドラインから実行されますが、Lambda 関数または API Gateway REST API を呼び出すとハングやタイムアウトすることがあります。

  • データベース、クライアントホスト、および AWS CloudFormation スタックがすべて同じ VPC および AWS リージョン内に作成されていることを確認してください。
  • データベースセキュリティグループを確認します。クライアントホストのセキュリティグループ ID (CIDRではない) がインバウンドルールの送信元として許可されていることを確認します。
  • Lambda 関数は、(フェール時) リトライする場合があります。CloudWatch Logs ではエラーを、CloudWatch メトリックではリソースの問題をご参照ください。
  • 提供されている簡単な例とは別なクエリをテストする場合は、長いクエリを実行するティップスをご参照ください。また、提供された単純なクエリもお試しください。

問題 : 簡単なクエリは終了しますが、実行に時間がかかります。

問題 :モジュールをインポートできません」というエラーが表示されます。

  • 詳細については、CloudWatch Logs をご確認ください。
  • ソースコードファイルの名前がserverless-query.py 、zip ファイルが serverless-query.zip 、AWS CloudFormation テンプレートの hanler 名が serverless-query.handler であることを確認します。
  • すべての権限が指示どおりに設定されていることを確認します。
  • serverless-query.zip を解凍し、serverless- query.pyルートディレクトリにあることを確認します。
  • 指示に従いファイルを再圧縮します。
  • create-stack コマンドで、S3 バケット名が正しいことと、your-bucket-name (S3:// という接頭辞が付いていない) 形式であることを確認します。serverless-query.zip の最新バージョンがバケットに入っていることを確認します。バケットの権限を確認してください。
  • create-stack コマンドのパラメータのタイプミスを確認してください。

問題 : AWS CloudFormation スタックの削除に時間を要します。AWS CloudFormation Events ログには、 「CloudFormation is waiting for Network Interfaces associated with the Lambda Function to be cleaned up.」というステータスの理由が表示されます。

問題 : REST API を呼び出すと、エラー {"message": "Missing Authentication Token"} がスローされます。

  • リソース名「 \ query1 」を URL に追加します。

問題点 : REST API を呼び出すと、JSON データの第 1 行第 2 列に「 SyntaxError: JSON.parse: unexpected character 」というエラーがスローされます。

  • Mozilla Firefox 以外のブラウザからお試しください。

他のすべてのエラーメッセージについては、CloudWatch Logs ご参照ください。

まとめと最終ステップ

サーバレスアプリケーションは、高可用性、柔軟性、管理の容易さなどの利点を提供します。この記事では、サーバレスアプリケーションを設定し、 URL から AWS データベースをクエリする方法の例を示します。データベースに公開されていない場合についても該当します。この例のステップでは、ソリューションの設定、テスト、トラブルシューティングを行いました。

最後のステップは、EC2 クライアントホストが不要になった場合に削除することです。結局のところ、これが全体のポイントだったためです。


著者について

Deana Holmer 氏は、アマゾン ウェブ サービスのプレミアムサポートチームのシニアデータベースエンジニアです。