0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

月額固定費を削るために ALB から public IPv4 を外した話

0
Last updated at Posted at 2026-09-06

要約

  • 個人運営の Web サービス (Go + Next.js を ECS Fargate で運用) から NAT Gateway と public IPv4 をほぼ全廃した。VPC は IPv6 デュアルスタック、Web タスクは IPv4 アドレスを持たない IPv6-only サブネットで動き、ALB は dualstack-without-public-ipv4 で public IPv4 を持たない。
  • 動機は固定費。NAT Gateway は 1 AZ あたり時間課金 (執筆時点の東京で約 0.062USD/時、月約 45USD) に加えて処理データ量課金があり、小規模サービスでは最大の固定費になりうる。public IPv4 アドレスも 2024 年から時間課金 (約 0.005/USD、こちらはリージョン非依存の一律料金) になった。
  • 「AWS の API も外部 API も全部 IPv6 で届く」わけではない。届かないものをどう扱うかの設計が本題で、答えは 3 通り: dualstack エンドポイントの明示、IPv4 が必要な処理だけ public IPv4 を付けて隔離、どうしても IPv4 が要る呼び出しを VPC 外の Lambda に肩代わりさせる。

構成

  • VPC: natGateways: 0。Public / Isolated の 2 種サブネットに Amazon 提供の IPv6 CIDR を追加し、さらに AZ ごとに ipv6Native: true の IPv6-only パブリックサブネットを作る。ルートは ::/0 → Internet Gateway。VPC エンドポイントは無料の S3 Gateway Endpoint だけ。
  • ALB: ipAddressType = dualstack-without-public-ipv4、ターゲットグループも ipv6。前段に Cloudflare を置き、ALB のセキュリティグループは Cloudflare の IPv6 帯だけを 443/80 で許可する (CDN バイパス防止を兼ねる)。
  • ECS Fargate: Web タスク (ARM64 / Graviton、Fargate Spot 優先) は IPv6-only サブネットに assignPublicIp: false で配置。コンテナイメージは ECR の dualstack URI (*.dkr-ecr.<region>.on.aws) から pull する。
  • Aurora: networkType: DUAL。

落とし穴

落とし穴 1: AWS サービスの「既定エンドポイント」は IPv4-only のものがある

SDK の既定エンドポイントは多くが IPv4-only で、IPv6-only のタスクからは名前解決はできても接続できない。対処はタスクの環境変数 AWS_USE_DUALSTACK_ENDPOINT=true を全タスクに付けること。これで S3 / SSM / CloudWatch Logs / Secrets Manager / ECR などの dualstack エンドポイントに切り替わる。

ただし例外がある。

  • Athena は SDK 側でも明示的に UseDualStackEndpoint を有効にする必要があった。
  • 一部の AWS API (Bedrockなど) は執筆時点で dualstack 未対応で、逆に UseDualStackEndpoint = Disabled を明示して IPv4 経路に固定した (後述の public IPv4 付きバッチで実行する)。
  • SES の SendEmail は dualstack エンドポイントを持つが、既定の IPv4 経路をそのまま使う実装にしたため、メール送信を含むバッチは同じく public IPv4 側に寄せた (同居するバッチとの整合を優先した)。

「対応済み」と思い込まず、サービスごとに dig AAAA と実接続で確認するのが早い。IndexNow の送信先 (api.indexnow.org) のように、確認したら dualstack だったので IPv6-only 側に残せたものもある。

落とし穴 2: 外部 API と CDN の IPv6 対応状況

外部サービスは IPv4-only がまだ多い。このサービスでは次が該当した。

  • 提携先の商品 API と画像 CDN (IPv4-only)
  • LINE Messaging API (api.line.me / api-data.line.me、IPv4-only)
  • 一部の決済系テストネット

対処は用途で分けた。

  1. バッチ処理: Step Functions から起動する Fargate タスクのうち、IPv4 が必要な実行モード (商品 API 取得、画像取得、外部投稿、メール送信) だけを public IPv4 付きでパブリックサブネットに配置し、それ以外のモードは IPv6-only サブネットで動かす。モードごとの振り分けは CDK の定数リストで管理する。public IPv4 は時間課金なので、短時間で終わるバッチに限定すれば NAT Gateway の常時課金より安い。
  2. Web からの同期呼び出し: LINE bot の画像取得と返信は Web サーバーから同期的に呼ぶ必要があり、Web タスクに IPv4 を付けたくない。そこで この 2 呼び出しだけを VPC 外の Lambda (AWS マネージドの IPv4 出口を持つ) に肩代わりさせる「egress proxy」を置いた。Lambda は資格情報を持たず、トークンは invoke の payload で渡す。Lambda 同期 invoke の 6 MB 制限に合わせ、画像は 4 MiB 上限にした。署名検証は Go 側で行い、Lambda は純粋な HTTP 転送に徹する。

落とし穴 3: SSR の fetch が「必ず失敗する IPv4」を試しに行く

Next.js の SSR から API を呼ぶ経路で、ある日突然 500 が出た。Sentry には IPv4 の ENETUNREACH 2 件と IPv6 の ETIMEDOUT 2 件が束ねられた AggregateError が残っていた。

原因は Happy Eyeballs。API の FQDN が A と AAAA の両方を返すため、Node の undici が IPv4 も試し、IPv6-only 環境では 構造的に必ず失敗する IPv4 試行が発生していた。普段は IPv6 側が先に成功するので気づかず、IPv6 側が一時的に遅い瞬間だけ全体が落ちる。

対処は undici のグローバル Dispatcher に connect: { family: 6 } を設定し、IPv4 候補の生成自体を止めること。「IPv6-only の環境では、IPv4 を試す余地を残さない」が教訓。

落とし穴 4: SSR の API 呼び出しを CDN 経由にしない

SSR からの API 呼び出しを公開 FQDN (Cloudflare 経由) にすると、同じ VPC 内の ALB に行くのに一度インターネットへ出ることになる。internet-facing ALB の ENI に付く IPv6 アドレスは VPC の IPv6 CIDR 内なので、Cloudflare を通さない DNS-only のホスト名を用意して ALB に直接向ければ、VPC 内のローカルルーティングで届く。ALB のセキュリティグループには VPC の IPv6 CIDR からの 443 だけを追加し、外部からそのホスト名を引いても Cloudflare 帯と VPC CIDR 以外は弾かれるので実質内部専用になる。

落とし穴 5: 既存 ALB から public IPv4 は簡単には消えない

既存 ALB を dualstack-without-public-ipv4 に変更しても public IPv4 が残留することがある。ECS サービスを CodeDeploy (Blue/Green) 制御にしていると、CloudFormation から ALB やターゲットグループを差し替える更新が通らないケースがあり、結局スタックをバージョン付きで並行作成して切り替えた。最初から public IPv4 なしで作る方がはるかに楽。

効果

  • NAT Gateway の時間課金と処理量課金がゼロになった。public IPv4 の課金は短時間のバッチ実行分だけ。
  • 引き換えに増えたのは「dualstack 対応状況を確認する運用」と、IPv4 が必要な呼び出しを隔離する設計。どちらも一度整えれば増えない。

感想

IPv6もっと普及してくれ。あとAWSはIPv4割り当てに料金とるならばIPv6 onlyでは使えないものを無くしてくれ。

まとめ

  • IPv6-only は「対応しているサービスを選ぶ」のではなく「対応していないものをどこに隔離するか」を設計する話だった。
  • AWS_USE_DUALSTACK_ENDPOINT=true を全タスクに、SDK 側で個別指定が要るサービスはコードで明示、IPv4 が要る処理は public IPv4 付きバッチか VPC 外 Lambda へ。
  • Happy Eyeballs の IPv4 試行は明示的に殺す。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?