💭

AWS学びなおし(+TF)_API Gateway

2025/03/05に公開

AWS API Gatewayは、開発者がAPIを容易に作成、公開、保守、モニタリング、および保護できるようにするフルマネージドサービスです。API Gatewayは、バックエンドサービスへの「玄関口」として機能し、クライアントからのリクエストを受け付け、バックエンドにルーティングし、レスポンスをクライアントに返します。

API Gatewayの主な役割とメリット:

  • APIの集中管理: 複数のバックエンドサービスへのアクセスポイントをAPI Gatewayに集約することで、APIの一元的な管理と運用が可能になります。
  • セキュリティ強化: 認証、認可、レート制限、WAF (Web Application Firewall) 連携など、APIのセキュリティ対策をAPI Gatewayで一括して行うことができます。
  • トラフィック管理: スロットリング (レート制限) やキャッシュなどの機能により、バックエンドサービスの負荷を軽減し、安定したAPI提供を実現します。
  • APIの公開と利用促進: APIドキュメントの自動生成、開発者ポータル機能などを活用することで、APIの公開と利用を促進できます。
  • マイクロサービスアーキテクチャとの親和性: マイクロサービスとして構築されたバックエンドサービス群を、API Gatewayを通じて外部に公開するのに適しています。
  • サーバーレスアーキテクチャとの連携: AWS LambdaなどのサーバーレスコンピューティングサービスとAPI Gatewayを組み合わせることで、スケーラブルでコスト効率の高いAPIを構築できます。
  • モニタリングとロギング: APIの利用状況、パフォーマンス、エラーなどを詳細にモニタリングし、ログを記録することで、APIの運用状況を可視化し、問題発生時の迅速な対応を可能にします。

API Gatewayの種類:

API Gatewayには、主に以下の3つの種類があります。

  • REST API: HTTPリクエスト/レスポンスに基づいたRESTful APIを構築するための最も一般的なタイプです。幅広い機能と柔軟性を持ち、多くのユースケースに対応できます。
  • HTTP API: REST APIと比較して、低レイテンシ、低コスト、シンプルな機能に特化したタイプです。主にプロキシAPIやシンプルなAPIに適しており、高速性とコスト効率を重視する場合に選択されます。
  • WebSocket API: 双方向通信を必要とするリアルタイムアプリケーション (チャット、ゲーム、IoTなど) に適したタイプです。クライアントとサーバー間の永続的な接続を確立し、リアルタイムなデータ交換を可能にします。

主要な機能:

  • 認証・認可: APIキー、IAM認証、Cognito認証、OAuth 2.0など、様々な認証・認可方式をサポートし、APIへのアクセス制御を実現します。
  • レート制限 (スロットリング): APIへのリクエストレートを制限することで、バックエンドサービスの過負荷を防ぎ、安定したAPI提供を維持します。
  • キャッシュ: APIレスポンスをキャッシュすることで、バックエンドへのリクエスト数を減らし、APIの応答速度を向上させます。
  • リクエスト/レスポンス変換: クライアントからのリクエストとバックエンドサービスが求めるリクエスト形式、およびバックエンドからのレスポンスとクライアントが求めるレスポンス形式を相互に変換します。
  • モニタリングとロギング: CloudWatch LogsおよびCloudWatch Metricsと統合されており、APIの利用状況、パフォーマンス、エラーなどをリアルタイムに監視し、ログを記録できます。
  • APIドキュメント生成: OpenAPI (Swagger) 仕様に基づいたAPIドキュメントを自動生成し、開発者ポータルを通じて公開できます。
  • カスタムドメイン: 独自のドメイン名 (例: api.example.com) をAPI Gatewayに関連付けることができます。
  • WAF連携: AWS WAF (Web Application Firewall) と連携することで、SQLインジェクション、クロスサイトスクリプティングなどの一般的なWeb攻撃からAPIを保護できます。
  • API Gateway Private Endpoint: VPC内にAPI Gatewayのエンドポイントを作成し、VPC内のリソースからのみアクセス可能なプライベートAPIを構築できます。

【実務レベルの内容と重要事項】

API Gatewayを実務で利用する上で重要な点は多岐に渡りますが、特に以下の点が重要になります。

1. API設計:

  • RESTful API設計: リソースベースの設計、HTTPメソッドの適切な使用、ステータスコードの適切な返却など、RESTful APIの原則に従った設計が重要です。
  • APIバージョン管理: APIの変更を安全に行うために、APIバージョン管理 (URLパス、ヘッダーなど) を適切に行う必要があります。
  • エラーハンドリング: エラー発生時のレスポンス形式 (エラーコード、エラーメッセージなど) を標準化し、クライアントがエラーを適切に処理できるようにする必要があります。
  • APIドキュメント: OpenAPI (Swagger) 仕様などでAPIドキュメントを記述し、常に最新の状態に保つことが重要です。開発者ポータルと連携させることで、API利用者の利便性を高めることができます。

2. セキュリティ:

  • 認証・認可方式の選定: APIの公開範囲、セキュリティ要件に応じて、適切な認証・認可方式 (APIキー、IAM認証、Cognito認証、OAuth 2.0など) を選択する必要があります。
  • 認可の粒度: API全体へのアクセス制御だけでなく、API内の特定のリソースや操作に対するアクセス制御 (認可) を検討する必要があります。
  • レート制限 (スロットリング) の設定: APIの利用状況、バックエンドサービスの処理能力などを考慮して、適切なレート制限を設定する必要があります。
  • WAF (Web Application Firewall) の導入: SQLインジェクション、クロスサイトスクリプティングなどのWeb攻撃からAPIを保護するために、WAFの導入を検討する必要があります。
  • APIキーの管理: APIキーを使用する場合、キーの漏洩、不正利用を防ぐために、適切な管理 (ローテーション、暗号化など) を行う必要があります。
  • TLS (HTTPS) の適用: APIとの通信は常にTLS (HTTPS) を使用し、通信経路の暗号化を行う必要があります。

3. パフォーマンスとスケーラビリティ:

  • キャッシュ戦略: APIの特性 (データの更新頻度、キャッシュヒット率など) に応じて、適切なキャッシュ戦略 (キャッシュ期間、キャッシュキーなど) を設計する必要があります。
  • レート制限 (スロットリング) の最適化: レート制限の設定が厳しすぎると、正当なリクエストが拒否される可能性があります。APIの利用状況をモニタリングしながら、レート制限を適切に調整する必要があります。
  • バックエンド統合: API Gatewayとバックエンドサービス間のネットワークレイテンシを最小限に抑えるために、適切なネットワーク構成 (VPCエンドポイント、Direct Connectなど) を検討する必要があります。
  • スケーラビリティ設計: API Gateway自体はスケーラブルなサービスですが、バックエンドサービスのスケーラビリティも考慮した設計が必要です。

4. 運用・監視:

  • モニタリング: CloudWatch Metricsを活用して、APIのレイテンシ、リクエスト数、エラー率などを継続的にモニタリングし、異常を早期に検知できるようにする必要があります。
  • ロギング: CloudWatch Logsを活用して、APIのリクエスト/レスポンスログ、アクセスログなどを記録し、問題発生時の原因究明やセキュリティ監査に役立てる必要があります。
  • アラート設定: CloudWatch Alarmsを設定し、APIのパフォーマンス劣化やエラー発生時に自動的に通知を受け取れるようにする必要があります。
  • デプロイ戦略: APIの変更を安全にデプロイするために、適切なデプロイ戦略 (Blue/Greenデプロイ、Canaryデプロイなど) を検討する必要があります。
  • 障害対策: API Gatewayの障害に備えて、バックアップ、フェイルオーバーなどの対策を検討する必要があります。

5. コスト最適化:

  • API Gatewayの料金体系の理解: API Gatewayの料金体系 (リクエスト数、データ転送量、キャッシュ利用料など) を理解し、コストを最適化できるようにAPI設計、設定を行う必要があります。
  • 不要な機能の削減: API Gatewayの多くの機能の中から、必要な機能のみを選択し、不要な機能を削減することで、コストを抑えることができます。
  • キャッシュの有効活用: キャッシュを有効活用することで、バックエンドへのリクエスト数を減らし、バックエンドサービスのコストも削減できます。
  • HTTP APIの検討: REST APIほどの高度な機能を必要としないAPIの場合は、HTTP APIを検討することで、コストを削減できる可能性があります。

【実務でどの程度使用されるのか】

API Gatewayは、実務で非常に広く使用されています。 特に、以下のようなケースでAPI Gatewayの利用が一般的です。

  • Webアプリケーション、モバイルアプリケーションのバックエンドAPI: Webアプリケーションやモバイルアプリケーションから利用されるバックエンドAPIの公開、管理にAPI Gatewayが活用されます。
  • マイクロサービスアーキテクチャ: マイクロサービスとして構築されたバックエンドサービス群を、API Gatewayを通じて外部に公開するケースが増えています。各マイクロサービスのエンドポイントを直接公開するのではなく、API Gatewayを経由することで、セキュリティ、トラフィック管理、モニタリングなどを一元的に行うことができます。
  • 外部API公開: 自社のサービスやデータを外部の開発者やパートナー企業に公開するためのAPIを構築する際に、API Gatewayが利用されます。
  • サーバーレスアーキテクチャ: AWS LambdaなどのサーバーレスコンピューティングサービスとAPI Gatewayを組み合わせることで、スケーラブルでコスト効率の高いAPIを構築するケースが増えています。
  • レガシーシステムのAPI化: 既存のレガシーシステムをAPI Gatewayでラップし、モダンなAPIとして公開することで、レガシーシステムの再活用、モバイル対応などを実現できます。

API Gatewayの利用頻度が高い理由:

  • API管理の効率化: APIの作成、公開、保守、セキュリティ対策、モニタリングなど、API管理に必要な機能を一元的に提供するため、API管理の効率を大幅に向上させることができます。
  • 開発効率の向上: API Gatewayが提供する様々な機能 (認証・認可、レート制限、キャッシュなど) を活用することで、開発者はAPIのビジネスロジックの実装に集中でき、開発効率を向上させることができます。
  • セキュリティの強化: API Gatewayでセキュリティ対策を一元的に行うことで、API全体のセキュリティレベルを向上させることができます。
  • スケーラビリティと可用性の確保: API Gateway自体がスケーラブルで可用性の高いサービスであるため、APIのスケーラビリティと可用性を容易に確保できます。

API Gatewayは、現代のWebアプリケーション、モバイルアプリケーション、マイクロサービスアーキテクチャにおいて、不可欠なコンポーネント と言えるでしょう。API Gatewayを適切に活用することで、API開発、運用、管理の効率化、セキュリティ強化、スケーラビリティと可用性の確保、コスト最適化など、多くのメリットを享受できます。

【terraformのコードで記述する場合の基本的内容と実務レベルの内容】

TerraformでAPI Gatewayを記述する場合、主に以下のリソースを組み合わせて定義します。

基本的なリソース:

  • aws_apigatewayv2_api: API Gateway API自体を定義します (REST API, HTTP API, WebSocket API)。
  • aws_apigatewayv2_stage: APIのステージ (例: dev, staging, prod) を定義します。ステージはAPIのデプロイ環境を分離するために使用されます。
  • aws_apigatewayv2_route: APIへのルーティングルールを定義します。HTTPメソッド、パス、ターゲット (バックエンドサービス) を指定します。
  • aws_apigatewayv2_integration: ルートとバックエンドサービス (Lambda関数、HTTPエンドポイントなど) の統合を定義します。
  • aws_apigatewayv2_authorizer: APIの認証・認可設定を定義します (APIキー、IAM認証、Cognito認証など)。

基本的なTerraformコード例 (HTTP API, Lambda統合):

resource "aws_apigatewayv2_api" "example" {
  name          = "example-api"
  protocol_type = "HTTP"
}

resource "aws_lambda_function" "example" {
  # Lambda関数の定義 (例: zipファイルからデプロイ)
  function_name = "example-lambda"
  runtime       = "nodejs16.x"
  handler       = "index.handler"
  # ... (zip_file, role など Lambda関数の設定)
}

resource "aws_apigatewayv2_integration" "example" {
  api_id             = aws_apigatewayv2_api.example.id
  integration_type   = "AWS_PROXY"
  integration_uri    = aws_lambda_function.example.invoke_arn
  payload_format_version = "2.0"
}

resource "aws_apigatewayv2_route" "example" {
  api_id    = aws_apigatewayv2_api.example.id
  route_key = "GET /example"
  target    = "integrations/${aws_apigatewayv2_integration.example.id}"
}

resource "aws_apigatewayv2_stage" "example" {
  api_id      = aws_apigatewayv2_api.example.id
  name        = "dev"
  auto_deploy = true
}

output "api_endpoint" {
  value = aws_apigatewayv2_stage.example.invoke_url
}

実務レベルのTerraformコードと考慮事項:

実務レベルでは、上記の基本的なリソースに加えて、以下の要素を考慮する必要があります。

  • モジュール化: API Gatewayの設定をモジュール化することで、コードの再利用性、可読性、保守性を向上させます。
  • 変数: 環境 (dev, staging, prod) や設定値 (API名、ステージ名、Lambda関数名など) を変数化することで、柔軟性を高めます。
  • backend設定: Terraformの状態ファイル (terraform.tfstate) をリモートに保存 (例: S3, DynamoDB) し、チームでの共同作業やCI/CDパイプラインとの連携を容易にします。
  • 認証・認可設定: aws_apigatewayv2_authorizer リソースを使用して、APIキー、IAM認証、Cognito認証などの認証・認可設定を記述します。
  • レート制限設定: aws_apigatewayv2_stage リソースの default_route_settings ブロックで、レート制限 (スロットリング) 設定を行います。
  • キャッシュ設定: aws_apigatewayv2_stage リソースの default_route_settings ブロックで、キャッシュ設定を行います。
  • カスタムドメイン設定: aws_apigatewayv2_domain_name および aws_apigatewayv2_domain_name_configuration リソースを使用して、カスタムドメインを設定します。
  • WAF連携: aws_wafv2_web_acl_association リソースを使用して、AWS WAFとAPI Gatewayを連携させます。
  • ログ設定: aws_apigatewayv2_stage リソースの access_log_settings ブロックで、アクセスログの設定を行います。
  • OpenAPI定義のインポート: aws_apigatewayv2_api リソースの body 引数にOpenAPI (Swagger) 定義を記述することで、既存のOpenAPI定義をインポートできます。
  • CI/CDパイプラインとの連携: TerraformコードをGitリポジトリで管理し、CI/CDパイプライン (例: AWS CodePipeline, GitHub Actions) を構築することで、APIのデプロイを自動化します。

実務レベルのTerraformコード例 (モジュール化、変数、認証、レート制限):

# modules/api_gateway/main.tf

resource "aws_apigatewayv2_api" "api" {
  name          = var.api_name
  protocol_type = "HTTP"
}

resource "aws_apigatewayv2_integration" "integration" {
  api_id             = aws_apigatewayv2_api.api.id
  integration_type   = "AWS_PROXY"
  integration_uri    = var.lambda_function_invoke_arn
  payload_format_version = "2.0"
}

resource "aws_apigatewayv2_route" "route" {
  api_id    = aws_apigatewayv2_api.api.id
  route_key = var.route_path
  target    = "integrations/${aws_apigatewayv2_integration.integration.id}"
  authorizer_id = var.enable_auth ? aws_apigatewayv2_authorizer.api_key.id : null # 認証を有効にするかどうかで条件分岐
}

resource "aws_apigatewayv2_stage" "stage" {
  api_id      = aws_apigatewayv2_api.api.id
  name        = var.stage_name
  auto_deploy = true

  default_route_settings {
    throttling_burst_limit = var.throttling_burst_limit
    throttling_rate_limit  = var.throttling_rate_limit
  }
}

resource "aws_apigatewayv2_authorizer" "api_key" {
  count = var.enable_auth ? 1 : 0 # 認証を有効にするかどうかでリソース作成を制御
  api_id             = aws_apigatewayv2_api.api.id
  name               = "ApiKeyAuth"
  authorizer_type    = "API_KEY"
  authorizer_uri     = null # APIキー認証の場合は不要
  identity_sources = ["$request.header.x-api-key"]
}

output "api_endpoint" {
  value = aws_apigatewayv2_stage.stage.invoke_url
}

# modules/api_gateway/variables.tf

variable "api_name" {
  type        = string
  description = "API Gateway API name"
}

variable "stage_name" {
  type        = string
  description = "API Gateway stage name"
  default     = "dev"
}

variable "lambda_function_invoke_arn" {
  type        = string
  description = "Lambda function invoke ARN"
}

variable "route_path" {
  type        = string
  description = "API route path"
}

variable "throttling_burst_limit" {
  type        = number
  description = "API throttling burst limit"
  default     = 1000
}

variable "throttling_rate_limit" {
  type        = number
  description = "API throttling rate limit"
  default     = 100
}

variable "enable_auth" {
  type        = bool
  description = "Enable API Key authentication"
  default     = true
}

# main.tf (ルートモジュール)

module "api_gw_example" {
  source = "./modules/api_gateway"

  api_name                 = "my-example-api"
  stage_name               = "prod"
  lambda_function_invoke_arn = aws_lambda_function.example.invoke_arn # Lambda関数のARNを渡す
  route_path               = "GET /items"
  throttling_burst_limit   = 500
  throttling_rate_limit    = 50
  enable_auth              = true
}

resource "aws_lambda_function" "example" {
  # Lambda関数の定義 (例: zipファイルからデプロイ)
  function_name = "example-lambda"
  runtime       = "nodejs16.x"
  handler       = "index.handler"
  # ... (zip_file, role など Lambda関数の設定)
}

上記の例は、モジュール化、変数、認証 (APIキー)、レート制限を組み込んだ実務レベルのTerraformコードの基本的な構成を示しています。実際のプロジェクトでは、さらに多くの要素 (カスタムドメイン、WAF連携、ログ設定など) をTerraformコードに含める必要があります。また、Terraform CloudやTerraform Enterpriseなどのツールを活用することで、状態管理、CI/CDパイプラインとの連携、チームでの共同作業をより効率的に行うことができます。

Discussion