コスト削減のため、VPC に NAT Gateway を置かない構成があります。NAT Gateway は、private subnet からインターネットや AWS の public endpoint へ出るための AWS マネージドな中継サービスです。便利な一方で、トラフィックの有無に関わらず時間課金が発生します。
常時稼働する本番サービスであれば、そのコストを受け入れる判断もあります。しかし、月に数回だけ動くバッチや、検証環境のクローラのために NAT Gateway を常時起動しておくのは、コストに見合わないことがあります。
このような環境で、ECS Fargate によるバッチやクローラを動かしたくなることがあります。Fargate タスクは起動時に ECR からイメージを pull し、実行中は CloudWatch Logs へログを送ります。private subnet にタスクを置いたまま、NAT Gateway と VPC エンドポイント のどちらも用意しない場合、このイメージ pull は失敗します。
# private subnet にタスクを置いた構成。NAT も VPC エンドポイントも無い
NetworkConfiguration = {
AwsvpcConfiguration = {
Subnets = var.private_subnet_ids # 0.0.0.0/0 の出口が無い
SecurityGroups = [var.task_security_group_id]
AssignPublicIp = "DISABLED"
}
}
この状態でタスクを起動すると、PENDING のまま RUNNING に到達せず、最終的にイメージ pull のタイムアウトで停止します。停止理由には CannotPullContainerError が記録されます。タスクのコードが失敗しているのではなく、コンテナーを起動する前の段階で、ECR からイメージを取得できていない状態です。
一般に、private subnet に置いた Fargate タスクが ECR へ到達するには、NAT Gateway か VPC エンドポイントが必要です。ただし、この説明には「タスクを private subnet に置く」という前提があります。
inbound (外部から入ってくる接続。Web サーバーが受け取るリクエストなど) が不要なバッチやクローラであれば、別の選択肢があります。タスクを public subnet に置き、assignPublicIp: ENABLED にする構成です。これにより、タスクは Internet Gateway 経由で ECR・S3・CloudWatch Logs の public endpoint に到達できます。対象は ECS Fargate platform version 1.4.0 系、awsvpc ネットワークモード です。
private subnet で ECR pull が失敗する理由
private subnet は、インターネットへ直接出る経路を持たない subnet です。route table に 0.0.0.0/0 の宛先があっても、それが Internet Gateway ではなく NAT Gateway を向いている、あるいはそもそも外向きの default route が無い、という構成になります。
そのため、private subnet に置いたタスクが AWS の public endpoint へ出るには、次のいずれかが必要です。
- NAT Gateway 経由で Internet Gateway へ出る
- VPC エンドポイントを使い、対象 AWS サービスへ VPC 内の経路で到達する
NAT Gateway を使う場合、経路は次のようになります。
Fargate task
private subnet
↓
NAT Gateway
↓
Internet Gateway
↓
ECR / S3 / CloudWatch Logs
VPC エンドポイントを使う場合は、ECR や S3 などの AWS サービスへ、VPC 内の経路で到達させます。
Fargate task
private subnet
↓
VPC Endpoint
↓
ECR / S3 / CloudWatch Logs
どちらも正しい構成です。特に本番環境や、通信経路を明確に制御したい環境では、private subnet に置いたうえで NAT Gateway や VPC エンドポイントを使う設計は自然です。
ただし、月数回だけ起動する inbound 不要のバッチに対して、常時 NAT Gateway を起動しておくことが常に最適とは限りません。ここで検討できるのが、public subnet に Fargate タスクを置き、タスク ENI に public IP を割り当てる構成です。
ECR pull に必要な到達先
ECR pull を考えるときに注意が必要なのは、通信先が ECR だけではないことです。
ECR はコンテナーイメージの保管先ですが、pull の内部では複数の AWS サービスが関係します。イメージの manifest や認証に関わる通信は ECR に向かいます。一方で、イメージレイヤの実体は、ECR が背後で利用している S3 から取得されます。さらに、タスク実行中に CloudWatch Logs へログを送る場合は、Logs への通信も必要です。
整理すると、主な到達先は次のようになります。
| 到達先 | 役割 | 経路設計上の意味 |
|---|---|---|
| ECR API | 認証トークンやイメージ情報の取得 | pull の開始に必要 |
| ECR Docker endpoint | Docker registry としての通信 | イメージ pull に必要 |
| S3 | イメージレイヤ本体の取得 | ECR pull の途中で必要 |
| CloudWatch Logs | タスク実行中のログ送信 | 起動後のログ出力に必要 |
このため、VPC エンドポイントで private subnet から pull とログ送信まで成立させる場合は、ecr.api・ecr.dkr・s3・logs などを揃える必要があります。ECR のエンドポイントだけを用意しても、S3 への経路が無ければレイヤ本体を取得できず、pull が途中で失敗します。
VPC エンドポイントは有効な選択肢ですが、必要なエンドポイントが増えるほど、設定対象とコストの両方が増えます。Interface 型の VPC エンドポイント には ENI ごとの時間課金が発生するため、少数のバッチのために複数本のエンドポイントを常時維持するかどうかは、要件とコストのバランスで判断する必要があります。
一方、public subnet + public IP + Internet Gateway の構成では、これらの到達先を個別に閉域化しません。ECR・S3・CloudWatch Logs の public endpoint へ、いずれも Internet Gateway 経由で到達させます。
Fargate task
public subnet
public IP あり
↓
Internet Gateway
↓
ECR / S3 / CloudWatch Logs
到達先ごとに VPC エンドポイントを用意するのではなく、タスク自身が public IP を持ち、public endpoint へ外向き通信する構成です。
public subnet と assignPublicIp による経路
Fargate の awsvpc ネットワークモードでは、タスクごとに ENI (Elastic Network Interface) が割り当てられます。assignPublicIp を ENABLED にすると、このタスク ENI に public IP が割り当てられます。
# public subnet にタスクを置き、public IP を割り当てる
NetworkConfiguration = {
AwsvpcConfiguration = {
Subnets = var.public_subnet_ids # 0.0.0.0/0 が IGW を向く subnet
SecurityGroups = [var.task_security_group_id]
AssignPublicIp = "ENABLED" # task ENI に public IP を割り当てる
}
}
この構成で重要なのは、AssignPublicIp = "ENABLED" だけでは外向き通信が成立しないことです。タスクを置く subnet が public subnet である必要があります。
ここでいう public subnet とは、route table の 0.0.0.0/0 が Internet Gateway を向いている subnet です。タスク ENI が public IP を持ち、その ENI が public subnet 上にあるとします。そのうえで route table が Internet Gateway への経路を持っていれば、public endpoint への外向き通信が初めて成立します。
経路を図にすると、次のようになります。
private subnet に置いたまま assignPublicIp だけを ENABLED にしても、この構成にはなりません。subnet 側に Internet Gateway への route が無ければ、public IP を持っていても外へ出る経路がありません。
つまり、この構成の条件は次の組み合わせです。
- タスクを public subnet に配置する
- その subnet の route table で
0.0.0.0/0が Internet Gateway を向いている -
assignPublicIpをENABLEDにする - Security Group で inbound を許可しない
- 必要な egress を許可する
public IP は、出口のある subnet に置かれて初めて意味を持ちます。
IAM 権限とネットワーク経路の分離
ECR pull には、ネットワーク経路だけでなく IAM 権限も必要です。ここは切り分けて考える必要があります。
Fargate タスク起動時に ECR からイメージを pull し、CloudWatch Logs へログを送るには、タスク定義の execution role へ適切な権限が必要です。一般的には AWS 管理ポリシーの AmazonECSTaskExecutionRolePolicy を割り当てます。
ネットワーク経路と IAM 権限は別の前提です。
ネットワーク経路がない
→ ECR や S3 に到達できない
IAM 権限がない
→ 到達できても操作が許可されない
どちらが欠けても pull は失敗します。CannotPullContainerError が出た場合、private subnet から外へ出られない問題だけでなく、execution role の権限不足も確認対象になります。
RUNNING 到達による確認
この構成が正しく動いているかは、タスクの状態遷移で確認できます。
Fargate タスクは、概ね次のように状態遷移します。
PROVISIONING
↓
PENDING
↓
RUNNING
イメージ pull は PENDING の段階で行われます。ECR や S3 への到達に失敗すると、タスクは RUNNING に進まず、STOPPED になり、停止理由として CannotPullContainerError が記録されます。
逆に、タスクが RUNNING に到達した場合、その起動時点では少なくとも ECR pull に必要な経路が機能していたと判断できます。ECR と S3 への到達が成立しなければ、コンテナーは起動できないためです。
さらに、起動後に CloudWatch Logs へログが出力されていれば、Logs への通信も成立していることが確認できます。
RUNNING に到達する
→ ECR / S3 からの pull が成功している
CloudWatch Logs にログが出る
→ Logs への egress も成功している
手動で RunTask した場合でも、Step Functions から入力付きで起動した場合でも、Fargate タスクとして起動される以上、pull に必要なネットワーク経路は同じです。Step Functions 経由だから ECR pull の経路が変わるわけではありません。
inbound を持たないタスクとして扱う
public IP を割り当てると、タスク ENI はインターネットから到達可能なアドレスを持ちます。ただし、public IP を持つことは、外部からの接続を受け付けることを意味しません。
外部から接続できるかどうかは、Security Group の ingress ルールによって制御します。inbound が不要なバッチやクローラであれば、ingress ルールを定義しない構成にします。Security Group は、明示的に許可した inbound だけを通すため、ingress を空にすれば外部からの接続は受け付けません。
resource "aws_security_group" "task" {
name = "${var.project}-${var.environment}-task-sg"
description = "Batch/crawler task. egress only."
vpc_id = var.vpc_id
# ingress は定義しない
# 外部からの接続を受け付けない
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
この設定では、タスクは public IP を持ちながら、外部からの接続を受け付けません。タスク自身から ECR・S3・CloudWatch Logs へ出ていく egress だけを許可する形です。
ただし、これは private subnet と同じという意味ではありません。public IP を持つ構成であること自体は、private subnet に置く構成とは異なります。そのため、この構成は「public IP を持っても問題ない」と判断できる、inbound 不要のバッチやクローラに限定して使うのが前提です。
特に、ALB 配下の Web タスクや常駐 API のように inbound が必要なサービスでは、この前提が崩れます。inbound を開ける必要があるサービスを public IP 付きで直接公開する設計は、別のリスク評価が必要です。
コスト面での効果と適用範囲
この構成で削減できる主なコストは、NAT Gateway の固定費です。NAT Gateway は、起動している間ずっと時間課金が発生し、さらに通過したデータ量にも課金されます。間欠的にしか動かないバッチのために NAT Gateway を常時起動している場合、この固定費が目立ちます。
public subnet + public IP + Internet Gateway の構成では、NAT Gateway を経由しません。そのため、NAT Gateway の時間課金と処理料は発生しません。Internet Gateway 自体には時間課金がありません。
ただし、すべての通信コストが消えるわけではありません。public IPv4 アドレスの料金や、データ転送料は別途確認が必要です。特に、大量のデータ転送が継続的に発生する本番ワークロードでは、NAT Gateway の有無だけで判断せず、全体の通信量と課金体系を見る必要があります。
この構成が向いているのは、次のようなワークロードです。
- dev 環境や検証環境のタスク
- 月数回、週数回だけ起動するバッチ
- 外部からの接続を受け付けないクローラ
- 起動時に ECR pull ができ、実行中に外向き通信ができればよい処理
- NAT Gateway の固定費を避けたい環境
一方で、次のような要件には向きません。
- 外部から inbound を受ける Web サービス
- ALB 配下で常時稼働する API
- AWS サービスへの通信を VPC 内に閉じたいシステム
- public IP を持たせたくない環境
- 本番で厳格なネットワーク統制が必要な環境
整理すると、選択肢は次のようになります。
| 構成 | 特徴 | 向くケース |
|---|---|---|
| private subnet + NAT Gateway | private subnet から public endpoint へ外向き通信する | private 配置を維持しつつ、外部通信が必要な構成 |
| private subnet + VPC Endpoint | AWS サービスへの通信を VPC 内の経路に寄せる | 閉域性や経路制御を重視する構成 |
| public subnet + public IP | NAT Gateway なしで public endpoint へ直接出る | inbound 不要で、低コストに動かしたい Fargate バッチ |
コスト最小化と閉域性は別の軸です。NAT Gateway をなくせるから常に良い、という話ではありません。どの要件を優先するかによって、選ぶ構成は変わります。
VPC Lambda との違い
この構成は、VPC に接続した Lambda には適用できません。Fargate タスクでこの構成が成立するのは、awsvpc モードのタスク ENI に public IP を割り当てられるためです。
ECS Fargate task
awsvpc mode
assignPublicIp = ENABLED
↓
task ENI に public IP が付く
↓
Internet Gateway 経由で egress できる
一方、VPC に接続した Lambda の ENI には、Fargate の assignPublicIp に相当する設定がありません。
VPC Lambda
VPC 内に ENI は作られる
ただし public IP を割り当てる設定はない
↓
Internet Gateway 経由で直接 egress できない
そのため、VPC Lambda から外向き通信をするには、NAT Gateway か VPC エンドポイントが必要です。
同じ VPC 内で動くワークロードであっても、Fargate と Lambda では egress の作り方が異なります。Fargate で使えた構成を Lambda にそのまま当てはめると、Lambda 側では通信できない構成になります。
まとめ
inbound が不要な Fargate バッチやクローラであれば、NAT Gateway と VPC エンドポイントのどちらも置かずに ECR からイメージを pull できます。条件は、タスクを public subnet に配置し、assignPublicIp: ENABLED にして、タスク ENI に public IP を割り当てることです。
このとき、subnet の route table では 0.0.0.0/0 が Internet Gateway を向いている必要があります。assignPublicIp だけを有効にしても、Internet Gateway への経路がなければ外向き通信は成立しません。
また、public IP を持たせる以上、Security Group で inbound を許可しないことが前提になります。inbound 不要のバッチとして扱い、外部からの接続は受け付けず、ECR・S3・CloudWatch Logs への egress だけを許可します。
この構成は、NAT Gateway の固定費を避けたい dev 環境や、間欠的に起動するバッチに向いています。一方で、閉域性が必要な場合は VPC エンドポイント、private 配置を維持したい場合は NAT Gateway、inbound が必要なサービスでは別構成を選びます。
Fargate では task ENI に public IP を割り当てられるためこの構成が成立しますが、VPC Lambda には同じ方法は使えません。VPC Lambda の egress には、NAT Gateway か VPC エンドポイントが必要です。
参考情報
- Amazon ECS task networking with the awsvpc mode
- AwsVpcConfiguration(
assignPublicIp)— Amazon ECS API リファレンス - Amazon ECR interface VPC endpoints (PrivateLink、ECR が S3 を使う点)
- Amazon ECS task execution IAM role (
AmazonECSTaskExecutionRolePolicy) - NAT gateways — Amazon VPC
- Internet gateways — Amazon VPC
- AWS Fargate for Amazon ECS (platform version)
- Configuring a Lambda function to access resources in a VPC (Lambda の VPC ENI)