📭

DocumentDBにローカルから接続するのは大変なのでLambda関数から接続してみた

2022/11/26に公開

先日、AWS Certified Database - Specialty にギリギリで合格しました。
勉強を進める中で気になったDocumentDBなるデータベースがあったので、Get Startedしてみました。

この記事のまとめ

  • DocumentDBのクエリ操作にはMongoDB用のライブラリを使用します
    • AWS SDKではMongoDBクラスターを作成したり起動したりスナップショットを取得したり、、、管理系の操作ができます
  • ローカルPCから直接DocumentDBに接続することはできません
    • 踏み台サーバを用意する等の工夫が必要です
  • Lambda等のAWS上のサービスからDocumentDBに接続する場合は、DocumentDBと同じVPCを使用する必要があります
    • 別のVPCから接続する時はVPCピアリングの設定が必要です(当たり前ですが)

背景

普段の業務ではWebアプリケーションのバックエンド開発を担当しています。
コードを書きながらAWSインフラの設計・構築を担当しています。

これまで、LambdaやS3、DynamoDB等の様々なユースケースで使用されるサービスの他に、画像解析や時系列データの保存といった特定のユースケースで使用されるRekognitionやTimestream等を触ってきました。
AWSの各サービスは、コンソールからポチポチしながらインフラを作ることができます。※1
また、SDKが非常に充実しているため、数行コードを書くだけでこれらのサービスとアプリケーションを結合させることができます。※2

※1 本番運用時はなるべくIaCを使います。検証の時はポチポチします。
※2 SDKの使用方法やドキュメントはやや癖がありますが、やりたいことはたいていできます。

今回もDocumentDBをコンソール上でポチポチ用意した後、AWS SDK にAPIキーを食わせるだけで直ぐに使えるかなと思いましたが、、違いました。

DocumentDBとは

  • フルマネージドのデータベースサービス
  • MongoDBとの互換性があり、MongoDB用のコード、ツールが使える
    • MongoDBはNoSQLのドキュメントデータベース
    • ドキュメントデータベースとは、データをドキュメントとして管理する
    • MongoDBでは、JSON形式のデータをドキュメントとして管理する

JSON(Likeな)データを管理するためのフルマネージドデータベースですね。
AWSのNoSQLといえばDynamoDBがあります。シンプルなデータを扱うだけであればDynamoDBを使うほうが良さそうです。
一方で、バリューとして非構造データを登録したい、保存されているJSONデータを解析したい場合にはDocumentDB(MongoDB)が向いていそうです。
DynamoDBでもJSONデータを保存できますが、MongoDBは複雑なクエリ機能を持っているようです。

https://aws.amazon.com/jp/documentdb/
https://www.mongodb.com/compare/mongodb-dynamodb

やったこと

セキュリティグループの作成

https://docs.aws.amazon.com/ja_jp/documentdb/latest/developerguide/get-started-guide.html#cloud9-security

DocumentDBの構築

https://docs.aws.amazon.com/ja_jp/documentdb/latest/developerguide/get-started-guide.html#cloud9-cluster

サンプルコード作成

SDKのマニュアルを読んでもインスタンスの操作やスナップショットの取得等のオペレーション用APIしか見つけられず、クエリを発行するためのAPIがありませんでした。(あったら教えて下さいmm)

改めてAWSの公式サイトを見てみると、AWS SDKを使用したサンプルコードは載っておらず、代わりにmongoDB用のライブラリを使用したサンプルが載っていました。
https://docs.aws.amazon.com/ja_jp/documentdb/latest/developerguide/connect_programmatically.html

MongoDBのライブラリでは、サンプルコードのconnectメソッドの使用例は推奨されていなかったため、以下のように書き変えてDocumentDBに接続できるか確認してみました。

サンプル

"use strict";
const MongoClient = require('mongodb').MongoClient

const main = async (key) => {
  console.log({ key })
  console.log('Connecting to MongoDB...')
  const uri = "DocumentDBのエンドポイント"
  const client = new MongoClient(
      uri,
      {  
          tlsCAFile: __dirname + '/rds-combined-ca-bundle.pem',
      }
  );
  await client.connect();
  console.log('Connected to MongoDB')

  const db = client.db('test');
  console.log('Connected to test table')
  
  const collection = db.collection('testCollection');
  console.log('Connected to testCollection')
  
  await collection.insertOne({key: "aa", name: "AAA"});
  await collection.insertOne({key: "bb", name: "BBB"});
  
  const result = await collection.findOne({ key });
  console.log({result});
  
  await collection.deleteMany({});
  const resultAfterDelete = await collection.findOne({ key });
  console.log({resultAfterDelete});
  
  await client.close();
}

main("aa")

DocumentDBのURIは以下の通りです。

mongodb://{ユーザ名}:{パスワード}@{DocumentDBのホスト名}:{ポート番号、デフォルトは27017}/{テーブル名}?tls=true&retryWrites=false

デフォルトはtls=trueではなくssl=trueです。tlsに変更する必要があります。

事前にCA証明書をダウンロードして、index.jsと同じディレクトリに置いておきます。
CA証明書のダウンロード方法はDocumentDBのコンソールから確認できます。

ローカルから接続できない、、?

上のコードをローカルPCから起動するとDocumentDBに接続ができません。
接続情報やセキュリティーグループは問題なさそうでしたが、再度公式ページの確認したところ、以下のような記述がありました

Amazon DocumentDB は仮想プライベートクラウド (VPC) 専用で、パブリックエンドポイントを現在サポートしていません。したがって、ノートパソコンまたは VPC の外部のローカル開発環境から直接の Amazon DocumentDB クラスターに接続することはできません。

Amazon DocumentDB クラスターと Amazon EC2 インスタンスが両方とも同じにある場合AWS リージョンでは、同じ Amazon VPC 内では、2 つの Amazon VPC 間で VPC ピアリングが有効になっていない限り、Amazon DocumentDB クラスターに直接接続することはできません。

https://docs.aws.amazon.com/ja_jp/documentdb/latest/developerguide/troubleshooting.connecting.html

つまり、同一VPC以外からアクセスする場合は、VPCピアリングや踏み台サーバを用意する必要があります。

Lambda関数作成

ローカルでの動作確認を諦め、Lambda関数を作成しました。作成するLambda関数はDocumentDBと同じVPCに紐付ける必要があります。

コードはmain関数をLambda関数のハンドラに直します。

index.js

"use strict";
const MongoClient = require('mongodb').MongoClient

exports.handler = async (event, _context) => {
  console.log({ event })

  const { key } = event
  console.log('Connecting to MongoDB...')
  const uri = process.env.DOCUMENT_DB_URI // Lambdaの環境変数に設定
  const client = new MongoClient(
      uri,
      {  
          tlsCAFile: __dirname + '/rds-combined-ca-bundle.pem',
      }
  );
  await client.connect();
  console.log('Connected to MongoDB')

... 以下略 ...

Lambda関数にアップロードするためのzipファイル作成

zip -r lambda.zip index.js node_modules/ rds-combined-ca-bundle.pem

CA証明書、node_modulesをアップロードするzipに含めます。
CA証明書は環境変数等から読んでも良いかもです。

動作確認

{ "key": "aa" } を入力パラーメタとして実行してみます。

テストの実行結果

START RequestId: xxxxx
INFO	{ key: 'aa' }
INFO	Connecting to MongoDB...
INFO	Connected to MongoDB
INFO	Connected to test table
INFO	Connected to testCollection
INFO	{
  result: {
    _id: new ObjectId("XXXXXXXXXXXXXXX"),
    key: 'aa',
    name: 'AAA'
  }
}
INFO	{ resultAfterDelete: null }
END RequestId: xxxxx

無事に接続・登録・取得・削除ができました。

試しにラムダ関数のVPCとの紐付けを解除して再度テストを実行してみると、DocumentDBに接続できずエラーとなりました。(当たり前ですが)

おわりに

以上です。

AWS Certified Database - Specialtyの勉強をしたことで、触ったことがなかったDocumentDBを知って触ってみる良い機会になりました。

GitHubで編集を提案
NCDCエンジニアブログ

Discussion