連載: VyOSで作るAWSハイブリッド接続(全4回)
- 単一Site-to-Site VPN — VyOS×AWSの基礎
- トンネル冗長化とBGP経路制御
- Transit Gatewayへの置き換えとECMP(この記事)
- マルチリージョン接続 — DX-GWの限界とTGWピアリング
0. この回のテーマ
第2回では、VPNの終端に VGW(仮想プライベートゲートウェイ) を使い、2本のトンネルをBGPでアクティブ/スタンバイに制御しました。
今回はこの終端を Transit Gateway(TGW) に置き換えます。狙いは2つです。
- 2本のトンネルを同時に使う(ECMP / ロードバランス) 構成にする
- 将来の拡張(複数VPC・マルチリージョン)の土台を作る
VGWとTGWは役割の格が違う
ネットワーク機器の感覚で対比すると分かりやすいです。
| VGW | Transit Gateway | |
|---|---|---|
| 立ち位置 | 1つのVPC専用のVPN終端装置 | リージョン規模のハブルータ |
| 束ねられるもの | そのVPCとのVPN/DXだけ | 複数VPC・複数VPN・DX・別リージョンのTGWまで |
| 例えるなら | 拠点ルータのVPNモジュール | データセンターのコアスイッチ/ルータ |
VGWが「VPCに後付けするVPN口」なら、TGWは「VPCもVPNもDXも全部ぶら下げる中央ルータ」です。今はVPN1本をつなぐだけですが、第4回でこのハブ性がマルチリージョン接続の鍵になります。
そしてもう一つ、TGWはECMP(Equal-Cost Multi-Path)に対応しています。これが今回の挙動差の本体です。
1. VyOS側: 経路制御を「外す」
第2回では、2本を意図的に偏らせるためにWeightとAS-Path Prependを入れました。ECMPは両トンネルを等コストにして同時利用するのが目的なので、この偏りを削除します。
// VyOS → AWS の偏り(Weight)を削除
delete protocols bgp neighbor 169.254.234.49 address-family ipv4-unicast weight '300'
// AWS → VyOS の偏り(AS-Path Prepend)を削除
delete protocols bgp neighbor 169.254.61.113 address-family ipv4-unicast route-map export 'VPC-Tunnel-2-OUT'
削除後のBGPテーブルを見ると、2本が等コストになり、*=(multipath = ECMP対象)のマークが付きます。
vyos@vyos:~$ show ip bgp
Network Next Hop Metric LocPrf Weight Path
*> 10.0.2.0/24 0.0.0.0 0 32768 i
*> 10.99.0.0/16 169.254.234.49 100 0 64512 i
*= 169.254.61.113 100 0 64512 i ## = はmultipath
*> と *= の両方が有効経路として使われる状態、これがECMPです。
2. AWS側: VGWを外しTGWを作る(Terraform)
ここから初めてTerraformで組みます。やることは「TGWを作る」「ネットワーク用サブネットにアタッチする」「VPN接続をTGWに紐づける」の3点です。
// Transit Gateway本体
resource "aws_ec2_transit_gateway" "tgw" {
auto_accept_shared_attachments = "enable"
tags = { Name = "satoshi-tgw" }
}
// TGWをネットワーク用サブネットにアタッチ
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw" {
subnet_ids = [aws_subnet.network.id]
transit_gateway_id = aws_ec2_transit_gateway.tgw.id
vpc_id = var.vpc.id
tags = { Name = "tgw-attach-shared-vpc" }
}
// Publicサブネットから、オンプレ(10.0.2.0/24)宛をTGWへ向けるルート
resource "aws_route" "public_tgw" {
route_table_id = aws_route_table.pub.id
destination_cidr_block = "10.0.2.0/24"
transit_gateway_id = aws_ec2_transit_gateway.tgw.id
}
// Site-to-Site VPNの接続先を VGW ではなく TGW にする
resource "aws_vpn_connection" "vpn" {
transit_gateway_id = aws_ec2_transit_gateway.tgw.id // ← ここがVGWからの変更点
customer_gateway_id = aws_customer_gateway.cgw.id
type = "ipsec.1"
static_routes_only = false // 動的(BGP)
tunnel1_log_options {
cloudwatch_log_options {
log_enabled = true
log_group_arn = aws_cloudwatch_log_group.vpn.arn
log_output_format = "json"
}
}
tags = { Name = "${var.name}-vpn-01" }
}
TGWアタッチで踏みやすい3つの注意点
- AZの一致: あるサブネットからTGW経由で通信したいなら、そのサブネットと同じAZにTGWのアタッチメント(ENI)が存在している必要があります。AZをまたぐと無言で届きません。
- 専用サブネット推奨: TGWアタッチ用に小さな専用サブネットを切るのがベストプラクティス。EC2を置くサブネットと混ぜない。
- ルートテーブルへの明示: VPCのルートテーブルに、オンプレ宛(
10.0.2.0/24)をTGW向きで登録する必要があります。VGWの「ルート伝搬」のようには自動で入りません。
3. 検証: VGWとの挙動差(MEDとECMP)
ここが第2回との一番面白い対比です。VPNテーブルを見ると、両トンネルともMEDが100で通知されています。
vyos@vyos:~$ show ip bgp
Network Next Hop Metric LocPrf Weight Path
*> 10.0.2.0/24 0.0.0.0 0 32768 i
*> 10.99.0.0/16 169.254.234.49 100 0 64512 i
* 169.254.61.113 100 0 64512 i
第2回のVGWは 100 / 200 を付けてアクティブ/スタンバイに誘導してきました。TGWは 100 / 100 の等コスト。これがTGWのデフォルトが**ECMP(両系アクティブ)**である証拠です。終端を替えただけで、AWS側のBGP広告の性格が変わる、というのは押さえどころです。
| VGW(第2回) | TGW(今回) | |
|---|---|---|
| AWSが付けるMED | 100 / 200 | 100 / 100 |
| デフォルト挙動 | アクティブ/スタンバイ | ECMP(両系同時利用) |
非対称ルーティングに注意
ECMPの実機検証で、実際にこういう現象が出ます。AWS側からpingを打つと、行きと戻りで別々のトンネルを通ることがあります。

VyOS側から打つと、両方向とも同じ経路(.6系)に乗りました。

これがECMPの本質的な副作用、非対称ルーティングです。経路が等コストで複数あると、ハッシュ次第で行きと戻りが別トンネルになる。
ICMPなら問題なく返りますが、経路上にステートフルな機器(FW・NAT・IDS等)があると致命的になり得ます。行きのパケットしか見ていない機器が戻りを不正と判断して落とすからです。実運用でECMPを使うなら、経路上のステートフル機器の有無を必ず確認してください。スループットを稼ぎたいだけなら第2回のアクティブ/スタンバイの方が安全、という判断もあります。
4. 完成形
VPNの終端がVGWからTransit Gatewayに替わり、2本のトンネルがECMPで同時に使われる構成です。

まとめ
- VGWは1VPC専用のVPN口、TGWはリージョンのハブルータ。格が違う
- 終端をTGWにすると、AWSのMEDが 100/100の等コストになり、デフォルトで**ECMP(両系アクティブ)**になる(VGWは100/200のアクティブ/スタンバイ)
- ECMPには非対称ルーティングの副作用がある。経路上にステートフル機器があると危険
- TGWアタッチは AZ一致・専用サブネット・ルートの明示登録 が踏みどころ
- TGWというハブを手に入れたことで、次回は別リージョンとの接続に進めます。ここで「DX-GWではリージョン間がつながらない」という壁にぶつかり、TGWピアリングで超えます。