AWS EC2で「connect: No route to host」を修正する — Internet GatewayまたはNAT Gatewayの設定漏れ

intermediate☁️ AWS2026-05-04| AWS EC2(Amazon Linux 2 / Ubuntu / 任意のAMI)、パブリックまたはプライベートサブネットを持つVPC

Error Message

connect: No route to host
#aws#vpc#ec2#internet-gateway#nat-gateway#networking

エラーの内容

EC2インスタンスにSSH接続して、パッケージをインストールしようとしたり、外部APIにアクセスしようとしたり、8.8.8.8にpingを打ったりすると、以下のエラーが発生します:

$ curl https://api.example.com
curl: (7) Failed to connect to api.example.com port 443: No route to host

$ ping 8.8.8.8
ping: connect: No route to host

$ yum update
Could not retrieve mirrorlist... connect: No route to host

深夜2時。デプロイは止まっている。インスタンスは起動しているし、セキュリティグループも問題なさそうなのに、アウトバウンド接続がことごとく失敗する。インターネットへの通信が一切届かない状態です。

根本原因

VPC内のEC2インスタンスは、デフォルトではインターネットアクセスが一切ありません。外部へのトラフィックには、明示的な経路が必要です。95%のケースは以下の2つのシナリオに当てはまります:

  • パブリックサブネットにInternet Gateway(IGW)が存在しない: VPCにIGWがアタッチされていないか、ルートテーブルに 0.0.0.0/0 → igw-xxxxxxxx のエントリが存在しない。
  • プライベートサブネットにNAT Gatewayが存在しない: インスタンスにパブリックIPがなく、アウトバウンドトラフィックを中継するNAT Gatewayも存在しない。

3番目のパターンとして、IGWやNAT Gatewayは存在するものの、サブネットのルートテーブルがそれらを指していないケースもあります。いずれにせよ、VPCがインターネットから遮断された状態です。

まず診断する

まだ何も変更しないでください。実際にどの障害が発生しているかを特定しましょう。

1. インスタンスにパブリックIPが割り当てられているか確認する

# インスタンス内部から実行
curl -s http://169.254.169.254/latest/meta-data/public-ipv4
# 何も返ってこない、または"404" = パブリックIPが割り当てられていない

2. AWS CLIでルートテーブルを確認する

# インスタンスが属するサブネットを確認
aws ec2 describe-instances \
  --instance-ids i-0abc123def456 \
  --query 'Reservations[].Instances[].SubnetId' \
  --output text

# そのサブネットのルートテーブルを確認
aws ec2 describe-route-tables \
  --filters Name=association.subnet-id,Values=subnet-0xxxxxxx \
  --query 'RouteTables[].Routes'

# 以下のようなルートを探す:
# { "DestinationCidrBlock": "0.0.0.0/0", "GatewayId": "igw-..." }
# 存在しない場合、それが原因です。

3. VPCにIGWがアタッチされているか確認する

aws ec2 describe-internet-gateways \
  --filters Name=attachment.vpc-id,Values=vpc-0xxxxxxx \
  --query 'InternetGateways[].InternetGatewayId'

# 空の配列 [] = IGWがアタッチされていない

修正A — パブリックサブネット:Internet Gatewayをアタッチする

インスタンスがパブリックサブネットにあり、パブリックIPを持つべき場合はこの方法で修正します。

手順1:IGWを作成してアタッチする

# ゲートウェイを作成
IGW_ID=$(aws ec2 create-internet-gateway \
  --query 'InternetGateway.InternetGatewayId' \
  --output text)
echo "Created IGW: $IGW_ID"

# VPCにアタッチ
aws ec2 attach-internet-gateway \
  --internet-gateway-id $IGW_ID \
  --vpc-id vpc-0xxxxxxx

手順2:ルートテーブルにデフォルトルートを追加する

# パブリックサブネットのルートテーブルIDを取得
RTB_ID=$(aws ec2 describe-route-tables \
  --filters Name=association.subnet-id,Values=subnet-0xxxxxxx \
  --query 'RouteTables[].RouteTableId' \
  --output text)

# IGWを指す0.0.0.0/0ルートを追加
aws ec2 create-route \
  --route-table-id $RTB_ID \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id $IGW_ID

手順3:インスタンスにパブリックIPが割り当てられているか確認する

パブリックIPなしで起動した場合は、Elastic IPを割り当てる必要があります:

# Elastic IPを確保
EIP_ALLOC=$(aws ec2 allocate-address --domain vpc \
  --query 'AllocationId' --output text)

# インスタンスに関連付け
aws ec2 associate-address \
  --instance-id i-0abc123def456 \
  --allocation-id $EIP_ALLOC

修正B — プライベートサブネット:NAT Gatewayを追加する

プライベートサブネットのインスタンス(アプリサーバー、バックグラウンドワーカー、VPC内のLambdaなど)はパブリックIPを持ちません。アウトバウンドトラフィックを中継するために、パブリックサブネットにNAT Gatewayを配置する必要があります。

手順1:パブリックサブネットにNAT Gatewayを作成する

# NAT Gateway用のElastic IPを確保
EIP_ALLOC=$(aws ec2 allocate-address --domain vpc \
  --query 'AllocationId' --output text)

# パブリックサブネット(プライベートではない)にNAT Gatewayを作成
NAT_ID=$(aws ec2 create-nat-gateway \
  --subnet-id subnet-PUBLIC-0xxxxxxx \
  --allocation-id $EIP_ALLOC \
  --query 'NatGateway.NatGatewayId' \
  --output text)

echo "NAT Gateway: $NAT_ID"

# 利用可能になるまで約60秒かかります
aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT_ID

手順2:プライベートサブネットのルートテーブルを更新する

# プライベートサブネットのルートテーブルを取得
PRIVATE_RTB=$(aws ec2 describe-route-tables \
  --filters Name=association.subnet-id,Values=subnet-PRIVATE-0xxxxxxx \
  --query 'RouteTables[].RouteTableId' \
  --output text)

# すべてのアウトバウンドトラフィックをNAT Gateway経由でルーティング
aws ec2 create-route \
  --route-table-id $PRIVATE_RTB \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id $NAT_ID

修正C — ルートは存在するが誤ったターゲットを指している場合

ルートテーブルにすでに 0.0.0.0/0 のエントリがあるが、削除済みまたはデタッチ済みのゲートウェイを指している場合。重複を追加するのではなく、既存のルートを置き換えます:

aws ec2 replace-route \
  --route-table-id $RTB_ID \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id $IGW_ID  # プライベートサブネットの場合は --nat-gateway-id $NAT_ID

修正の確認

# インスタンスにSSH接続してアウトバウンド接続をテスト
curl -I https://www.google.com
# 期待する結果: HTTP/2 200

ping -c 3 8.8.8.8
# 期待する結果: 3 packets transmitted, 3 received

# ルートテーブルが正しいか確認
aws ec2 describe-route-tables \
  --route-table-ids $RTB_ID \
  --query 'RouteTables[].Routes[?DestinationCidrBlock==`0.0.0.0/0`]'

セキュリティグループの落とし穴

ゲートウェイの設定もルートも正しいのに、まだ No route to host が出る場合は、セキュリティグループのアウトバウンドルールを確認してください。デフォルトではすべてのエグレスが許可されていますが、誰かがある時点で制限をかけて、そのことを忘れているケースがよくあります:

aws ec2 describe-security-groups \
  --group-ids sg-0xxxxxxx \
  --query 'SecurityGroups[].IpPermissionsEgress'

# 以下のようなルールが含まれているべきです:
# { "IpProtocol": "-1", "IpRanges": [{ "CidrIp": "0.0.0.0/0" }] }

予防策

このエラーが発生する原因の9割は、手動でVPCを構築した場合や、Terraform/CloudFormationで自動生成した場合に、ゲートウェイやルートテーブルの設定を忘れることです。次回のために役立つ習慣をいくつか紹介します:

  • AWSコンソールのVPC Reachability Analyzerを使うと、ネットワーク経路全体をトレースして、どこでトラフィックが遮断されているかを正確に教えてくれます。実際のパケットは送信されないので、推測は不要です。
  • Terraformでサブネットに tier=public / tier=private タグを付ける。数週間後にVPCの設定を見直すとき、どのサブネットがIGW経由かNAT経由かが一目でわかります。
  • Terraformではaws_subnetと明示的なaws_route_table_associationを必ずセットで定義する。メインルートテーブルに依存するのは静かな落とし穴で、新しいサブネットがすべてそれを継承するため、メインテーブルを変更すると触っていない箇所まで壊れます。
  • **CIDRの設計は重要です。**重複しているCIDRや不適切なサイズのサブネットは、デバッグが困難な微妙なルーティング問題を引き起こします。ToolCraftのSubnet Calculatorのようなブラウザベースのツールを使えば、VPCのCIDRを事前に適切に設計できます。情報が外部に送信されることもありません。

実際に動作するTerraformの最小構成

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
}

resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

Related Error Notes