NLB経由でAmazon DocumentDBに接続する方法を検証しました

モバイルアプリサービス部の五十嵐です。

今年は GraphQL + MongoDB + TypeScript なスタックを広めていきたいと思っていた矢先、AWSからMongoDB互換のデータベースサービス Amazon DocumentDB(以下、DocumentDBと書きます) が発表されてめちゃくちゃテンション上がりました!!

New – Amazon DocumentDB (with MongoDB Compatibility): Fast, Scalable, and Highly Available

DocumentDB(MongoDB)を利用したいケースとしては、Lambdaを利用したサーバーレスアプリケーションのデータストアとして利用している DynamoDB + Elasticsearch Service(より具体的には DynamoDB → DynamoDB Streams → Lmabda → Elasticsearch Serviceの組み合わせ)を、DocumentDB(MongoDB)に置き換えたいという場合も多いのではないでしょうか。その場合、DocumentDBは現在パブリックエンドポイントをサポートしていないため、Lambda を Lambda in VPC にしてVPC経由で接続するという方法もありますが、Lambda in VPCの考慮事項(起動速度やプライベートIPの枯渇など)を踏まえるとアプリケーションの特性によってはインターネット経由で接続できたほうが望ましい場合もあります。そこで、NLB経由でDocumentDBに接続する方法を検証しました。

ちなみに公式のドキュメントではSSHトンネルを使用できますとあります。オペレーション用でしたらこれで十分なのですが、アプリケーションから接続する場合は踏み台となるインスタンス(EC2など)がボトルネックとなるため、NLBの方が望ましいと考えました。

Amazon DocumentDB のトラブルシューティング

Amazon DocumentDB は仮想プライベートクラウド (VPC) 専用で、パブリックエンドポイントを現在サポートしていません。そのため、VPC の外部から Amazon DocumentDB クラスターに直接接続することはできません。 VPC の外部から Amazon DocumentDB クラスターに接続するには、SSH トンネルを使用できます。詳細については、「Amazon VPC 外部からの Amazon DocumentDB クラスターへの接続」を参照してください。

検証

Clusterの作成~接続確認

まずはDocumentDBのClusterを作成し、VPC内のEC2から接続できることを確認します。

Clusterの作成手順は誰かが書くことを期待して割愛します。

Clusterの準備ができたらEC2(Amazon Linux)にsshしてターミナルに接続します。

こちらの手順に従い、mongo shellをインストールし、CAの公開鍵をダウンロードして、DocumentDBに接続できることを確認します。

ステップ 3: mongo シェルを使用して Amazon DocumentDB クラスターにアクセスする

$ sudo vim /etc/yum.repos.d/mongodb-org-3.6.repo
### ここから
[mongodb-org-3.6]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/3.6/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.6.asc
### ここまでを書き込み保存する

$ sudo yum install -y mongodb-org-shell
$ mongo --version
MongoDB shell version v3.6.9

$ wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem

$ mongo --ssl --host [DocumentDBのClusterのドメイン]:27017 --sslCAFile rds-combined-ca-bundle.pem --username [ユーザ名] --password [パスワード]
MongoDB shell version v3.6.9
connecting to: mongodb:/[DocumentDBのClusterのドメイン]:27017/
Implicit session: session { "id" : UUID("20edbd71-b327-4d8f-8479-5810b263551c") }
MongoDB server version: 3.6.0
rs0:PRIMARY> 

コンソールが表示されたら接続成功です。

接続できない場合はSecurityGroupの設定などを確認してみてください。トラブルシューティングのドキュメントもあります。

Amazon DocumentDB のトラブルシューティング

SSH トンネルを使用した接続

せっかくなのでSSHトンネルを使用した接続を試してみましょう。

いま、先ほど使用したEC2のホスト名をworkとして、sshで接続できる状態になっています。 そして、workを踏み台にしてDocumentDBに接続できる状態にします。

$ ssh -F ~/.ssh/ssh_hosts work -Nf -L 27017:[DocumentDBのClusterのドメイン]:27017

これで、ローカル(localhost)の27017ポートを、リモート(work)の27017ポートに転送する状態になります。

Macにmongo shellがインストールされていない場合は、こちらの手順に従ってインストールします。

Install MongoDB Community Edition on macOS — MongoDB Manual

そして、mongo shellでホストをlocalhostにして接続します。注意点としては、公開鍵に署名されているホスト名が接続先のホスト名(localhost?work?)と不一致になってしまうため、ホスト名を検証しない --sslAllowInvalidHostnames オプションが必要になります。

$ mongo --ssl --host localhost:27017 --sslCAFile rds-combined-ca-bundle.pem --username root --password password --sslAllowInvalidHostnames

コンソールが表示されたら接続成功です。

ここではmongo shellで接続しましたが、MongoDB CompassというMongoDBのクライアントアプリケーションでもSSHトンネルで接続できました。

NLBを使用した接続

それでは、本題のNLBを使用した接続を試してみましょう。

大まかな流れは以下のとおりです。

  1. DocumentDBのSecurityGroupにNLBを配置するサブネットからのアクセス許可を追加する
  2. DocumentDBのインスタンスのプライベートIPをターゲットとするTargetGroupを作成する
  3. NLBを作成してTargetGroupをアタッチする
  4. NLBのヘルスチェックが通れば準備完了

具体的には以下のリソースを作成します。

ターゲットグループの作成

  • ターゲットグループ名: 任意
  • ターゲットの種類: IP
  • プロトコル: TCP
  • ポート: 27017
  • VPC: 任意
  • ヘルスチェック: TCP

ターゲットグループにターゲットを追加

  • ネットワーク: 任意のVPC
  • IP: ターゲットのIP(DocumentDBのインスタンスのプライベートIP)
  • ポート: 27017

ロードバランサ(Network Load Balancer)の作成

  • 名前: 任意
  • スキーム: インターネット向け
  • リスナー: TCP 27017
  • アベイラビリティゾーン: 任意(※DocumentDBのインスタンスと同じAZを含める!!)
  • ターゲットグループ: 既存のターゲットグループから先ほど作成したターゲットグループを選択

接続するためのCAの公開鍵も先ほどのEC2(work)から取得します。

$ scp -F ~/.ssh/ssh_hosts work:/home/ec2-user/rds-combined-ca-bundle.pem ~/.ssh/
$ chmod 400 ~/.ssh/rds-combined-ca-bundle.pem

NLBのドメインを指定してDocumentDBに接続します。--sslAllowInvalidHostnames オプションが必要な点はSSHトンネルの場合と同じです。

$ mongo --ssl --host [NLBのドメイン]:27017 --sslCAFile ~/.ssh/rds-combined-ca-bundle.pem --username root --password password --sslAllowInvalidHostnames

コンソールが表示されたら接続成功です。

2019/01/16 追記

ただしこの方法では、プライベートIPが変わると疎通できなくなること、エンドポイントがインターネットに露出してしまうためセキュアではないこと、などの問題点がありますのでご留意ください。

おわりに

本記事では、インターネット(NLB)経由でDocumentDBに接続できることを確認できました。次はLambdaからmongodbのクライアントライブラリを使ってインターネット経由で接続する方法を試してみたいと思います。

参考