連載: VyOSで作るAWSハイブリッド接続(全4回)
- VyOSでつくるAWS Site-to-Site VPN — IPsec+BGPの基礎
- AWS Site-to-Site VPNの冗長化とBGP経路制御
- VGWからTransit Gatewayへ — VPNのECMP冗長化
- マルチリージョン接続 — DX-GWの限界とTransit Gatewayピアリング(この記事)
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経由で別リージョンの経路を学習しないからです。
これはAWSの一貫した設計原則 「共通のゲートウェイにぶら下がっただけのもの同士は、自動では相互接続しない(非トランジティブ)」 の一例です。VPCピアリングで「A-B と B-C を張ってもA-Cは通らない」のと同じ発想。DX-GWは「オンプレと各リージョンをつなぐ集約点」であって、「ぶら下がったTGW同士を中継するルータ」ではありません。後述の allowed_prefixes で経路を許可できても、それは「オンプレ↔そのリージョン」という縦方向の話で、リージョン同士の横方向は対象外です。

図の × がそれです。オンプレからは各リージョンに届くのに、東京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宛のルートを手で登録して初めて通ります。
なぜ自動で伝播しないのか — association と propagation
その理由を理解するには、TGWのルーティングを支える2語を押さえる必要があります。TGWのルートテーブルは、アタッチメントごとに 「向き先(association)」と「学習(propagation)」という別々のレバーで制御されます。
| 用語 | 意味 | 例えるなら |
|---|---|---|
| association | そのアタッチメントから入った通信が、どのルートテーブルを引いて次の転送先を決めるか | 機器に紐づける「参照するルーティングテーブル」 |
| propagation | そのアタッチメントが知っている経路を、どのルートテーブルへ自動で書き込むか | BGPで学んだ経路をRIBへ流し込むイメージ |
VPNアタッチメントやVPCアタッチメントは propagation に対応しています。VPNならBGPで学んだオンプレ経路、VPCならそのCIDRが、ルートテーブルへ自動で入る。第1回で有効化した「VGWのルート伝搬」と同じ発想の仕組みです — ただしあちらはVPCのルートテーブルへ、こちらはTGWのルートテーブルへの書き込みという違いがあります。
ところが、ピアリングアタッチメントは propagation 非対応です。AWSの仕様で、ピアリング越しの経路は自動学習されません。だから association 先のルートテーブルに、相手リージョンのCIDRをスタティックに自分で書くしかない。VPNやVPCのダイナミックな学習に慣れているほど、ここで確実にハマります。
ピアリングは「線をつなぐ(association)」だけ。「どの宛先をその線に流すか」は別途スタティックに教える、と覚えてください。下のTerraformで association_default_route_table_id(=各TGWのデフォルトルートテーブル)へルートを書いているのは、この association 先テーブルへの手動登録そのものです。
// ピアリング接続を張る(東京→バージニア)
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バックボーン経由でリージョン間を通っていることが数字でも分かります。
2つの実運用メモ:
- 暗号化: TGWピアリングのリージョン間トラフィックは、AWSのグローバルバックボーン上を通り、自動で暗号化されます。VPNのように自分でIPsecを張る必要はありません(その代わり、経路の取り回しはAWS任せになります)。
- 課金: リージョン間のデータ転送にはクロスリージョンのデータ転送料金がかかります。レイテンシだけでなくコストも「太平洋を渡る分」が乗る、と頭に入れておくと見積りで事故りません。VPC内やリージョン内の通信感覚で設計すると、後で請求に驚きます。
5. うまくいかないときの確認ポイント
リージョン間は「ピアリングは張れているのに通らない」が定番です。今回はBGPによる動的学習がない分、症状の大半はルートの書き忘れとassociation先の取り違えに集約されます。
| 症状 | 確認するところ |
|---|---|
| リージョン間が全く通らない | ピアリングアタッチメントが available か。両リージョンのTGWルートテーブルに相手CIDR宛のスタティックルートを両方向とも書いたか(§2②) |
| 片方向だけ通る | 戻りルートの書き忘れ。例: 東京→バージニアは通るのにバージニア→オンプレが返らない → バージニアTGWに 10.0.2.0/24 宛ルートがあるか |
| ルートはあるのに届かない | association先のルートテーブルが想定通りか。アタッチメントが引いているルートテーブルと、ルートを書いたルートテーブルが一致しているか |
| VyOSに別リージョン経路が来ない | 東京TGW→VPN方向の伝播。show ip bgp でバージニアCIDRがAS-PATH 65100 越しに見えているか(§3) |
ピアリングが pending のまま | バージニア側で accepter(受諾)リソースが適用されているか(§2②のhcl) |
6. 連載のまとめと、ここから先
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はリージョン間を繋がない(非トランジティブ)。ピアリングは propagation 非対応で、ルートは association 先テーブルへ手書き |
道具の守備範囲を取り違えない、というのが通底するテーマでした。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構成は、そのまま実験台に使えます。