アプリからRDSへつなぐと、プロンプトが戻らないまま数十秒後にタイムアウトで終了する。セキュリティグループを開いてみるとDBポートは許可済みに見える。エンドポイントのコピペミスも見当たらない。この状態でコンソールのタブを往復しても原因は絞れません。接続タイムアウトはパケットが途中で破棄された結果なので、どこで破棄されたかを上から順に機械的に確定させる必要があります。
この記事は「RDS 接続できない・タイムアウトの原因と切り分け手順」の一部を、話題を絞って書き直したものです。タイムアウト以外のエラーメッセージの読み分け方や、普段はつながるのに時々だけ失敗する場合の調査ポイントは、ブログの完全版にまとめています: RDS 接続できない・タイムアウトの原因と切り分け手順
切り分けの順番を固定する
手順1 接続元・エンドポイント・ポートを文字列で確定させる
調査を始める前に、どこからどこへどのポートでつなごうとしているのかを文字列レベルで確定させてください。ここが曖昧なままセキュリティグループを眺めても時間を使うだけです。
aws rds describe-db-instancesでステータス、エンドポイント、ポート、PubliclyAccessible、VPCセキュリティグループ、サブネットグループをまとめて取得します。DBInstanceStatusがstoppedなら、そもそも接続できません。RDSは最大7日で自動起動しますが、その間は到達しません。ポートも既定値とは限らず、PostgreSQLの5432やMySQLの3306から変更している環境は珍しくありません。セキュリティグループで5432を開けていたのに実体は5433だったという例もあります。
名前解決はdigやnslookupでAレコードが返るか確認します。返るIPが10.x、172.16〜31.x、192.168.xのプライベートIPなら、そのVPC内部からしか届きません。VPC外から試していた場合は、この時点で原因が確定します。逆にVPC内からパブリックIPが返るなら、DNSの経路を疑ってください。エンドポイントのDNSはVPC内からプライベートIPを返す仕様なので、オンプレのDNSを参照している、enableDnsHostnamesやenableDnsSupportが無効といった条件に当たります。
手順2 セキュリティグループのインバウンドを3点で見る
手順1で得たセキュリティグループIDを使い、インバウンドルールを確認します。見る点は3つです。DBポートに対するルールが存在するか、ソースに接続元が含まれているか、ソースがセキュリティグループIDの場合に接続元のEC2やLambdaがそのSGを実際にアタッチしているか。SG参照は便利ですが、スケールアウトで追加したインスタンスにSGを付け忘れる設定漏れが起きやすい箇所です。
CIDR指定のずれも頻出します。10.0.1.0/24を許可したのに接続元が10.0.2.xにいる、/32のホスト指定が古いIPのまま、自宅や社内のグローバルIPが変わっている、といったパターン。接続元がEC2なら、EC2側のアウトバウンドも確認します。既定は全許可ですが、送信を絞っている環境ではDBポートへの通信が許可されていないことがあります。
手順3 サブネットとルートテーブルを経路パターンで判断する
同一VPC内のEC2からの接続であれば、サブネット間はローカルルートで到達するため、ルーティングは通常問題になりません。セキュリティグループかネットワークACLを疑います。別VPCからならVPCピアリングまたはTransit Gatewayが必要で、両側のルートテーブルに相手のCIDRへのルートが入っているかを見ます。片側だけ設定してタイムアウトするのは典型的な例です。オンプレミスや別アカウントからVPNやDirect Connect経由でつなぐ場合は、VGWやTGWへのルートと、オンプレ側ファイアウォールのDBポート開放を確認してください。
ローカルPCからインターネット経由で直接つなぐには、PubliclyAccessibleがtrueであること、DBのサブネットのルートテーブルに0.0.0.0/0 → igw-xxxxがあること、SGのインバウンドで自分のグローバルIPが許可されていることの3つがすべて必要です。PubliclyAccessible: trueでもIGWへのルートがなければパブリックIPは割り当たりません。プライベートサブネット配置のまま外から接続したいなら、踏み台EC2のSSHポートフォワードか、Session Managerのポートフォワーディングを使います。
手順4 素のTCPで測ってから原因を分ける
設定を目で追うのと並行して、TCPが実際に届くかを測ります。DBクライアントのエラーメッセージは原因を曖昧にしがちなので、先に素のTCPで判定するほうが早く進みます。結果の読み分けは次のとおりです。
- 到達が返るのにDBクライアントで失敗する。ネットワークは通っているため、認証、SSL、データベース名を確認します
- 5秒待って無反応。パケットが破棄されているので、SG、ACL、ルーティングを確認します
-
Connection refusedが即返る。到達はしているがそのポートで待ち受けていないため、ポート番号の誤りか別ホストへの接続を疑います
設定が複雑で目視では追えない場合は、VPCのReachability Analyzerを使います。送信元のENIと宛先のIP、プロトコル、ポートを指定すると、NO_ROUTE_TO_DESTINATIONやACL_RULE、SECURITY_GROUP_RULEといったExplanationCodeで、どのリソースのどのルールが遮断しているかを特定できます。宛先IPにはVPC内から引いたプライベートIPを指定するのが手軽です。解析ごとに課金されるため、料金は公式の料金ページで確認してください。
最初に打つ2つのコマンド
# DB インスタンスの基本情報をまとめて確認
aws rds describe-db-instances \
--db-instance-identifier my-db \
--query 'DBInstances[0].{Status:DBInstanceStatus,Endpoint:Endpoint.Address,Port:Endpoint.Port,Public:PubliclyAccessible,VpcSG:VpcSecurityGroups,SubnetGroup:DBSubnetGroup.DBSubnetGroupName,AZ:AvailabilityZone}' \
--output json
# nc(netcat)で TCP 到達性のみ確認。タイムアウトを 5 秒に制限
nc -zv -w 5 my-db.xxxxxxxx.ap-northeast-1.rds.amazonaws.com 5432
この2つで、設定値の実体とTCPの到達可否が同時に分かります。あとは手順2から順に該当箇所を詰めていけば、推測に頼らず原因へ到達できます。
元記事で扱っている内容
- 結論の章では、セキュリティグループのインバウンドとサブネットの経路から見る最短ルートを提示しています
- タイムアウト以外のエラーメッセージの読み分け方として、認証やSSL、データベース名など、TCPは通っているのに失敗するケースを整理しています
- 普段はつながるのに時々タイムアウトする場合の調査ポイントを、断続的な失敗に絞って解説しています
- 再発防止のための設定と設計のチェックポイントをまとめ、同じ調査を繰り返さないための構成を示しています
- よくある質問として、切り分け中に迷いやすい論点に答えています