AWS LambdaからNAT Gateway経由で固定IPアクセスを実現する完全ガイド
エンタープライズシステムとの連携やサードパーティAPIとの通信において、IP制限は依然として重要なセキュリティ対策として広く採用されています。サーバーレスアーキテクチャの採用が進む中で、AWS Lambdaから固定IPでの通信を実現する需要は増加の一途を辿っています。
本記事では、2025年現在の最新のAWS仕様に基づき、Lambda関数からNAT Gateway経由で固定IPアクセスを実現する方法について、実装例を交えながら詳しく解説していきます。特に、2019年に実施されたVPC Lambda のネットワーク改善により、従来の課題だったコールドスタート問題が大幅に改善された点についても触れながら、プロダクション環境で安心して利用できる設計パターンを提示します。
Lambdaの実行環境に関する重要な認識
Lambda関数の実行場所の正しい理解
まず最初に、Lambda関数の実行場所について正しく理解することが重要です。Lambda関数は「Edge」ロケーションで実行されるという誤解が散見されますが、これは事実ではありません。AWS公式ドキュメントによると、通常のAWS Lambda関数は「リージョン内のAvailability Zone」で実行されます。
エッジで動作するのは「Lambda@Edge」という別のサービスであり、これはCloudFrontと連携して動作する特殊な実行環境です。通常のLambda関数とLambda@Edgeは、実行場所も用途も大きく異なるため、この違いを正確に理解しておくことが設計の第一歩となります。
VPC内Lambda関数のネットワーク特性
Lambda関数をVPCに接続した場合の重要な特性として、Lambda関数自体にはパブリックIPを割り当てることができないという点があります。これは、パブリックサブネットに配置したとしても同様です。そのため、VPC内のLambda関数からインターネットへアクセスするには、必ず「NAT Gateway」もしくは「NAT Instance」を経由する必要があります。
この仕様は、EC2インスタンスがパブリックサブネットに配置すればElastic IPやパブリックIPを持てることと大きく異なる点であり、Lambda特有の制約として理解しておく必要があります。
2019年のVPCネットワーク改善がもたらした革新
コールドスタート問題の大幅な改善
2019年9月にAWSが発表したVPC Lambdaのネットワーキング改善は、VPC接続Lambda関数の実用性を大きく向上させる画期的なアップデートでした。従来、VPC内のLambda関数は起動時にENI(Elastic Network Interface)の作成と接続に時間がかかり、コールドスタートが10秒以上かかることも珍しくありませんでした。
この改善により、ENIの管理がAWS Hyperplaneというサービスに移行され、Lambda関数の起動時間が劇的に短縮されました。現在では、VPC接続による追加のレイテンシはほとんど無視できるレベルまで改善されており、プロダクション環境でも安心して採用できるようになっています。
改善後の実装上のメリット
この改善がもたらした最大のメリットは、VPC接続に対する心理的なハードルが大きく下がったことです。以前は「VPCに入れるとコールドスタートが重くなる」という理由で、VPC接続を避ける傾向がありましたが、現在はこの懸念はほぼ解消されています。これにより、セキュリティ要件に応じて柔軟にVPC接続を選択できるようになりました。
NAT Gatewayを活用した固定IP実装パターン
基本的な設計アプローチ
VPC内のLambda関数から固定IPでインターネットアクセスを実現する基本的な設計パターンは、以下の構成となります。Lambda関数をプライベートサブネットに配置し、そのサブネットのルートテーブルで、デフォルトルート(0.0.0.0/0)をNAT Gatewayに向けます。NAT Gatewayはパブリックサブネットに配置し、Elastic IP(EIP)を割り当てることで、固定IPでの送信を実現します。
NAT GatewayとElastic IPの重要な仕様
NAT Gatewayの仕様として特に注意すべき点は、複数のElastic IPを割り当てた場合の挙動です。NAT Gatewayに複数のEIPを割り当てると、フローハッシュアルゴリズムによって送信元IPが分散されます。これは、トラフィックのスケーラビリティを向上させる機能ですが、単一の固定IPを厳密に要求される環境では問題となる可能性があります。
そのため、接続先のシステムが単一IPのみを許可リストに登録する仕様の場合は、NAT GatewayのEIPは必ず1つのみに制限する必要があります。一方、複数IPの登録が可能な場合は、複数EIPを活用してトラフィックを分散させることで、より高いスループットを実現できます。
高可用性と固定IPのトレードオフ
プロダクション環境では高可用性が求められますが、固定IP要件との間にトレードオフが生じます。AWS公式ドキュメントでは、各Availability ZoneにNAT Gatewayを配置することを推奨していますが、これは各AZで異なるEIPを持つことを意味します。
単一の固定IPを厳格に要求される場合、単一AZにNAT Gatewayを配置せざるを得ず、そのAZに障害が発生した場合のリスクを受け入れる必要があります。一方、接続先システムが複数IPの登録を許可する場合は、各AZのNAT GatewayのEIPをすべて許可リストに登録することで、高可用性と固定IP要件の両立が可能になります。この設計判断は、ビジネス要件とリスク許容度に基づいて慎重に行う必要があります。
最新のランタイムとツールチェーンへの移行
Node.js 22への移行の重要性
2023年11月にAWS Lambdaは Node.js 22のサポートを開始し、現在はこれが推奨ランタイムとなっています。Node.js 12.xは既に非推奨となっており、セキュリティアップデートも提供されないため、早急な移行が必要です。
Node.js 18以降の大きな変更点として、内蔵されているAWS SDKがv3にアップデートされている点があります。SDK v2に依存したコードは動作しないため、移行時にはコードの修正が必要になることに注意が必要です。
Serverless Frameworkとバンドラーの最新化
Serverless Frameworkもv4系がリリースされており、パフォーマンスとセキュリティの改善が施されています。また、バンドラーとしては「serverless-webpack」から「serverless-esbuild」への移行が進んでいます。esbuildはGoで書かれた高速なバンドラーで、webpackと比較して数倍から数十倍高速にビルドが完了します。
これらのツールチェーンの最新化により、開発体験が大幅に向上し、CI/CDパイプラインの実行時間も短縮されます。特に大規模なプロジェクトでは、ビルド時間の短縮は開発生産性に直結する重要な要素となります。
実装例:Serverless Frameworkを使用した構築
プロジェクト構成と依存関係の定義
最新のツールチェーンを使用したプロジェクト構成を見ていきます。package.jsonでは、Node.js 22に対応したパッケージバージョンを使用し、esbuildをバンドラーとして採用します。
以下のような依存関係を定義することで、モダンな開発環境を構築できます。
{
"name": "lambda-fixed-ip-example",
"version": "2.0.0",
"description": "Lambda with fixed IP using NAT Gateway",
"scripts": {
"deploy": "sls deploy -v",
"remove": "sls remove"
},
"devDependencies": {
"serverless": "^4.10.0"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.500.0",
"axios": "^1.6.5"
}
}
VPCとNAT Gatewayの構成
VPCの構成において特に重要なのは、DNS設定の正しい構成です。AWS公式ドキュメントによると、「enableDnsSupport」はVPC内でのDNS解決を有効にし、「enableDnsHostnames」はEC2インスタンスへのパブリックホスト名の割り当てを有効にします。
Lambda関数から外部APIへアクセスする場合、DNS解決は必須であるため、「enableDnsSupport」は必ずtrueに設定する必要があります。また、VPC内のリソース間でホスト名を使用した通信を行う場合は、「enableDnsHostnames」もtrueに設定します。
# serverless.yml
service: lambda-fixed-ip-example
frameworkVersion: '>=3'
provider:
name: aws
runtime: nodejs22.x
region: ap-northeast-1
stage: ${opt:stage, 'dev'}
functions:
getSourceIp:
handler: src/functions/getSourceIp.handler
events:
- httpApi:
path: /source-ip
method: get
vpc:
securityGroupIds:
- Ref: PrivateSecurityGroup
subnetIds:
- Ref: VpcPrivateSubnetA
- Ref: VpcPrivateSubnetC
requestWithFixedIp:
handler: src/functions/requestWithFixedIp.handler
environment:
API_ENDPOINT: ${self:custom.apiEndpoint}
events:
- httpApi:
path: /request
method: post
vpc:
securityGroupIds:
- Ref: PrivateSecurityGroup
subnetIds:
- Ref: VpcPrivateSubnetA
- Ref: VpcPrivateSubnetC
custom:
esbuild:
bundle: true
minify: true
commonTag:
- Key: "Service"
Value: ${self:service}-${self:provider.stage}
apiEndpoint:
Fn::Join:
- ""
- - "https://"
- Ref: HttpApi
- ".execute-api."
- ${self:provider.region}
- ".amazonaws.com"
resources:
- ${file(./infrastructure/network.yml)}
ネットワークインフラストラクチャの定義
NAT Gatewayを含むネットワーク構成を定義します。単一の固定IPを保証するため、NAT GatewayにはElastic IPを1つのみ割り当てます。
# infrastructure/network.yml
Resources:
ExampleAppVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: '10.0.0.0/16'
EnableDnsSupport: true
EnableDnsHostnames: true
Tags: ${self:custom.commonTag}
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags: ${self:custom.commonTag}
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref ExampleAppVpc
# Public Subnets
VpcPublicSubnetA:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: 'ap-northeast-1a'
CidrBlock: '10.0.1.0/24'
MapPublicIpOnLaunch: true
Tags: ${self:custom.commonTag}
VpcId: !Ref ExampleAppVpc
# Private Subnets
VpcPrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: 'ap-northeast-1a'
CidrBlock: '10.0.10.0/24'
Tags: ${self:custom.commonTag}
VpcId: !Ref ExampleAppVpc
VpcPrivateSubnetC:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: 'ap-northeast-1c'
CidrBlock: '10.0.11.0/24'
Tags: ${self:custom.commonTag}
VpcId: !Ref ExampleAppVpc
# Route Tables
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
Tags: ${self:custom.commonTag}
VpcId: !Ref ExampleAppVpc
PrivateRouteTableA:
Type: AWS::EC2::RouteTable
Properties:
Tags: ${self:custom.commonTag}
VpcId: !Ref ExampleAppVpc
# Route Table Associations
PublicRouteTableAssociationA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref VpcPublicSubnetA
RouteTableId: !Ref PublicRouteTable
PrivateRouteTableAssociationA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref VpcPrivateSubnetA
RouteTableId: !Ref PrivateRouteTableA
PrivateRouteTableAssociationC:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref VpcPrivateSubnetC
RouteTableId: !Ref PrivateRouteTableA
# Routes
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref InternetGateway
PrivateRouteA:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableA
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref NatGatewayA
# Security Groups
PrivateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for Lambda functions"
SecurityGroupEgress:
- CidrIp: "0.0.0.0/0"
IpProtocol: "-1"
Tags: ${self:custom.commonTag}
VpcId: !Ref ExampleAppVpc
# NAT Gateway and Elastic IP
NatElasticIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags: ${self:custom.commonTag}
NatGatewayA:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatElasticIP.AllocationId
SubnetId: !Ref VpcPublicSubnetA
Tags: ${self:custom.commonTag}
Lambda関数の実装
HTTP APIを使用する場合、送信元IPの取得方法がREST APIとは異なる点に注意が必要です。REST API(v1)では「requestContext.identity.sourceIp」でアクセスしますが、HTTP API(v2)では「requestContext.http.sourceIp」となります。
import {Context} from 'aws-lambda';
export const handler = async (event:Context) => {
// HTTP API (v2) を使用する場合
const sourceIp = event.requestContext?.http?.sourceIp;
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sourceIp: sourceIp,
timestamp: new Date().toISOString(),
message: 'Source IP retrieved successfully'
}),
};
};
import axios from 'axios';
import {Context} from 'aws-lambda';
export const handler = async (event:Context) => {
const apiEndpoint = process.env.API_ENDPOINT;
try {
// 自身のAPIエンドポイントにリクエストを送信
const response = await axios.get(`${apiEndpoint}/source-ip`);
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
fixedIp: response.data.sourceIp,
message: 'Request sent with fixed IP',
timestamp: new Date().toISOString()
}),
};
} catch (error) {
console.error('Error making request:', error);
return {
statusCode: 500,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
error: 'Failed to make request',
details: error.message
}),
};
}
};
運用とコスト最適化の考慮事項
NAT Gatewayのコスト構造と削減策
NAT Gatewayの料金体系は、時間単位の料金とデータ処理料金の二階建てになっています。2025年1月現在、東京リージョンでは1時間あたり約0.062 USDの基本料金に加え、処理データ1GBあたり約0.062 USDが課金されます。
コスト削減の観点から重要なのは、AWS内のサービスへのアクセスについては、可能な限りVPCエンドポイントを使用することです。S3やDynamoDBへのアクセスはGatewayエンドポイントを使用することで、NAT Gatewayを経由せずに通信でき、データ処理料金を大幅に削減できます。
また、同一リージョン内の複数AZ間でデータ転送が発生する場合、追加のデータ転送料金が発生することにも注意が必要です。Lambda関数とNAT Gatewayを同じAZに配置することで、このコストを最小化できます。
監視とトラブルシューティング
固定IP経由の通信を行うLambda関数の運用では、以下の監視項目が重要になります。NAT Gatewayのメトリクスとして、「ActiveConnectionCount」で同時接続数を監視し、上限(55,000接続)に近づいていないか確認します。「PacketsDropCount」でパケットドロップが発生していないか監視し、ネットワークの健全性を確認します。
Lambda関数側では、タイムアウトエラーの発生率を監視し、ネットワーク経路の問題を早期に検知します。また、実行時間の推移を監視することで、ネットワークレイテンシの増加を検知できます。
セキュリティグループとNACLの設計
VPC内のLambda関数のセキュリティは、セキュリティグループとネットワークACL(NACL)の二層で制御します。Lambda関数に付与するセキュリティグループは、アウトバウンドルールで必要な通信のみを許可する最小権限の原則に従います。インバウンドルールは基本的に不要ですが、VPC内の他のリソースからの通信が必要な場合は、適切に設定します。
NACLはサブネットレベルでの制御となるため、より広範な制御に使用します。プライベートサブネットのNACLは、デフォルトですべての通信を許可しますが、セキュリティ要件に応じて制限を加えることも検討します。
EventBridgeを活用した定期実行パターン
CloudWatch EventsからEventBridgeへの移行
定期的にLambda関数を実行する場合、以前は「CloudWatch Events」を使用していましたが、現在はEventBridgeが推奨されています。EventBridgeはCloudWatch Eventsの進化形であり、より豊富な機能を提供します。
EventBridge Schedulerを使用することで、秒単位の精密なスケジューリングが可能になり、タイムゾーンを考慮したスケジュール設定も容易になります。また、リトライポリシーやデッドレターキューの設定も統合されており、より堅牢な定期実行システムを構築できます。
VPC Lambda関数の定期実行における考慮事項
2019年のネットワーク改善により、VPC内Lambda関数のコールドスタート問題は大幅に改善されましたが、完全にゼロになったわけではありません。定期実行で厳密な実行時刻が要求される場合は、「Provisioned Concurrency」の使用を検討します。
Provisioned Concurrencyを設定することで、指定した数の実行環境を常に温かい状態で維持でき、コールドスタートを完全に回避できます。ただし、追加コストが発生するため、ビジネス要件とコストのバランスを考慮した判断が必要です。
プロダクション環境での実装チェックリスト
デプロイ前の確認項目
プロダクション環境へのデプロイ前には、以下の項目を確認することが重要です。まず、VPCのDNS設定が正しく構成されているか確認します。「enableDnsSupport」と「enableDnsHostnames」が適切に設定されていることを確認します。
ルートテーブルの設定として、プライベートサブネットのデフォルトルートがNAT Gatewayを指していることを確認します。パブリックサブネットのデフォルトルートがInternet Gatewayを指していることも確認が必要です。
本番運用における推奨事項
本番環境では、以下の実装を推奨します。Lambda関数のタイムアウト設定は、外部API呼び出しの最大応答時間を考慮して適切に設定します。デフォルトの3秒では不十分な場合が多いため、実際の通信パターンに基づいて調整します。
エラーハンドリングとリトライロジックを実装し、一時的なネットワークエラーに対する耐性を持たせます。exponential backoffを実装することで、API側への負荷も軽減できます。
CloudWatch Logsへの適切なログ出力を実装し、問題発生時のトラブルシューティングを容易にします。構造化ログを採用することで、ログの検索と分析が効率化されます。
まとめ・今後の展望
現在の技術スタックの成熟度
AWS LambdaからNAT Gateway経由で固定IPアクセスを実現する技術は、2025年現在、十分に成熟したソリューションとなっています。2019年のVPCネットワーク改善により、パフォーマンス面での懸念はほぼ解消され、プロダクション環境での採用に適した状態になっています。
Serverless Frameworkやesbuildといったツールチェーンも成熟し、開発生産性と運用効率の両立が可能になっています。Node.js 22のサポートにより、最新のJavaScript APIを活用した効率的な開発が可能です。
将来的な改善の可能性
今後期待される改善として、NAT Gatewayのコスト最適化オプションの拡充が挙げられます。現在は時間課金とデータ処理課金の組み合わせですが、予約インスタンスのような長期契約による割引オプションが追加される可能性があります。
また、Lambda関数のネイティブな固定IP機能の実装も期待されています。現在はNAT Gatewayを経由する必要がありますが、将来的にはLambda関数に直接Elastic IPを割り当てる機能が追加される可能性もあります。
セキュリティ面では、AWS PrivateLinkの活用がさらに進むと予想されます。より多くのSaaSサービスがPrivateLink対応となることで、インターネットを経由しない安全な通信が標準になっていくでしょう。
本記事で紹介した実装パターンは、現時点でのベストプラクティスですが、AWSのサービスは継続的に進化しています。定期的に最新情報をキャッチアップし、より良い実装方法を模索し続けることが、プロフェッショナルなエンジニアとして重要だと考えています。