連載: VyOSで作るAWSハイブリッド接続(全4回)
- 単一Site-to-Site VPN — VyOS×AWSの基礎
- トンネル冗長化とBGP経路制御
- Transit Gatewayへの置き換えとECMP
- マルチリージョン接続 — DX-GWの限界とTGWピアリング(この記事)
0. この回のテーマ
第3回で、VPNの終端をリージョンのハブ=Transit Gatewayにしました。ハブができたので、次の自然な欲求は 「別のリージョンともつなぎたい」 です。
東京(ap-northeast-1)のVPCと、バージニア(us-east-1)のVPCを相互接続し、さらにオンプレ(VyOS)から両リージョンに届く状態をゴールにします。
ここで多くの人が最初に考えるのが Direct Connect Gateway(DX-GW) です。「DX回線を複数リージョンで共用できる」と聞くと、リージョン間もつながりそうに見える。ところが、ここに落とし穴があります。この回は「DX-GWでは越えられない壁」を確認してから、正しい道具=TGWピアリングで超えます。
1. なぜDX-GWではリージョン間がつながらないのか
DX-GWは、1本のDirect Connect回線を、複数リージョン・複数アカウントのVPC/TGWから共用するための仕組みです。「オンプレ↔各リージョン」の通信を提供します。
ですが、DX-GWはリージョン間(TGW↔TGW)のルートを交換しません。各リージョンのTGWルートテーブルは、DX-GW経由で別リージョンの経路を学習しないからです。

図の × がそれです。オンプレからは各リージョンに届くのに、東京VPC ↔ バージニアVPC が直接つながらない。DX-GWはあくまで「オンプレとAWSをつなぐDX回線の共用ポイント」であって、「リージョン間ルータ」ではないからです。
補足: 今回DXは用意できないので、DX-GWは「あるものと想定」して実際には構築していません。Terraform定義だけ置いておきます。要点は 「DX-GWの守備範囲はオンプレ↔AWSであって、リージョン間ではない」 という役割の理解です。
DX-GWのTerraform(参考・未構築)
resource "aws_dx_gateway" "test" {
name = "satoshi-dxgw"
amazon_side_asn = "64600"
}
// 東京TGWをDX-GWに関連付け
resource "aws_dx_gateway_association" "jp_tgw" {
dx_gateway_id = aws_dx_gateway.test.id
associated_gateway_id = aws_ec2_transit_gateway.tgw.id
allowed_prefixes = [
"10.99.0.0/16", # 東京VPC
"10.0.2.0/24", # オンプレ(VPN経由)
]
}
// バージニアTGWをDX-GWに関連付け
resource "aws_dx_gateway_association" "us_tgw" {
provider = aws.us
dx_gateway_id = aws_dx_gateway.test.id
associated_gateway_id = aws_ec2_transit_gateway.tgw_us.id
allowed_prefixes = ["10.201.0.0/16"] # バージニアVPC
}
allowed_prefixes で各リージョンの経路を許可しても、それは「オンプレ↔そのリージョン」の話。リージョン同士の経路はここには現れません。
2. リージョン間をつなぐ正解 — Transit Gatewayピアリング
リージョン間を直接つなぐ道具が TGWピアリングです。東京TGWとバージニアTGWを1対1でピアリングし、AWSのバックボーン上でリージョン間トラフィックを運びます。

構成のポイントは2つあります。
① TGWのASNをリージョンごとに分ける
ピアリングする2つのTGWは、別々のASNを持たせます(今回は東京 65100 / バージニア 65200)。
// 東京TGW
resource "aws_ec2_transit_gateway" "tgw" {
amazon_side_asn = 65100
auto_accept_shared_attachments = "enable"
tags = { Name = "satoshi-tgw-jp" }
}
// バージニアTGW
resource "aws_ec2_transit_gateway" "tgw_us" {
provider = aws.us
amazon_side_asn = 65200
auto_accept_shared_attachments = "enable"
tags = { Name = "satoshi-tgw-us" }
}
② ピアリングは「張る」と「ルートを書く」が別作業
ここが一番の落とし穴です。TGWピアリングはBGPでルートを自動伝播しません。ピアリング接続を張っただけでは1パケットも流れない。各TGWのルートテーブルに、相手リージョンのCIDR宛のルートを手で登録して初めて通ります。
VPNやVPC内のダイナミックなルート学習に慣れていると、ここで確実にハマります。ピアリングは「線をつなぐ」だけ、「どの宛先をその線に流すか」は別途スタティックに教える、と覚えてください。
// ピアリング接続を張る(東京→バージニア)
resource "aws_ec2_transit_gateway_peering_attachment" "jp_us" {
peer_region = "us-east-1"
peer_transit_gateway_id = aws_ec2_transit_gateway.tgw_us.id
transit_gateway_id = aws_ec2_transit_gateway.tgw.id
}
// バージニア側で受諾
resource "aws_ec2_transit_gateway_peering_attachment_accepter" "tgw_us" {
provider = aws.us
transit_gateway_attachment_id = aws_ec2_transit_gateway_peering_attachment.jp_us.id
}
// 東京TGWルートテーブル: バージニアVPC宛 → ピアリングへ
resource "aws_ec2_transit_gateway_route" "jp_to_us" {
destination_cidr_block = "10.201.0.0/16" // バージニアVPC
transit_gateway_attachment_id = aws_ec2_transit_gateway_peering_attachment.jp_us.id
transit_gateway_route_table_id = aws_ec2_transit_gateway.tgw.association_default_route_table_id
}
// バージニアTGWルートテーブル: 東京VPC宛・オンプレ宛 → ピアリングへ
resource "aws_ec2_transit_gateway_route" "us_to_jp" {
provider = aws.us
destination_cidr_block = "10.99.0.0/16" // 東京VPC
transit_gateway_attachment_id = aws_ec2_transit_gateway_peering_attachment.jp_us.id
transit_gateway_route_table_id = aws_ec2_transit_gateway.tgw_us.association_default_route_table_id
}
resource "aws_ec2_transit_gateway_route" "us_to_onprem" {
provider = aws.us
destination_cidr_block = "10.0.2.0/24" // オンプレ(VyOS)
transit_gateway_attachment_id = aws_ec2_transit_gateway_peering_attachment.jp_us.id
transit_gateway_route_table_id = aws_ec2_transit_gateway.tgw_us.association_default_route_table_id
}
バージニア側からオンプレ(10.0.2.0/24)に戻すルートも忘れずに。東京TGWを経由してVPNに落ちるので、10.0.2.0/24宛もピアリングアタッチメントに向けます。
3. オンプレ(VyOS)から見えるもの
面白いのは、VyOSは何も追加設定していないのに、バージニアVPC(10.201.0.0/16)への経路を学習している点です。東京AWSが、VPN越しに「US向けはこっち」とBGPで広告してくれるからです。しかも第3回のECMPが効いていて、2本のトンネル両方から等コストで届いています。
vyos@vyos:~$ show ip route
B>* 10.99.0.0/16 [20/100] via 169.254.61.113, vti1, weight 1 ## 東京VPC
* via 169.254.234.49, vti0, weight 1
B>* 10.201.0.0/16 [20/100] via 169.254.61.113, vti1, weight 1 ## バージニアVPC(新規)
* via 169.254.234.49, vti0, weight 1
BGPテーブルでも、東京・バージニア両方のVPCが、AS-PATH 65100(東京TGWのASN)越しに2本で見えています。
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.61.113 100 0 65100 i
*> 169.254.234.49 100 0 65100 i
*= 10.201.0.0/16 169.254.61.113 100 0 65100 i
*> 169.254.234.49 100 0 65100 i
4. エンドツーエンドの疎通確認
3つの経路をすべて確認します。
東京VPC → バージニアVPC(リージョン間。これがDX-GWでは通らなかった経路):
$ ping 10.201.33.240
64 bytes from 10.201.33.240: icmp_seq=1 ttl=252 time=147 ms
64 bytes from 10.201.33.240: icmp_seq=2 ttl=252 time=145 ms
オンプレVyOS → バージニアVPC(VPN→東京TGW→ピアリング→バージニア、と3ホップ跨ぐ):
vyos@vyos:~$ ping 10.201.33.240 source-address 10.0.2.15
64 bytes from 10.201.33.240: icmp_seq=1 ttl=251 time=169 ms
64 bytes from 10.201.33.240: icmp_seq=2 ttl=251 time=170 ms
バージニアVPC → オンプレVyOS(戻り。バージニアTGWの10.0.2.0/24ルートが効いている証明):
$ ping 10.0.2.15
64 bytes from 10.0.2.15: icmp_seq=1 ttl=61 time=166 ms
64 bytes from 10.0.2.15: icmp_seq=2 ttl=61 time=176 ms
東京経由(約25ms)に対し、バージニアは約145〜170ms。太平洋を渡る分のレイテンシがそのまま乗っていて、AWSバックボーン経由でリージョン間を通っていることが数字でも分かります。
5. 連載のまとめと、ここから先
4回かけて、VyOSを偽オンプレCGWに見立てたまま、AWSハイブリッド接続の能力を1段ずつ積み上げました。
| 回 | 到達点 | 核心 |
|---|---|---|
| 1 | 単一VPN | IPsec+BGPの基礎と「謎の値の出どころ」 |
| 2 | 冗長化 | BGP経路制御は方向で道具が変わる(Weight / AS-Path Prepend) |
| 3 | TGW + ECMP | 終端をハブルータTGWに。MED 100/100で両系アクティブ、非対称ルーティングの副作用 |
| 4 | マルチリージョン | DX-GWはリージョン間を繋がない。TGWピアリングは「張る」と「ルートを書く」が別作業 |
道具の守備範囲を取り違えない、というのが通底するテーマでした。VGWとTGW、DX-GWとTGWピアリング——名前が似ていても役割の格が違います。
今後の拡張(未検証・構想)
この検証環境はさらに伸ばせます。以下は今後の課題として:
- AWS Network Firewall: SG/ACLでは難しい、ステートフル+ブラックリスト+ドメインベースのフィルタを中央集約。TGWの経路上に挟む構成
- Accelerated Site-to-Site VPN: AWS Global Accelerator経由でVPNを高速化(TGW終端が必須、VGW不可)
- PrivateLink / Route 53 Resolver: サービス公開・DNSのハイブリッド解決
ベースができていれば、これらは「経路の途中に何を挟むか」の話に落ちます。土台としてのVyOS×TGW構成は、そのまま実験台に使えます。