HTTP API のプライベート統合でプライベートサブネット内の EC2 上の API をコールしてみた

HTTP API のプライベート統合でプライベートサブネット内の EC2 上の API をコールしてみた

2025.08.27

こんにちは、クラウド事業本部 コンサルティング部のいたくらです。

タイトルの通り、HTTP API のプライベート統合でプライベートサブネット内の EC2 上の API をコールしてみました。

構成図

API Gateway(HTTP API)から VPC リンクを経由して、プライベートサブネットに配置された ALB 配下の EC2 上の API をコールします。
構成図.png

事前準備

プライベートサブネットに EC2 を作成し、EC2 上で Python の Flask を使用した簡単な API アプリケーションを準備しました。
セッションマネージャー経由で EC2 にログインして作業した内容は以下に畳みました。

作業内容
作業時に使用したコマンド
# Python, Flask をインストール
$ sudo dnf update -y
$ sudo dnf install python3 -y
$ sudo yum install python3-pip -y
$ pip3 install flask

# Flask アプリケーションコードを作成
$ cd ~
$ pwd
/home/ssm-user
$ mkdir my-api
$ cd my-api/
$ pwd
/home/ssm-user/my-api
$ vi app.py

# Flask アプリケーションを起動
$ nohup python3 app.py > app.log 2>&1 &
[1] 2370

# 動作確認
$ curl http://localhost:8000
Hello from the Home page! (EC2 Host: ip-10-0-10-148.ap-northeast-1.compute.internal)
$
$ curl http://localhost:8000/service-a
This is Service A! (EC2 Host: ip-10-0-10-148.ap-northeast-1.compute.internal, Path: /service-a)
$
$ curl http://localhost:8000/service-b
This is Service B! (EC2 Host: ip-10-0-10-148.ap-northeast-1.compute.internal, Path: /service-b)
$

app.py の内容は以下に畳みました。

app.py
from flask import Flask, request
import socket

app = Flask(__name__)

# ホスト名を取得
hostname = socket.gethostname()

@app.route('/')
def home():
    return f"Hello from the Home page! (EC2 Host: {hostname})\n"

@app.route('/service-a')
def service_a():
    return f"This is Service A! (EC2 Host: {hostname}, Path: {request.path})\n"

@app.route('/service-b')
def service_b():
    return f"This is Service B! (EC2 Host: {hostname}, Path: {request.path})\n"

@app.route('/health')
def health_check():
    return "OK"

if __name__ == '__main__':
    # 0.0.0.0でリッスンすることで、外部からのアクセスを受け付ける
    # ポートはALBのターゲットグループ設定と合わせる (例: 8000)
    app.run(host='0.0.0.0', port=8000)

EC2 にアタッチしているセキュリティグループ(test-ec2-sg)の内容は以下です。

  • インバウンドルール
    • プロトコル:HTTP、ポート:8000、ソース:test-alb-sg
  • アウトバウンドルール
    • デフォルトの全許可ルールのみ

また、ALB とターゲットグループも以下のパラメーターで事前に準備しました。

  • ALB
    • スキーム:Internal
    • ロードバランサーの IP アドレスタイプ:IPv4
    • VPC:test-vpc
    • サブネット:test-private1, test-private2
    • セキュリティグループ:test-alb-sg
    • リスナー
      • プロトコル:HTTP
      • ポート:80
      • デフォルトアクション:test-tg へ転送
  • ターゲットグループ
    • ターゲット:ポート 8000 を指定して準備した EC2 を登録
    • ヘルスチェックの設定
      • プロトコル:HTTP
      • パス:/health
      • ポート:8000
      • 他パラメーターはデフォルト値を使用

補足:test-alb-sg の内容は以下です。

  • インバウンドルール
    • プロトコル:HTTP、ポート:80、ソース:test-vpclink-sg
  • アウトバウンドルール
    • デフォルトの全許可ルールのみ

VPC リンクを作成する

API Gateway と VPC を接続するための VPC リンクを作成します。

API Gateway のサービスページ > VPC リンク の順に移動し、「Create」をクリックします。
1-2.png

以下について選択&入力して「作成」をクリックします。

  • VPC リンクバージョンを選択:VPC link for HTTP APIs
  • 名前:test-vpclink
  • VPC:test-vpc
  • サブネット:EC2 をデプロイしているプライベートサブネットを選択
    • 今回は ap-northeast-1a のプライベートサブネットにデプロイしていたので、1つだけを選択でよかったのですが癖で2つ選択しちゃってます
  • セキュリティグループ:test-vpclink-sg
    2-3.png

数分で VPC リンクのステータスが「Available」になります。

補足:test-vpclink-sg の内容は以下です。

  • インバウンドルール
    • なし
  • アウトバウンドルール
    • デフォルトの全許可ルールのみ

HTTP API を作成する

クライアントからのリクエストを受け付ける HTTP API を作成します。

API Gateway のサービスページ > API の順に移動し、「API を作成」をクリックします。
3.png

HTTP API の「構築」をクリックします。
4.png

以下について選択&入力して「次へ」をクリックします。

  • API 名:test-http-api
  • IP アドレスのタイプ:IPv4
    5.png

「ルートを設定」画面でバックエンドにすべてのリクエストを転送するためのルートを作成します。後続画面で設定するので、何も設定せずに「次へ」をクリックします。
6.png

「ステージを定義」画面ではデフォルトで$defaultステージが自動デプロイ有効で作成されます。この設定のまま「次へ」をクリックします。
7.png

「確認して作成」画面では、内容を確認し「作成」をクリックします。
8.png

そのままルートを作成できるページに遷移するので、「{新規作成した API }のルート」の「作成」をクリックします。
9.png

「ルートの作成」画面では、メソッド:ANY、パス:/{proxy+} を入力して「作成」をクリックします。
10-1.png

プライベート統合を作成する

作成した API のルートと VPC リンク、そして ALB を紐付ける「統合」を作成します。

API 選択済みの状態から「Integrations」> 「統合を管理」タブの順に移動し、「Create」をクリックします。
11.png

以下について選択&入力して「作成」をクリックします。

  • この統合をルートにアタッチする:ANY /{proxy+}
  • 統合タイプ:プライベートリソース
  • 選択方法:手動で選択
  • ターゲットサービス:ALB/NLB
  • ロードバランサー:test-alb
  • リスナー:HTTP 80
  • VPC リンク:test-vpclink
    12.png

動作確認

API 選択済みの状態から「Stages」に移動すると「URL を呼び出す」のところに URL が記載されています。
13-1.png
こちらの URL を使用して動作確認を実施します。

$ curl -i https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/
HTTP/2 200 
date: Tue, 26 Aug 2025 16:51:39 GMT
content-type: text/html; charset=utf-8
content-length: 85
server: Werkzeug/3.1.3 Python/3.9.23
apigw-requestid: P7DkRj44NjMEM8A=

Hello from the Home page! (EC2 Host: ip-10-0-10-148.ap-northeast-1.compute.internal)
$
$ curl -i https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/service-a
HTTP/2 200 
date: Tue, 26 Aug 2025 16:51:47 GMT
content-type: text/html; charset=utf-8
content-length: 96
server: Werkzeug/3.1.3 Python/3.9.23
apigw-requestid: P7DloiANNjMEMuQ=

This is Service A! (EC2 Host: ip-10-0-10-148.ap-northeast-1.compute.internal, Path: /service-a)
$
$ curl -i https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/service-b
HTTP/2 200 
date: Tue, 26 Aug 2025 16:51:51 GMT
content-type: text/html; charset=utf-8
content-length: 96
server: Werkzeug/3.1.3 Python/3.9.23
apigw-requestid: P7DmMj7VtjMEMsg=

This is Service B! (EC2 Host: ip-10-0-10-148.ap-northeast-1.compute.internal, Path: /service-b)
$

事前準備で動作確認したときと同様の挙動になることを確認できました!
無事、インターネットから HTTP API を経由してプライベートサブネット内の EC2 の API をコールすることができました。

さいごに

HTTP API のプライベート統合でプライベートサブネット内の EC2 の API をコールしてみました。
本ブログでは EC2 上の API をコールしてますが、実は Fargate 上の API 版は 5 年前に執筆されていました。
https://dev.classmethod.jp/articles/http_api_with_private_integration_for_fargate/

似たようなブログになってしまうなと思いつつ、5 年前で若干 UI が変わっていたり、NLB ではなく ALB だったり多少違いがあるので勉強がてら執筆してみました。
API Gateway をあまり触ったことがない私でもマネコン操作で 1 時間くらいで試すことができたので、プライベート統合を試してみたい方は是非やってみてください。

この記事がどなたかのお役に立てれば幸いです。

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.