داستانی درباره گم شدن بسته های DNS از پشتیبانی فنی Google Cloud

از ویرایشگر وبلاگ Google: آیا تا به حال به این فکر کرده اید که مهندسان راه حل های فنی Google Cloud (TSE) چگونه درخواست های پشتیبانی شما را مدیریت می کنند؟ مهندسین پشتیبانی فنی TSE مسئول شناسایی و تصحیح منابع گزارش شده توسط کاربر از مشکلات هستند. برخی از این مشکلات بسیار ساده هستند، اما گاهی اوقات با بلیطی مواجه می شوید که نیاز به توجه چند مهندس در آن واحد دارد. در این مقاله، یکی از کارمندان TSE به ما در مورد یک مشکل بسیار مشکل از تمرین اخیر خود به ما می گوید - مورد گم شدن بسته های DNS. در این داستان، خواهیم دید که مهندسان چگونه توانستند وضعیت را حل کنند و چه چیزهای جدیدی در هنگام رفع خطا یاد گرفتند. امیدواریم این داستان نه تنها به شما در مورد یک باگ عمیق آموزش دهد، بلکه به شما بینشی در مورد فرآیندهایی که برای ثبت یک بلیط پشتیبانی با Google Cloud انجام می شود، می دهد.

داستانی درباره گم شدن بسته های DNS از پشتیبانی فنی Google Cloud

عیب یابی هم علم است و هم هنر. همه چیز با ایجاد یک فرضیه در مورد دلیل رفتار غیر استاندارد سیستم شروع می شود و پس از آن از نظر قدرت آزمایش می شود. با این حال، قبل از اینکه یک فرضیه را فرموله کنیم، باید مسئله را به وضوح تعریف و به طور دقیق فرموله کنیم. اگر سوال خیلی مبهم به نظر می رسد، باید همه چیز را با دقت تجزیه و تحلیل کنید. این "هنر" عیب یابی است.

تحت Google Cloud، چنین فرآیندهایی به طور تصاعدی پیچیده تر می شوند، زیرا Google Cloud تمام تلاش خود را می کند تا حریم خصوصی کاربران خود را تضمین کند. به همین دلیل، مهندسان TSE به ویرایش سیستم های شما دسترسی ندارند و همچنین نمی توانند تنظیمات را به طور گسترده ای مانند کاربران مشاهده کنند. بنابراین، برای آزمایش هر یک از فرضیه های خود، ما (مهندسین) نمی توانیم به سرعت سیستم را اصلاح کنیم.

برخی از کاربران بر این باورند که ما همه چیز را مانند مکانیک در یک سرویس خودرو درست می کنیم و به سادگی شناسه یک ماشین مجازی را برای ما ارسال می کنیم، در حالی که در واقعیت این فرآیند در قالب مکالمه انجام می شود: جمع آوری اطلاعات، تشکیل و تایید (یا رد) فرضیه ها، و در نهایت، مشکلات تصمیم گیری مبتنی بر ارتباط با مشتری است.

مشکل مورد نظر

امروز داستانی داریم که پایان خوبی دارد. یکی از دلایل حل موفقیت آمیز پرونده پیشنهادی، شرح بسیار دقیق و دقیق مشکل است. در زیر می توانید یک کپی از اولین بلیط (ویرایش شده برای پنهان کردن اطلاعات محرمانه) را مشاهده کنید:
داستانی درباره گم شدن بسته های DNS از پشتیبانی فنی Google Cloud
این پیام حاوی اطلاعات مفید زیادی برای ما است:

  • VM خاص مشخص شده است
  • خود مشکل نشان داده شده است - DNS کار نمی کند
  • جایی که مشکل خود را نشان می دهد - VM و ظرف نشان داده شده است
  • مراحلی که کاربر برای شناسایی مشکل طی کرده است نشان داده شده است.

این درخواست به عنوان "P1: تاثیر بحرانی - خدمات غیرقابل استفاده در تولید" ثبت شد، که به معنای نظارت مداوم بر وضعیت 24 ساعته و 7 روز هفته مطابق با طرح "پیروی از خورشید" است (شما می توانید در مورد بیشتر بخوانید. اولویت های درخواست های کاربر، با انتقال آن از یک تیم پشتیبانی فنی به تیم دیگر با هر تغییر منطقه زمانی. در واقع، زمانی که این مشکل به تیم ما در زوریخ رسید، قبلاً جهان را در بر گرفته بود. در این زمان، کاربر اقدامات کاهشی را انجام داده بود، اما از تکرار وضعیت در تولید می ترسید، زیرا علت اصلی هنوز کشف نشده بود.

زمانی که بلیط به زوریخ رسید، اطلاعات زیر را در دست داشتیم:

  • محتوا /etc/hosts
  • محتوا /etc/resolv.conf
  • نتیجه iptables-save
  • توسط تیم مونتاژ شده است ngrep فایل pcap

با این داده ها، ما آماده بودیم تا مرحله "تحقیق" و عیب یابی را آغاز کنیم.

اولین قدم های ما

اول از همه، لاگ ها و وضعیت سرور ابرداده را بررسی کردیم و مطمئن شدیم که درست کار می کند. سرور ابرداده به آدرس IP 169.254.169.254 پاسخ می دهد و از جمله مسئولیت کنترل نام دامنه را بر عهده دارد. ما همچنین دوبار بررسی کردیم که فایروال با VM درست کار می کند و بسته ها را مسدود نمی کند.

این یک نوع مشکل عجیب بود: بررسی nmap فرضیه اصلی ما در مورد از دست دادن بسته‌های UDP را رد کرد، بنابراین ما به طور ذهنی چندین گزینه و روش دیگر برای بررسی آنها پیدا کردیم:

  • آیا بسته ها به صورت انتخابی رها می شوند؟ => قوانین iptables را بررسی کنید
  • خیلی کوچک نیست؟ MTU? => خروجی را بررسی کنید ip a show
  • آیا مشکل فقط بسته های UDP یا TCP را نیز تحت تأثیر قرار می دهد؟ => برانید dig +tcp
  • آیا بسته های حفاری تولید شده برمی گردند؟ => برانید tcpdump
  • آیا libdns به درستی کار می کند؟ => برانید strace برای بررسی انتقال بسته ها در هر دو جهت

در اینجا تصمیم داریم با کاربر تماس بگیریم تا مشکلات را به صورت زنده عیب یابی کند.

در طول تماس می توانیم چندین مورد را بررسی کنیم:

  • پس از چندین بررسی، قوانین iptables را از لیست دلایل حذف می کنیم
  • ما رابط های شبکه و جداول مسیریابی را بررسی می کنیم و صحت MTU را دوباره بررسی می کنیم
  • ما آن را کشف می کنیم dig +tcp google.com (TCP) همانطور که باید کار می کند، اما dig google.com (UDP) کار نمی کند
  • رانده شدن tcpdump هنگام کارکردن dig، متوجه می شویم که بسته های UDP در حال بازگرداندن هستند
  • ما دور می شویم strace dig google.com و ما می بینیم که چگونه حفاری به درستی تماس می گیرد sendmsg() и recvms()، اما دومی با تایم اوت قطع می شود

متأسفانه پایان شیفت فرا می رسد و ما مجبور می شویم مشکل را به منطقه زمانی بعدی افزایش دهیم. با این حال، این درخواست در تیم ما علاقه مند شد و یکی از همکاران پیشنهاد می کند بسته DNS اولیه را با استفاده از ماژول اسکرپی پایتون ایجاد کنید.

from scapy.all import *

answer = sr1(IP(dst="169.254.169.254")/UDP(dport=53)/DNS(rd=1,qd=DNSQR(qname="google.com")),verbose=0)
print ("169.254.169.254", answer[DNS].summary())

این قطعه یک بسته DNS ایجاد می کند و درخواست را به سرور ابرداده ارسال می کند.

کاربر کد را اجرا می کند، پاسخ DNS برگردانده می شود و برنامه آن را دریافت می کند و تأیید می کند که در سطح شبکه مشکلی وجود ندارد.

پس از یک "سفر دور دنیا" دیگر، درخواست به تیم ما برمی گردد و من آن را کاملاً به خودم منتقل می کنم و فکر می کنم اگر درخواست از جایی به مکان دیگر چرخش را متوقف کند برای کاربر راحت تر خواهد بود.

در این بین، کاربر با مهربانی با ارائه یک عکس فوری از تصویر سیستم موافقت می کند. این خبر بسیار خوبی است: توانایی تست کردن سیستم خودم عیب یابی را بسیار سریعتر می کند، زیرا دیگر مجبور نیستم از کاربر بخواهم دستورات را اجرا کند، نتایج را برای من ارسال کند و آنها را تجزیه و تحلیل کند، من خودم می توانم همه کارها را انجام دهم!

همکارانم کم کم به من حسادت می کنند. در طول ناهار، ما در مورد تبدیل بحث می کنیم، اما هیچ کس هیچ ایده ای ندارد که چه اتفاقی می افتد. خوشبختانه، خود کاربر قبلاً اقداماتی را برای کاهش عواقب انجام داده است و عجله ای ندارد، بنابراین ما زمان داریم تا مشکل را تشریح کنیم. و از آنجایی که ما یک تصویر داریم، می توانیم هر آزمایشی را که به ما علاقه دارد اجرا کنیم. عالی!

برداشتن یک قدم به عقب

یکی از پرطرفدارترین سوالات مصاحبه برای موقعیت های مهندس سیستم این است: "وقتی پینگ می کنید چه اتفاقی می افتد؟ www.google.com? سوال عالی است، زیرا نامزد باید همه چیز را از پوسته گرفته تا فضای کاربر، هسته سیستم و سپس شبکه را توصیف کند. من لبخند می زنم: گاهی اوقات سؤالات مصاحبه در زندگی واقعی مفید است ...

من تصمیم دارم این سوال HR را برای یک مشکل فعلی اعمال کنم. به طور کلی، هنگامی که می خواهید یک نام DNS را تعیین کنید، موارد زیر رخ می دهد:

  1. برنامه یک کتابخانه سیستمی مانند libdns را فراخوانی می کند
  2. libdns پیکربندی سیستم را بررسی می کند که با کدام سرور DNS باید تماس بگیرد (در نمودار این 169.254.169.254، سرور ابرداده است)
  3. libdns از تماس های سیستمی برای ایجاد یک سوکت UDP (SOKET_DGRAM) و ارسال بسته های UDP با پرس و جوی DNS در هر دو جهت استفاده می کند.
  4. از طریق رابط sysctl می توانید پشته UDP را در سطح هسته پیکربندی کنید
  5. هسته برای انتقال بسته ها از طریق شبکه از طریق رابط شبکه با سخت افزار تعامل دارد
  6. هایپروایزر بسته را پس از تماس با آن به سرور ابرداده می گیرد و به آن ارسال می کند
  7. سرور ابرداده با جادوی خود نام DNS را تعیین می کند و با استفاده از همان روش پاسخی را برمی گرداند

داستانی درباره گم شدن بسته های DNS از پشتیبانی فنی Google Cloud
اجازه دهید به شما یادآوری کنم که چه فرضیه هایی را قبلاً در نظر گرفته ایم:

فرضیه: کتابخانه های خراب

  • تست 1: strace را در سیستم اجرا کنید، بررسی کنید که دیگ فراخوانی صحیح سیستم را می‌خواند
  • نتیجه: تماس های سیستمی صحیح فراخوانی می شود
  • تست 2: استفاده از srapy برای بررسی اینکه آیا می توانیم نام هایی را که کتابخانه های سیستم را دور می زنند تعیین کنیم
  • نتیجه: ما می توانیم
  • تست 3: rpm –V را روی بسته libdns و فایل های کتابخانه md5sum اجرا کنید
  • نتیجه: کد کتابخانه کاملاً با کد موجود در سیستم عامل کار یکسان است
  • تست 4: تصویر سیستم ریشه کاربر را بدون این رفتار روی ماشین مجازی نصب کنید، chroot را اجرا کنید، ببینید آیا DNS کار می کند یا خیر
  • نتیجه: DNS به درستی کار می کند

نتیجه گیری بر اساس آزمایشات: مشکل در کتابخانه ها نیست

فرضیه: در تنظیمات DNS خطایی وجود دارد

  • تست 1: tcpdump را بررسی کنید و ببینید آیا بسته های DNS پس از اجرای دیگ به درستی ارسال و بازگردانده می شوند یا خیر
  • نتیجه: بسته ها به درستی منتقل می شوند
  • تست 2: بررسی مجدد روی سرور /etc/nsswitch.conf и /etc/resolv.conf
  • نتیجه: همه چیز درست است

نتیجه گیری بر اساس آزمایشات: مشکل از پیکربندی DNS نیست

فرضیه: هسته آسیب دیده است

  • تست: نصب کرنل جدید، بررسی امضا، راه اندازی مجدد
  • نتیجه: رفتار مشابه

نتیجه گیری بر اساس آزمایشات: هسته آسیب نمی بیند

فرضیه: رفتار نادرست شبکه کاربر (یا رابط شبکه هایپروایزر)

  • تست 1: تنظیمات فایروال خود را بررسی کنید
  • نتیجه: فایروال بسته های DNS را بر روی میزبان و GCP ارسال می کند
  • تست 2: رهگیری ترافیک و نظارت بر صحت ارسال و بازگشت درخواست های DNS
  • نتیجه: tcpdump تأیید می کند که میزبان بسته های برگشتی را دریافت کرده است

نتیجه گیری بر اساس آزمایشات: مشکل از شبکه نیست

فرضیه: سرور ابرداده کار نمی کند

  • تست 1: گزارش های سرور ابرداده را برای ناهنجاری بررسی کنید
  • نتیجه: هیچ ناهنجاری در لاگ ها وجود ندارد
  • تست 2: دور زدن سرور ابرداده از طریق dig @8.8.8.8
  • نتیجه: وضوح حتی بدون استفاده از سرور ابرداده خراب است

نتیجه گیری بر اساس آزمایشات: مشکل از سرور ابرداده نیست

خط پایین: ما همه زیرسیستم ها را آزمایش کردیم به جز تنظیمات زمان اجرا!

غواصی در تنظیمات زمان اجرا هسته

برای پیکربندی محیط اجرای هسته، می توانید از گزینه های خط فرمان (grub) یا رابط sysctl استفاده کنید. نگاه کردم /etc/sysctl.conf و فقط فکر کنید، من چندین تنظیمات سفارشی را کشف کردم. با احساس اینکه به چیزی چنگ زده ام، تمام تنظیمات غیر شبکه ای یا غیر tcp را کنار گذاشتم و در تنظیمات کوه باقی ماندم. net.core. سپس به جایی رفتم که مجوزهای میزبان در ماشین مجازی وجود داشت و شروع به اعمال تنظیمات یکی یکی و یکی پس از دیگری با ماشین مجازی خراب کردم تا اینکه مقصر را پیدا کردم:

net.core.rmem_default = 2147483647

اینجاست، یک پیکربندی شکستن DNS! سلاح قتل را پیدا کردم. اما چرا این اتفاق می افتد؟ من هنوز به یک انگیزه نیاز داشتم.

اندازه بافر بسته DNS پایه از طریق پیکربندی می شود net.core.rmem_default. یک مقدار معمولی حدود 200 کیلوبایت است، اما اگر سرور شما بسته های DNS زیادی دریافت می کند، ممکن است بخواهید اندازه بافر را افزایش دهید. اگر هنگام ورود بسته جدید بافر پر باشد، برای مثال به این دلیل که برنامه به اندازه کافی سریع آن را پردازش نمی کند، در این صورت بسته ها را از دست خواهید داد. مشتری ما به درستی اندازه بافر را افزایش داد زیرا از از دست دادن داده ها می ترسید، زیرا از برنامه ای برای جمع آوری معیارها از طریق بسته های DNS استفاده می کرد. مقداری که او تنظیم کرد حداکثر ممکن بود: 231-1 (اگر روی 231 تنظیم شود، کرنل "INVALID ARGUMENT" را برمی گرداند).

ناگهان متوجه شدم که چرا nmap و scapy درست کار می کنند: آنها از سوکت های خام استفاده می کردند! سوکت های خام با سوکت های معمولی متفاوت هستند: iptable ها را دور می زنند و بافر نمی شوند!

اما چرا «بافر خیلی بزرگ» مشکل ایجاد می کند؟ به وضوح آنطور که در نظر گرفته شده کار نمی کند.

در این مرحله می‌توانم مشکل را روی چندین هسته و توزیع‌های متعدد بازتولید کنم. مشکل قبلاً در هسته 3.x ظاهر شد و اکنون در هسته 5.x نیز ظاهر شد.

در واقع، هنگام راه اندازی

sysctl -w net.core.rmem_default=$((2**31-1))

DNS از کار افتاد.

من شروع به جستجوی مقادیر کاری از طریق یک الگوریتم جستجوی باینری ساده کردم و متوجه شدم که سیستم با 2147481343 کار می کند، اما این عدد برای من مجموعه ای بی معنی از اعداد بود. من به مشتری پیشنهاد دادم این شماره را امتحان کند، و او پاسخ داد که سیستم با google.com کار می کند، اما همچنان با دامنه های دیگر خطا می دهد، بنابراین به بررسی خود ادامه دادم.

من نصب کرده ام قطره چکانابزاری که باید قبلاً استفاده می شد: دقیقاً نشان می دهد که یک بسته در کجای هسته قرار می گیرد. مقصر عملکرد بود udp_queue_rcv_skb. من منابع هسته را دانلود کردم و چند مورد را اضافه کردم توابع printk برای ردیابی اینکه بسته دقیقاً به کجا ختم می شود. به سرعت شرایط مناسب را پیدا کردم if، و مدتی به آن خیره شدم، زیرا در آن زمان بود که همه چیز در نهایت به یک تصویر کامل تبدیل شد: 231-1، یک عدد بی معنی، یک دامنه غیر فعال... این یک قطعه کد بود در __udp_enqueue_schedule_skb:

if (rmem > (size + sk->sk_rcvbuf))
		goto uncharge_drop;

لطفا توجه داشته باشید:

  • rmem از نوع int است
  • size از نوع u16 (بدون علامت شانزده بیتی) است و اندازه بسته را ذخیره می کند
  • sk->sk_rcybuf از نوع int است و اندازه بافر را ذخیره می کند که طبق تعریف برابر با مقدار in است net.core.rmem_default

وقتی که sk_rcvbuf نزدیک شدن به 231، جمع کردن اندازه بسته ممکن است منجر شود سرریز عدد صحیح. و از آنجایی که یک int است، مقدار آن منفی می شود، بنابراین شرط زمانی درست می شود که باید نادرست باشد (شما می توانید در این مورد بیشتر بخوانید پیوند).

خطا را می توان به روشی کم اهمیت اصلاح کرد: با ریخته گری unsigned int. من اصلاح را اعمال کردم و سیستم را دوباره راه اندازی کردم و DNS دوباره کار کرد.

طعم پیروزی

من یافته های خود را برای مشتری ارسال کردم و ارسال کردم LKML پچ هسته من خوشحالم: هر قطعه از پازل با هم جا می شود، می توانم دقیقاً توضیح دهم که چرا آنچه را مشاهده کردیم مشاهده کردیم و مهمتر از همه، ما به لطف کار گروهی خود توانستیم راه حلی برای مشکل پیدا کنیم!

شایان ذکر است که این مورد نادر بوده است و خوشبختانه ما به ندرت چنین درخواست های پیچیده ای از کاربران دریافت می کنیم.

داستانی درباره گم شدن بسته های DNS از پشتیبانی فنی Google Cloud


منبع: www.habr.com

اضافه کردن نظر