سه تا Peering و ۱۰ دلار در ماه: کِی VPC Peering از Transit Gateway می‌بَره

aws networking terraform cost-optimization

ما یه AWS Organization کوچیک داریم. یه اکانت ops/internal چندتا سرویس داخلی مشترک رو نگه می‌داره. سه‌تا workload account (dev، staging، prod) هر کدوم VPC خودشون رو دارن و باید به این سرویس‌ها برسن. یه ریجن (us-east-1)، هر VPC با ۳ تا AZ. تمام معماری‌های مرجع AWS به Transit Gateway اشاره می‌کنن. من قیمتش رو در برابر ترافیک واقعی‌مون حساب کردم و به جاش VPC Peering رو انتخاب کردم. اینجا منطق و اعدادش رو می‌گم.

مسئله

چهارتا اکانت AWS زیر یه Organization:

  • ops-internal: VPC سرویس‌های مشترک، 172.16.0.0/16. یه مجموعه کوچیک از ابزارهای داخلی روش — observability، مدیریت secret، control plane برای connectivity داخلی. در مجموع سه‌تا سرویس.
  • dev: 172.17.0.0/16
  • staging: 172.18.0.0/16
  • prod: 172.19.0.0/16

VPCهای workload باید به VPC ops برسن. لازم نیست با همدیگه ارتباط داشته باشن — dev نباید prod رو ببینه، prod هم نباید dev رو. ساختار hub-and-spoke با ops در مرکز.

محدودیت‌ها:

  • تیم کوچیکیم. هرچی انتخاب کنم، خودم باید نگهش دارم.
  • یه ریجن، حداقل تا یه سال دیگه برنامه‌ای برای گسترش نداریم.
  • هزینه برامون مهمه. ما یه استارت‌آپیم؛ هر آیتم تکراری روی صورتحساب AWS باید توجیه داشته باشه.
  • هر چهارتا اکانت از قبل تو AWS Organizations هستن، پس IAM بین اکانت‌ها ساده‌ست.

این کل setup ـه. تصمیم جالب، لایه‌ی connectivity ـه.

گزینه‌هایی که بررسی کردم

گزینهچی هستکاربرد ایده‌آلمدل هزینه
VPC Peeringلینک مستقیم ۱:۱ بین دو VPCچندتا VPC، بدون نیاز به routing گذرا0$/ساعت، فقط هزینه‌ی data transfer
Transit Gatewayروتر منطقه‌ای، hub-and-spokeتعداد زیاد VPC، routing گذرا، inspection مرکزی$0.05/ساعت برای هر attachment + $0.02/GB
Site-to-Site VPNتونل IPSecهیبریدی (on-prem ↔ AWS)$0.05/ساعت برای هر connection + data out
PrivateLinkendpoint های سرویس مبتنی بر NLBافشای سرویس‌های مشخص بین اکانت‌ها$0.01/ساعت برای هر endpoint در هر AZ + $0.01/GB
VPC Latticeservice mesh در لایه‌ی اپلیکیشنتعداد زیاد سرویس، احراز هویت مبتنی بر identity$0.025/ساعت برای هر سرویس + $0.025/GB
Overlay در فضای کاربر (مدل Tailscale)mesh وایرگارد روی هر underlayconnectivity در لایه‌ی app برای هاست‌های شرکت‌کنندهرایگان / self-hosted

گزینه‌ی overlay یه نکته‌ی جدا داره. یه mesh وایرگارد جایگزین VPC Peering نمی‌شه؛ روی هر چی که زیرش داری می‌شینه و فقط هاست‌هایی که به mesh متصل بشن می‌تونن ازش استفاده کنن. برای ترافیک ماشین به ماشین بین EC2 هایی که agent اجرا نمی‌کنن — مثل scrape های Prometheus یا API call های داخلی — هنوز به connectivity در سطح VPC نیاز داری. حذفش به‌عنوان تنها لایه‌ی connectivity راحت بود: خیلی چیزها باید mesh-aware می‌شدن.

Site-to-Site VPN برای هیبریدیه (دیتاسنترت به AWS). برای استفاده‌ی داخل AWS، گرون و بدشکله — برای حل مسئله‌ای که AWS با Peering حلش کرده، باید هزینه‌ی تونل و عملیات customer gateway بدی. صرفاً برای کامل بودن لیست آوردمش.

می‌مونه چهارتا گزینه‌ی جدی: Peering، TGW، PrivateLink، Lattice.

چرا VPC Peering رو انتخاب کردم

چهارتا VPC در hub-and-spoke یعنی سه‌تا peering connection. همین. هشدار اسکیلینگ n*(n-1)/2 که همه تکرار می‌کنن فقط وقتی گاز می‌گیره که هر VPC با هر VPC دیگه‌ای باید حرف بزنه. در hub-and-spoke با یه hub، فقط n-1 ـه.

Tradeoff هایی که آگاهانه قبول کردم:

  • بدون routing گذرا. اگه یه روزی dev بخواد به staging برسه، باید یه peering چهارم اضافه کنم یا کلاً بازنگری کنم. الان نیاز نیست، و مسئله‌ی امروز تنها مسئله‌ایه که دارم حل می‌کنم.
  • ورودی دستی route table از هر دو طرف. هر دو VPC تو یه peering به route صریح روی pcx-* نیاز دارن. یه طرف یادت بره و ترافیک بی‌سروصدا تو black-hole گیر می‌کنه (پایین‌تر بیشتر).
  • CIDRها نباید همپوشانی داشته باشن. فضای آدرس رو از قبل برنامه‌ریزی کردم — /16 های مجاور زیر یه supernet با /14 — پس فقط یه‌بار هزینه‌ی ذهنی داشت.
  • بدون egress مرکزی، بدون inspection، بدون firewall hop. برامون قابل قبوله؛ تیم SecOps نداریم که نقطه‌ی inspection روی transit رو الزامی کنه.

نکته‌ی تعیین‌کننده: تو چهارتا VPC، فقط هزینه‌ی attachment ـه TGW در ماه حدود ۱۴۶ دلاره، قبل از این که حتی یه بایت رد بشه. Peering در حالت بدون ترافیک ۰ دلار/ماهه. این که «TGW بهتر اسکیل می‌شه» درسته، ولی رایگان نیست — و ما در مقیاسی نیستیم که سادگی عملیاتیش ۱۴۶ دلار در ماه بیارزه.

مرور setup

سه نکته‌ی برجسته در Terraform. الگو برای هر spoke تکرار می‌شه.

Provider alias برای cross-account. خود peering connection سمت requester (ops) ـه؛ resource ـه accepter سمت workload ـه. هر دو provider صریح می‌خوان — تو نسخه‌ی اولم، aws_vpc_peering_connection_accepter به اشتباه با provider ـه requester اجرا می‌شد. Terragrunt بدون مشکل apply کرد؛ accepter هیچ‌وقت نرفت بالا:

provider "aws" {
  alias  = "ops"
  region = "us-east-1"
  assume_role { role_arn = "arn:aws:iam::${var.ops_account_id}:role/TerragruntExec" }
}

provider "aws" {
  alias  = "workload"
  region = "us-east-1"
  assume_role { role_arn = "arn:aws:iam::${var.workload_account_id}:role/TerragruntExec" }
}

سمت requester (اکانت ops). برای peering تو یه ریجن، peer_region ست نکن — این آرگومان فقط برای inter-region ـه و ست کردنش AWS رو به حالت cross-region می‌بره (هزینه‌گذاری متفاوت، قوانین option block متفاوت). فلگ auto_accept اینجا فقط وقتی معنا داره که هر دو VPC تو یه اکانت باشن؛ برای cross-account باید یه resource ـه aws_vpc_peering_connection_accepter جدا با provider ـه accepter تعریف کنی:

resource "aws_vpc_peering_connection" "ops_to_workload" {
  provider      = aws.ops
  vpc_id        = aws_vpc.ops.id
  peer_vpc_id   = var.workload_vpc_id
  peer_owner_id = var.workload_account_id
  auto_accept   = false
  # peer_region نداره — same-region

  tags = { Name = "ops-to-${var.workload_env}" }
}

سمت accepter (اکانت workload):

resource "aws_vpc_peering_connection_accepter" "workload_from_ops" {
  provider                  = aws.workload
  vpc_peering_connection_id = aws_vpc_peering_connection.ops_to_workload.id
  auto_accept               = true

  tags = { Name = "from-ops" }
}

ورودی‌های route table — هر دو طرف:

resource "aws_route" "ops_to_workload" {
  provider                  = aws.ops
  route_table_id            = aws_route_table.ops_private.id
  destination_cidr_block    = var.workload_vpc_cidr
  vpc_peering_connection_id = aws_vpc_peering_connection.ops_to_workload.id
}

resource "aws_route" "workload_to_ops" {
  provider                  = aws.workload
  route_table_id            = var.workload_private_rt_id
  destination_cidr_block    = aws_vpc.ops.cidr_block
  vpc_peering_connection_id = aws_vpc_peering_connection.ops_to_workload.id
}

اون gotcha ای که ۳۰ دقیقه از عمرم رو خورد. اولین peering رفت روی ACTIVE. security group ها درست تنظیم شده بودن. ترافیک تو black-hole گیر کرد. بدون خطا، بدون ICMP unreachable، فقط دراپ خاموش بسته‌ها. ورودی route table رو سمت accepter یادم رفته بود. وضعیت peering و وضعیت routing مستقل از هم‌اند؛ AWS با کمال خوشحالی peering رو سالم گزارش می‌ده، در حالی که ترافیک واقعی هیچ‌جا نمی‌تونه بره.

gotcha دوم: DNS. هاست‌نیم‌های داخلی از سمت اکانت‌های workload به IP عمومی resolve می‌شدن تا وقتی که remote DNS resolution رو فعال کردم. برای peering های cross-account، این گزینه باید از هر طرف در اکانت خودش ست بشه — requester نمی‌تونه فلگ accepter رو ست کنه و برعکس. یعنی دو resource جدا، هر کدوم با provider درست و ID درست (resource ـه accepter باید به ID خود accepter اشاره کنه نه به requester):

resource "aws_vpc_peering_connection_options" "requester" {
  provider                  = aws.ops
  vpc_peering_connection_id = aws_vpc_peering_connection.ops_to_workload.id
  requester { allow_remote_vpc_dns_resolution = true }
}

resource "aws_vpc_peering_connection_options" "accepter" {
  provider                  = aws.workload
  vpc_peering_connection_id = aws_vpc_peering_connection_accepter.workload_from_ops.id
  accepter  { allow_remote_vpc_dns_resolution = true }
}

با نگاه به عقب، باید روز اول یه ماژول peering-pair می‌ساختم — requester، accepter، هر دو route، هر دو option block ـه DNS، همگی پشت یه مجموعه ورودی. اولی رو inline نوشتم چون «خب فقط یه peering ـه»، برای دومی و سومی copy-paste کردم، و الان سه تا peering جلوتر با همون تکرار نشسته‌م. refactor یعنی رقص state-move ـه Terraform که اولویتش نکردم. کلاسیک.

مقایسه‌ی هزینه با اعداد واقعی

سناریو: ۳ تا VPC ـه workload ↔ ۱ VPC ـه shared services، us-east-1، حدود ۵۰۰ گیگابایت در ماه ترافیک cross-VPC، ۳ تا AZ. همه‌ی قیمت‌ها به صفحات pricing ـه AWS لینک شدن تا وقتی (نه اگر) عوض شدن، بتونی دوباره حساب کنی.

VPC Peering. ۰ دلار/ساعت برای خود connection. data transfer تنها آیتم هزینه‌ست. AWS هزینه‌ی cross-AZ رو دوبار حساب می‌کنه — $0.01/GB روی اکانت فرستنده (out) به‌علاوه‌ی $0.01/GB روی اکانت گیرنده (in)، یعنی مجموعاً $0.02/GB به ازای هر گیگ منتقل‌شده. در حدود ۵۰۰ گیگابایت/ماه روی هر سه‌تا peering: حدود ۱۰ دلار در ماه. اگه سرویس‌ها رو به AZ خاصی pin کنی کمتره، ولی برای صداقت همون بدترین حالت رو نگه می‌دارم. (قیمت VPC)

Transit Gateway. ۴ تا attachment × $0.05/ساعت × ۷۳۰ ساعت = ۱۴۶ دلار/ماه فقط برای attachment ها. به‌علاوه $0.02/GB processed × ۵۰۰ گیگابایت = ۱۰ دلار. حدود ۱۵۶ دلار در ماه، قبل از هر چیز دیگه. همون حدود ۱۰ دلار data transfer ـه cross-AZ روی TGW هم می‌اد — تفاوت اصلی، اون ۱۴۶ دلار attachment fee ـه، نه لایه‌ی داده. (قیمت TGW)

Site-to-Site VPN. $0.05/ساعت برای هر connection × ۷۳۰ ساعت × ۳ تا connection = ۱۰۹.۵ دلار/ماه، به‌علاوه‌ی data transfer out و بار عملیاتی customer gateway. حدود ۱۱۰ دلار در ماه و ابزار غلط برای intra-AWS. (قیمت VPN)

PrivateLink. هزینه‌ی per-endpoint وقتی چندتا سرویس داری سریع بالا می‌ره. ۳ سرویس × ۳ AZ × $0.01/ساعت × ۷۳۰ = ۶۵.۷ دلار/ماه به ازای هر VPC مصرف‌کننده. ۳ تا VPC ـه workload مصرف‌کننده = ۱۹۷ دلار/ماه فقط برای endpoint ها، به‌علاوه $0.01/GB × ۵۰۰ گیگ = ۵ دلار. حدود ۲۰۲ دلار در ماه. (قیمت PrivateLink)

VPC Lattice. $0.025/ساعت برای هر سرویس × ۳ سرویس × ۷۳۰ = ۵۴.۷۵ دلار/ماه، به‌علاوه $0.025/GB × ۵۰۰ گیگ = ۱۲.۵ دلار. حدود ۶۷ دلار در ماه. (قیمت Lattice)

گزینههزینه‌ی ماهانه (این سناریو)
VPC Peeringحدود ۱۰ دلار
VPC Latticeحدود ۶۷ دلار
Site-to-Site VPNحدود ۱۱۰ دلار
Transit Gatewayحدود ۱۵۶ دلار
PrivateLinkحدود ۲۰۲ دلار

نقطه‌ی break-even با TGW. هزینه‌ی data transfer به‌ازای هر گیگ تقریباً یکیه (حدود $0.02/GB). تصمیم کاملاً روی attachment fee در برابر سختی عملیاتی n-1 تا peering (در hub-and-spoke) یا n*(n-1)/2 (در full mesh) می‌چرخه. قاعده‌ی سرانگشتی من: بالای ~۵ تا VPC در hub-and-spoke یا هر سناریوی full-mesh، برو سراغ TGW. پایین‌تر از اون، peering در هزینه با اختلاف یه مرتبه‌ی بزرگی برنده‌ست و فاصله‌ی عملیاتیش می‌شه «دو تا ورودی route table بیشتر».

کجاها نباید سراغ peering بری

  • بیش از ~۵ تا VPC. منحنی n*(n-1)/2 حتی در hub-and-spoke هم مدیریت رو دردناک می‌کنه، اگه قرار باشه یه spoke به spoke دیگه برسه.
  • routing گذرا. peering ها ترانزیت نمی‌کنن. اگه A↔B و B↔C باشه، A بدون A↔C به C نمی‌رسه. TGW این رو ذاتی حل می‌کنه.
  • تغییرات مکرر CIDR. هر تغییر یه رویداد هماهنگی بین اکانت‌هاست.
  • egress یا inspection مرکزی. اگه compliance یه VPC ـه inspection یا NAT مرکزی بخواد، باید سراغ TGW یا الگوی transit VPC بری.
  • cross-region در مقیاس بزرگ. peering بین ریجن‌ها کار می‌کنه، ولی هزینه‌ی داده و بار عملیاتی سریع بالا می‌ره. peering ـه TGW بین ریجن‌ها شکل بهتریه.
  • کنترل دسترسی در لایه‌ی سرویس به جای لایه‌ی شبکه. اون قلمرو PrivateLink یا Lattice ـه.

نکات نهایی

  • پیش‌فرض سراغ TGW رفتن، برای تیم‌های کوچیک یه تصمیم cargo-cult ـه. معماری‌های مرجع AWS برای شرکت‌هایی نوشته شدن که ده‌ها اکانت و تیم شبکه‌ی اختصاصی دارن. تو (احتمالاً) اون‌ها نیستی.
  • هشدار «peering اسکیل نمی‌شه» درسته، ولی بد بیان شده. بالای ~۵ تا VPC در full mesh اسکیل نمی‌شه. تو ۴ تا VPC در hub-and-spoke، می‌شه سه‌تا connection — کسل‌کننده، ارزون، تموم.
  • همیشه هزینه‌ی connectivity رو روی پروفایل ترافیک واقعی خودت حساب کن. نقطه‌ی break-even رو attachment hour ها تعیین می‌کنن نه داده — و پیش‌فرض‌های AWS calculator تو رو سمت TGW می‌برن، حتی وقتی با peering ماهی ۱۴۰ دلار صرفه‌جویی می‌کردی.
  • ماژول peering-pair رو روز اول به‌صورت Terraform ـه قابل استفاده‌ی مجدد بساز. اولی رو inline ننویس چون «فقط یکیه». من دقیقاً همین کارو کردم و هنوز با copy-paste هزینه‌اش رو می‌دم.
  • حالت‌های شکست خاموش، هزینه‌ی واقعی‌اند. peering به‌علاوه‌ی ورودی فراموش‌شده‌ی route table به‌علاوه‌ی فلگ‌های گم‌شده‌ی DNS option، یه بعدازظهر رو می‌خوره. تو ماژول بذارشون و دیگه بهشون فکر نکن.