قصة دعم سحابة DNS مفقودة من Google Cloud

من محرر مدونة Google: هل تساءلت يومًا كيف يتعامل مهندسو Google Cloud Technical Solutions (TSE) مع مكالمات الدعم الفني؟ تتمثل مسؤولية مهندسي الدعم الفني في TSE في الكشف عن مصادر المشكلات التي حددها المستخدمون وحلها. بعض هذه المشاكل بسيطة للغاية ، ولكن في بعض الأحيان تصادف نداء يتطلب اهتمام العديد من المهندسين مرة واحدة. في هذه المقالة ، سيخبرنا أحد موظفي TSE عن مشكلة صعبة للغاية من ممارسته الأخيرة - حالة حزم DNS المفقودة. في سياق هذه القصة ، سنرى كيف تمكن المهندسون من حل الموقف ، وما هي الأشياء الجديدة التي تعلموها في سياق إزالة الخطأ. نأمل ألا تخبرك هذه القصة فقط عن خطأ متجذر ، بل توفر أيضًا فهمًا للعمليات التي تحدث عند إرسال طلب لدعم Google Cloud.



استكشاف الأخطاء وإصلاحها هو كل من العلم والفن. يبدأ كل شيء ببناء فرضية حول سبب السلوك غير القياسي للنظام ، وبعد ذلك يتم اختباره للقوة. ومع ذلك ، قبل صياغة الفرضية ، يجب علينا تحديد المشكلة بوضوح وصياغتها بدقة. إذا كان السؤال يبدو غامضًا للغاية ، فعليك تحليل كل شيء بشكل صحيح ؛ هذا هو "فن" استكشاف الأخطاء وإصلاحها.

في سياق Google Cloud ، تكون هذه العمليات معقدة في بعض الأحيان ، حيث إن Google Cloud تكافح لضمان خصوصية مستخدميها. وبسبب هذا ، لا يتمتع مهندسو TSE بحق الوصول لتعديل أنظمتك ، ولا القدرة على عرض التكوينات على نطاق واسع كما يفعل المستخدمون. لذلك ، لاختبار أي من فرضياتنا ، لا يمكننا (المهندسين) تعديل النظام بسرعة.

يعتقد بعض المستخدمين أننا سنصلح كل شيء كما لو أن الميكانيكا في خدمة السيارة ، ونرسل إلينا ببساطة معرّف الآلة الافتراضية ، بينما في الواقع تستمر العملية في شكل محادثة: جمع المعلومات ، وإنشاء وتأكيد (أو دحض) الفرضيات ، وفي النهاية ، حل يتم بناء المشاكل على التواصل مع العميل.

القضية قيد النظر


اليوم لدينا قصة ذات نهاية جيدة. أحد أسباب الحل الناجح للحالة المقترحة هو وصف تفصيلي ودقيق للغاية للمشكلة. أدناه يمكنك رؤية نسخة من التذكرة الأولى (تم تحريرها من أجل إخفاء المعلومات السرية):

تحتوي هذه الرسالة على الكثير من المعلومات المفيدة لنا:
  • VM المحدد
  • يشار إلى المشكلة - لا يعمل DNS
  • يشار إلى حيث تتجلى المشكلة - VM والحاوية
  • يشار إلى الخطوات التي اتخذها المستخدم لتحديد المشكلة.

تم تسجيل الالتماس على أنه "P1: الأثر الحرج - الخدمة غير قابلة للاستخدام في الإنتاج" ، مما يعني المراقبة المستمرة للحالة على مدار الساعة طوال أيام الأسبوع وفقًا لنظام "Follow the Sun" (يمكن قراءة الرابط بمزيد من التفاصيل حول أولويات مكالمات المستخدم ) ، مع نقله من فريق دعم فني واحد إلى الآخر في كل تحول للمنطقة الزمنية. في الواقع ، عندما وصلت المشكلة إلى فريقنا في زيورخ ، تمكنت من الإبحار حول العالم. بحلول هذا الوقت ، اتخذ المستخدم تدابير لتقليل العواقب ، ومع ذلك ، كان يخشى تكرار الموقف على الإنتاج ، حيث لم يتم العثور على السبب الرئيسي.

في الوقت الذي وصلت فيه التذكرة إلى زيورخ ، كانت لدينا بالفعل المعلومات التالية في متناول اليد:
  • المحتوى /etc/hosts
  • المحتوى /etc/resolv.conf
  • استنتاج iptables-save
  • ngrepملف pcap الذي تم تجميعه بواسطة الأمر

بهذه البيانات ، كنا على استعداد لبدء مرحلة "التحقيق" واستكشاف الأخطاء وإصلاحها.

خطواتنا الأولى


بادئ ذي بدء ، قمنا بفحص السجلات وحالة خادم البيانات الوصفية والتأكد من أنه يعمل بشكل صحيح. يستجيب خادم البيانات الوصفية بعنوان IP 169.254.169.254 وهو مسؤول ، من بين أمور أخرى ، عن التحكم في أسماء النطاقات. لقد تحققنا أيضًا من أن جدار الحماية يعمل بشكل صحيح مع VM ولا يمنع الحزم.

كانت هناك مشكلة غريبة: دحض اختبار nmap فرضيتنا الرئيسية حول فقدان حزم UDP ، لذلك استنتجنا عقليًا عدة خيارات وطرق أخرى للتحقق منها:
  • هل تختفي الحزم بشكل انتقائي؟ => تحقق من قواعد iptables
  • هل وحدة الإرسال الكبرى صغيرة جدًا ؟ => التحقق من الإخراج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 المصدر باستخدام خردة وحدة Python.
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 ، ويتلقى التطبيق ذلك ، مما يؤكد عدم وجود مشكلة على مستوى الشبكة.

بعد "الجولة حول العالم" التالية ، يعود الالتماس إلى فريقنا ، وأنا أقوم بترجمته بالكامل إلى نفسي ، معتقدًا أنه سيكون أكثر ملاءمة للمستخدم إذا توقف الالتماس حوله من مكان إلى آخر.

في غضون ذلك ، يوافق المستخدم بلطف على تقديم لقطة لصورة النظام. هذه أخبار جيدة جدًا: إن القدرة على اختبار النظام بنفسك تسريع عملية استكشاف الأخطاء وإصلاحها بشكل ملحوظ ، لأنك لم تعد بحاجة إلى مطالبة المستخدم بتشغيل الأوامر ، وإرسال النتائج إليّ وتحليلها ، يمكنني القيام بكل شيء بنفسي!

بدأ الزملاء يحسدونني قليلاً. في الغداء ، نناقش النداء ، ولكن لا أحد لديه فكرة عما يحدث. لحسن الحظ ، فإن المستخدم نفسه قد اتخذ بالفعل إجراءات للتخفيف وليس في عجلة من أمره ، لذلك لدينا الوقت لإعداد المشكلة. وبما أننا نمتلك صورة ، يمكننا إجراء أي اختبارات تهمنا. غرامة!

الرجوع خطوة إلى الوراء


أحد الأسئلة الأكثر شيوعًا في مقابلة لمهندس نظم هو: "ماذا يحدث عندما تقوم ping www.google.com ؟" السؤال هو أنيق ، لأن المرشح يحتاج إلى وصف من القشرة إلى مساحة المستخدم ، إلى قلب النظام وكذلك إلى الشبكة. أبتسم: أحيانًا تكون أسئلة المقابلة مفيدة أيضًا في الحياة الواقعية ...

أقرر تطبيق سؤال إيشار على المشكلة الحالية. تحدث تقريبًا ، عند محاولة تحديد اسم DNS ، يحدث ما يلي:
  1. يستدعي التطبيق مكتبة النظام ، على سبيل المثال libdns
  2. يفحص libdns تكوين النظام الذي يجب أن يستخدمه خادم DNS (في الرسم البياني هو 169.254.169.254 ، خادم البيانات الوصفية)
  3. يستخدم libdns مكالمات النظام لإنشاء مقبس UDP (SOKET_DGRAM) وإرسال حزم UDP مع طلب DNS في كلا الاتجاهين
  4. باستخدام واجهة sysctl ، يمكنك تكوين مكدس UDP على مستوى kernel
  5. تتفاعل النواة مع الجهاز لإرسال الحزم عبر الشبكة من خلال واجهة الشبكة
  6. يلتقط مراقب المراقبة ويمرر الحزمة إلى خادم البيانات الوصفية عندما يتلامس معها
  7. يحدد خادم البيانات الوصفية اسم DNS من خلال السحر الخاص به ويعيد الاستجابة نفسها


دعني أذكرك بالفرضيات التي نجحنا في أخذها في الاعتبار:

الفرضية: المكتبات المكسورة
  • اختبار 1: تشغيل سريع في النظام ، تحقق من أن حفر يسبب مكالمات النظام الصحيحة
  • النتيجة: استدعاء مكالمات النظام الصحيحة
  • الاختبار 2: من خلال srapy للتحقق مما إذا كان بإمكاننا تحديد الأسماء التي تتجاوز مكتبات النظام
  • النتيجة: نستطيع
  • الاختبار 3: تشغيل rpm –V على حزمة libdns وملفات مكتبة md5sum
  • النتيجة: رمز المكتبة مطابق تمامًا للرمز الموجود في نظام التشغيل العامل
  • اختبار 4: تحميل صورة نظام الجذر للمستخدم على الجهاز الظاهري بدون هذا السلوك ، تشغيل chroot ، معرفة ما إذا كان DNS يعمل
  • النتيجة: يعمل DNS بشكل صحيح

الخلاصة على أساس الاختبارات: المشكلة ليست في المكتبات

الفرضية: يوجد خطأ في إعدادات DNS
  • اختبار 1: تحقق من tcpdump ومعرفة ما إذا تم إرسال حزم DNS وإعادتها بشكل صحيح بعد تشغيل حفر
  • النتيجة: إرسال الحزم بشكل صحيح
  • اختبار 2: إعادة فحص على الخادم /etc/nsswitch.confو/etc/resolv.conf
  • النتيجة: كل شيء صحيح

الاستنتاج القائم على الاختبار: المشكلة ليست في فرضية تكوين DNS

: تلف Kernel
  • اختبار: تثبيت نواة جديدة ، والتحقق من التوقيع ، وإعادة التشغيل
  • النتيجة: سلوك مماثل

الخلاصة بناءً على الاختبارات: النواة غير تالفة

الفرضية: السلوك غير الصحيح لشبكة المستخدم (أو واجهة الشبكة الخاصة ببرنامج Hypervisor)
  • اختبار 1: تحقق من إعدادات جدار الحماية
  • النتيجة: يقوم جدار الحماية بتمرير حزم DNS على كل من المضيف و GCP
  • اختبار 2: اعتراض حركة المرور وتتبع صحة نقل وإرجاع استعلامات DNS
  • النتيجة: يقر tcpdump باستلام حزم الإرجاع من قبل المضيف

الاستنتاج القائم على الاختبار: المشكلة ليست في شبكة

الفرضية: خادم بيانات التعريف لا يعمل
  • الاختبار 1: التحقق من سجلات خادم البيانات الوصفية بحثًا عن الحالات الشاذة
  • النتيجة: لا يوجد شذوذ في السجلات
  • الاختبار 2: تجاوز خادم بيانات التعريف من خلال dig @8.8.8.8
  • النتيجة: تم انتهاك الإذن حتى بدون استخدام خادم بيانات التعريف

الاستنتاج القائم على الاختبار: المشكلة ليست في

الخلاصة النهائية لخادم البيانات الوصفية : لقد اختبرنا جميع الأنظمة الفرعية باستثناء إعدادات وقت التشغيل!

الغوص في إعدادات وقت تشغيل النواة


لتهيئة وقت تشغيل kernel ، يمكنك استخدام خيارات سطر الأوامر (grub) أو واجهة sysctl. بحثت /etc/sysctl.confوفكرت فقط ، وجدت بعض الإعدادات المخصصة. أشعر وكأنني قد أمسكت بشيء ما ، لقد رفضت جميع الإعدادات غير المتصلة بالشبكة أو غير TCP ، المتبقية من إعدادات الجبل net.core. ثم انتقلت إلى المكان الذي يمتلك فيه VM أذونات المضيف وبدأت في تطبيق واحد تلو الآخر ، واحدًا تلو الآخر مع إعدادات VM مكسورة ، حتى وصلت إلى المجرم:
net.core.rmem_default = 2147483647

ها هو ، تكوين كسر DNS! لقد وجدت أداة للجريمة. ولكن لماذا يحدث هذا؟ ما زلت بحاجة إلى دافع.

يحدث تعيين الحجم الأساسي للمخزن المؤقت لحزمة DNS من خلال net.core.rmem_default. تختلف القيمة النموذجية في مكان ما ضمن 200 كيلوبايت ، ومع ذلك ، إذا كان الخادم الخاص بك يتلقى الكثير من حزم DNS ، يمكنك زيادة حجم المخزن المؤقت. إذا كان المخزن المؤقت ممتلئًا في الوقت الحالي ، تصل حزمة جديدة ، على سبيل المثال ، لأن التطبيق لا يعالجها بالسرعة الكافية ، فستبدأ في فقدان الحزم. قام عميلنا بزيادة حجم المخزن المؤقت بشكل صحيح لأنه كان يخشى فقدان البيانات ، لأنه استخدم التطبيق لجمع المقاييس من خلال حزم DNS. القيمة التي حددها هي الحد الأقصى الممكن: 2 31 -1 (إذا قمت بتعيين 2 31 ، فسوف تُرجع النواة "INVALID ARGUMENT").

فجأة ، أدركت سبب عمل nmap و scapy بشكل صحيح: لقد استخدموا مآخذ غير مطلية! تختلف مآخذ التوصيل الأولية عن مآخذ التوصيل العادية: فهي تعمل على تجاوز أجهزة iptables ، ولا يتم تخزينها مؤقتًا!

لكن لماذا "المخزن المؤقت الكبير للغاية" يسبب مشاكل؟ من الواضح أنه لا يعمل على النحو المنشود.

عند هذه النقطة ، يمكنني إعادة إنتاج المشكلة على نوى متعددة وتوزيعات متعددة. وقد تجلت المشكلة بالفعل في نواة 3.x وتتجلى الآن أيضًا في نواة 5.x.

في الواقع ، عند بدء التشغيل
sysctl -w net.core.rmem_default=$((2**31-1))

توقف DNS عن العمل.

بدأت البحث عن قيم العمل من خلال خوارزمية بحث ثنائية بسيطة ووجدت أن النظام يعمل مع 2147481343 ، ولكن هذا الرقم كان عبارة عن مجموعة لا معنى لها من الأرقام. دعوت العميل لتجربة هذا الرقم ، فأجاب بأن النظام يعمل مع google.com ، ولكنه لا يزال يخطئ في المجالات الأخرى ، لذلك واصلت تحقيقي.

لقد قمت بتثبيت Dropwatch ، وهي أداة كان يجب أن أستخدمها من قبل: فهي توضح مكان وصول الحزمة بالضبط إلى النواة. كانت الوظيفة مذنبة udp_queue_rcv_skb. لقد قمت بتنزيل مصادر kernel وأضفت عدة وظائف printk لتتبع المكان الذي تحصل فيه الحزمة على وجه التحديد. لقد وجدت بسرعة الشرط الصحيحif، و لفترة من الوقت كان يحدق به ببساطة ، لأنه في ذلك الوقت اجتمع كل شيء أخيرًا في صورة كاملة: 2 31 -1 ، رقم لا معنى له ، مجال خامل ... كان جزءًا من التعليمات البرمجية في __udp_enqueue_schedule_skb:
if (rmem > (size + sk->sk_rcvbuf))
		goto uncharge_drop;

ملحوظة:
  • rmem لديه نوع int
  • size هو من نوع u16 (غير موقعة ستة عشر بت) ويخزن حجم الحزمة
  • sk->sk_rcybuf هو من نوع int ويخزن حجم المخزن المؤقت ، الذي يساوي القيمة في التعريف net.core.rmem_default

عند sk_rcvbufالاقتراب من 2 31 ، يمكن أن يؤدي جمع حجم الحزمة إلى تجاوز عدد صحيح . ولأنه int ، تصبح قيمته سالبة ، لذا يصبح الشرط صحيحًا عندما يجب أن يكون كاذبًا (يمكن العثور على المزيد حول هذا عن طريق المرجع ).

يتم تصحيح الخطأ بطريقة تافهة: عن طريق الإرسال إلى unsigned int. قمت بتطبيق التصحيح وأعدت تشغيل النظام ، وبعد ذلك بدأ DNS في العمل مرة أخرى.

طعم النصر


قمت بإعادة توجيه النتائج التي توصلت إليها إلى العميل وأرسلت تصحيح LKML kernel. أنا راضٍ: كل جزء من اللغز اجتمع معًا في وحدة واحدة ، يمكنني أن أشرح بدقة لماذا لاحظنا ما لاحظناه ، والأهم من ذلك ، تمكنا من إيجاد حل للمشكلة من خلال العمل معًا!

تجدر الإشارة إلى أن الحالة اتضح أنها نادرة ، ولحسن الحظ ، نادرًا ما يتم تلقي مثل هذه المكالمات المعقدة من المستخدمين منا.





All Articles