دو تا لایه‌ی DNS که وقتی هر دو سبزن، عین همن

aws route53 dns vpc networking troubleshooting

pod های توی VPC یه account مدام واسه اسم‌های یه private hosted zone که مال یه account دیگه بود NXDOMAIN می‌گرفتن. این خطا یه شکل خیلی مشخص داشت: فقط از همون یه VPC غریبه اتفاق می‌افتاد. همون اسم‌ها رو از VPC خونگیِ خود zone می‌پرسیدی، فوری resolve می‌شدن. پس این نه یه zone خراب بود، نه یه record غلط، نه یه تأخیر تو propagation. محدود بود دقیقاً به یه VPC، همون جور جزئیاتی که باید بهم می‌گفت چی خرابه و به‌جاش هیچی بهم نگفت، چون داشتم از لایه‌ی اشتباه می‌خوندمش.

peering connection بین اون دو تا VPC سالم بود. route ها هر دو طرف سر جاشون بودن. security group ها traffic رو راه می‌دادن. و اون option به اسم allow_remote_vpc_dns_resolution روی peering هم تیک خورده بود، یعنی دقیقاً همون تنظیمی که اسمش بیشتر از همه شبیه «بذار این VPC از رو peering، DNS رو resolve کنه» به نظر می‌رسه. هر سیگنالی که بلد بودم نگاه کنم، سبز بود. اسم‌ها بازم resolve نمی‌شدن.

اون دو تا لایه

دلیل اینکه هیچ‌کدوم از اون سیگنال‌های سبز کمک نکرد اینه که DNS تو یه setup که هم peer شده و هم cross-account ئه، روی دو تا مکانیزم مستقل کار می‌کنه، و این دو تا از بیرون عین هم به نظر می‌رسن.

اولی، DNS resolution خود peering connection ئه. اون flag به اسم allow_remote_vpc_dns_resolution فقط یه کار باریک انجام می‌ده: می‌ذاره instance های یه طرف peering، اون اسم‌های private DNSِ provider-assigned مربوط به instance های طرف دیگه رو resolve کنن، همون hostname های ip-10-x-x-x.region.compute.internal که AWS خودکار می‌ده. کل دامنه‌ی کار این flag همینه. درباره‌ی اون DNS داخلیِ پیش‌فرضیه که هر VPC مجانی می‌گیره.

دومی، عضویت تو private hosted zone ئه. یه private hosted zone فقط به query هایی که از VPC های associate‌شده باهاش میان جواب می‌ده. association یه کار جدا و صریحه: یه VPC مشخص رو می‌چسبونی به یه zone مشخص، و تازه اون‌وقته که اون zone تو resolver اون VPC ظاهر می‌شه. یه zone که با VPC تو associate نشده، از دید resolver اون VPC اصلاً وجود نداره، و جواب صادقانه و درستِ یه query واسه یکی از اسم‌هاش، NXDOMAIN ئه.

این دو تا لایه هیچ ربطی به هم ندارن. peering DNS resolution درباره‌ی اون اسم‌های خودکارِ compute.internal ئه؛ عضویت تو zone درباره‌ی zone سفارشیِ خودته. تیک زدنِ اون گزینه‌ی peering، اولی رو عوض می‌کنه و واسه دومی مطلقاً هیچ کاری نمی‌کنه. اون VPC غریبه‌ی من می‌تونست hostname های compute.internal طرف مقابل رو بی‌عیب resolve کنه، دقیقاً همون چیزی که flag قول می‌ده، و این موفقیت هیچی بهم نمی‌گفت درباره‌ی اینکه zone سفارشیِ خودم جواب می‌ده یا نه، چون این اصلاً کار اون flag نبود.

چرا گولت می‌زنه

هر واکنش غریزیِ عیب‌یابی که داشتم، به سمت لایه‌ی peering نشونه می‌رفت. peering active ئه؟ route ها سر جاشونن؟ security group ها راه می‌دن؟ DNS resolution روی connection فعاله؟ این‌ها سؤال‌هایین که آدم درباره‌ی connectivity بین VPC ها می‌پرسه، و تک‌تک جواب‌ها درست برمی‌گشت، چون لایه‌ی peering واقعاً درست بود. مشکل این بود که یه لایه‌ی peeringِ درست، هیچ مدرکی درباره‌ی عضویت تو zone نیست. من همین‌جوری چراغ‌های سبز رو از یه مکانیزم جمع می‌کردم و مثل یه دلگرمی درباره‌ی یه مکانیزم دیگه باهاشون رفتار می‌کردم.

تو کل این سطح، هیچی نمی‌گه «این VPC عضو zone نیست». هیچ چراغ قرمزی واسش نیست. کنسول peering راضیه، route table ها راضی‌ان، security group ها راضی‌ان، و اون association ای که غایبه تو یه resource کاملاً جداگونه زندگی می‌کنه که فقط وقتی پیداش می‌کنی که از قبل بهش شک کرده باشی. خطا تو اون شکاف بین دو تا لایه می‌شینه که هر دو سبز گزارش می‌دن، و شکل این باگ اینه که سبز بودنِ اون لایه‌ای که می‌بینی، قانعت می‌کنه که اون لایه‌ای که نمی‌بینی هم روبراهه.

راه‌حل، و اینکه چرا دو مرحله‌ست

راه‌حل اینه که اون VPC غریبه رو واقعاً عضو zone کنی. چون zone و VPC تو دو تا account مختلف زندگی می‌کنن، این association یه handshake دو مرحله‌ایه، یه call تو هر account:

# تو account صاحب zone: به اون VPC غریبه authorize بده.
aws route53 create-vpc-association-authorization \
  --hosted-zone-id "$ZONE_ID" \
  --vpc VPCRegion="$REGION",VPCId="$FOREIGN_VPC_ID" \
  --profile zone-owner

# تو account صاحب VPC: با associate کردن قبولش کن.
aws route53 associate-vpc-with-hosted-zone \
  --hosted-zone-id "$ZONE_ID" \
  --vpc VPCRegion="$REGION",VPCId="$FOREIGN_VPC_ID" \
  --profile vpc-owner

این تقسیم شدن، تشریفات نیست. authorization تصمیمیه که صاحب zone باید بگیره، پس با credential های صاحب zone اجرا می‌شه؛ association، zone رو می‌چسبونه به یه VPC که مال اون account دیگه‌ست، پس با credential های اون account اجرا می‌شه. هر نصفه همون‌جا اجرا می‌شه که resource ش زندگی می‌کنه. به همین ترتیب اجراشون کنی، resolver اون VPC غریبه فوری شروع می‌کنه به جواب دادن واسه zone. اون record مربوط به authorization بعد از تموم شدنِ association هم باقی می‌مونه، پس هر دو نصفه ماندگارن و بعداً هم هر دو importable ان تو هر چیزی که infrastructure as code ت رو مدیریت می‌کنه.

تله‌ی audit

همین که فهمیدی association همون چیزیه که مهمه، یه سورپرایز دوم منتظرته: هیچ command تکی‌ای نیست که بهت بگه یه VPC می‌تونه یه zone رو resolve کنه یا نه. دو تا هست، و به سؤال‌های مختلفی جواب می‌دن.

# چی واقعاً associate شده (حقیقتِ resolver):
aws route53 get-hosted-zone --id "$ZONE_ID"

# چی فقط authorize شده (یه دعوت‌نامه‌ی معلق):
aws route53 list-vpc-association-authorizations --hosted-zone-id "$ZONE_ID"

get-hosted-zone اون VPC هایی که واقعاً associate شدن رو لیست می‌کنه، یعنی همون مجموعه‌ای که می‌تونه resolve کنه. list-vpc-association-authorizations اون VPC هایی که authorize شدن رو لیست می‌کنه، یعنی فقط اجازه‌ی association، نه خودِ association. این دو تا مرتب با هم اختلاف دارن. یه audit روی یه zone، یه VPC رو رو کرد که authorize شده بود ولی هیچ‌وقت associate نشده بود: از سمت infrastructure as code انگار تموم شده بود، resource مربوط به authorization وجود داشت، و resolve از اون VPC هنوز خراب بود. حالت برعکسش هم پیش میاد: همون zone یه association داشت به یه VPC که دیگه تو هیچ account ای وجود نداشت، یه ته‌مونده از یه محیط جمع‌شده، بی‌خطر ولی راحت میشه با یه peer زنده اشتباهش گرفت. authorize یعنی associate نیست، و associate هم لزوماً یعنی هنوز واقعیه نیست.

درسی که ارزش نگه داشتن داره

اون درس ماندگار درباره‌ی Route53 نیست. اینه: وقتی دو تا لایه یه چراغ سبز مشترک نشون می‌دن، یه سیگنال سالم از یکیشون، مدرکی درباره‌ی اون‌یکی نیست، هر چقدر هم اسمش با اعتماد‌به‌نفس چیز دیگه‌ای رو القا کنه. allow_remote_vpc_dns_resolution جوری به نظر می‌رسه انگار کل DNS رو رو peering کنترل می‌کنه. یه برش باریک رو کنترل می‌کنه، و اون برشی که من واقعاً لازم داشتم، تو یه resource کاملاً دیگه بود.

پس وقتی یه سیگنال سبزه و اون چیز بازم کار نمی‌کنه، سؤال مفید این نیست که «چرا این چیز سبز داره بهم دروغ می‌گه». اون چیز سبز معمولاً درباره‌ی لایه‌ی خودش داره راست می‌گه. سؤال اینه که خطا واقعاً تو کدوم لایه زندگی می‌کنه، و اون سبزِ دلگرم‌کننده‌ای که بهش زل زدی، اون‌جا اصلاً هیچ اقتداری داره یا نه. بیشتر وقت‌ها نداره، و جواب واقعی یه resource اون‌طرف‌تره، تو جایی که هیچ‌وقت نگاه نکردی چون هیچی تو رو به اون سمت هدایت نکرد.

$ cat DNS .md
· 7 دقیقه مطالعه

یه رکورد DNS رو دوبار پاک کردم. بار سوم پرسیدم چرا.

یه endpoint عمومی توی سه روز دو بار قطع شد، و هر دو بار همون fix پنج‌دقیقه‌ای جواب داد: پاک کردن یه رکورد AAAA یتیم. باگ واقعی یه رکورد ownership توی DNS بود که هیچ‌وقت نمی‌تونه خودش رو صاحب بشه، دقیقاً روی apex یه zone دلگیت‌شده.

dns aws route53 kubernetes devops
$ cat KUBERNETES .md
· 8 دقیقه مطالعه

اون EKS 401 که هیچ ربطی به credential نداشت

CI pipeline ما یه EKS token معتبر درست می‌کرد، تمیز ارائه می‌داد، و رد می‌شد. مشکل credential های AWS نبود. access control خود EKS بود، و failure داشت توی RBAC کلاستر، open و clear، قایم می‌شد.

kubernetes eks aws devops platform-engineering troubleshooting