أساسيات الشبكات لـ DevOps

مشروع: تشخيص حادثة انقطاع الاتصال

18 دقيقة الدرس 10 من 30

مشروع: تشخيص حادثة انقطاع الاتصال

كل مفهوم شبكي درسته في هذا الدليل — طبقات TCP/IP، وعنونة IP، وDNS، وHTTP/HTTPS، وTLS، وموازنة التحميل، والجدران النارية، وNAT، والبروكسيات — يتلاقى هنا. الحوادث الحقيقية لا تأتي بتسميات واضحة. إنها تصل كأعراض غامضة: "الـ API معطل"، أو "النشرات مكسورة"، أو "المستخدمون في منطقة معينة لا يستطيعون تسجيل الدخول". يسير هذا المشروع عبر جلسة تصحيح متكاملة ومتدرجة من الإنذار الأول إلى السبب الجذري المؤكد، تماماً كما يفعل مهندس SRE أول في شركة تقنية كبرى.

مبدأ التصحيح المتدرج: ابدأ دائماً من أدنى طبقة OSI لديك فيها دليل على وجود مشكلة، وحلّها، ثم تصعّد. تجاوز الطبقات يُضيّع الوقت ويُخفي الأعطال المتتالية. التسلسل هو: مادي/توجيه ← إمكانية وصول IP ← DNS ← منفذ TCP ← TLS ← HTTP التطبيقي ← منطق الخدمة.

الحادثة

يُطلق نظام مراقبتك إنذاراً في الساعة 14:23 UTC. نص الإنذار: "api.example.com — معدل أخطاء 5xx 34%، كُمون p99 هو 8 ث، ارتفع من الخط الأساسي البالغ 120 مللي ثانية." أنت المهندس المناوب. إليك كيفية التعامل معها منهجياً.

Layered Debugging Flow for a Connectivity Incident Alert / Symptom IP Reachability DNS Resolution TCP / Port TLS / HTTP / App OK? تصعّد OK? تصعّد OK? تصعّد السبب الجذري هنا ping / traceroute dig / nslookup nc / curl -v openssl / logs
سلّم التصحيح المتدرج: ابدأ من إمكانية الوصول IP وتسلّق حتى تجد نقطة الكسر.

الخطوة الأولى — تحديد النطاق (الدقيقتان الأوليتان)

قبل تشغيل أي أمر، أجب على ثلاثة أسئلة: هل تتأثر جميع المستخدمين أم مجموعة فرعية؟ جميع المناطق أم منطقة واحدة؟ جميع نقاط النهاية أم مسار واحد؟ هذا التحديد للنطاق يحدد فرضيتك الأولى.

# تحقق من لوحات المراقبة — كيف يبدو توزيع الأخطاء؟ # استعلام Prometheus نموذجي لرؤية معدل الخطأ لكل Pod أو upstream: # rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) # تحقق سريع من إمكانية الوصول للموقع من خارج شبكتك curl -o /dev/null -s -w "HTTP %{http_code} time_total=%{time_total}s time_connect=%{time_connect}s\n" \ https://api.example.com/health # هل المشكلة في DNS؟ أجرِ الدقة من خادم أسماء خارجي dig @8.8.8.8 api.example.com +short dig @1.1.1.1 api.example.com +short # قارن بما يُعيده المحلّل الداخلي لديك dig api.example.com +short

إذا أعاد DNS الخارجي عنوان IP مختلفاً عما هو متوقع، فلديك مشكلة في طبقة DNS — سجل خاطئ، أو TTL نشر قيمة قديمة، أو اختطاف. إذا اتفق كلا المحللين وأعادا IP الصحيح، فـ DNS خُلّص منه؛ انتقل إلى TCP.

الخطوة الثانية — إمكانية الوصول عبر IP والتوجيه

هل تستطيع الحزم الوصول إلى الوجهة أصلاً؟ تغيير قاعدة جدار ناري، أو تحديث جدول توجيه، أو تعديل مجموعة أمان في السحابة قد يُخفي حركة المرور بصمت.

# إمكانية الوصول ICMP الأساسية (ملاحظة: بعض المضيفين يحجبون ICMP — غياب الاستجابة ليس قاطعاً) ping -c 4 api.example.com # تتبع المسار — أين تتوقف الحزم؟ traceroute -n api.example.com # Linux tracert api.example.com # Windows mtr --report --report-cycles 10 api.example.com # أفضل: يجمع ping + traceroute # تحقق من فتح منفذ TCP على مستوى الشبكة (يتجاوز طبقة التطبيق) nc -zv api.example.com 443 # -z: وضع المسح، -v: مفصّل # أو timeout 5 bash -c "echo > /dev/tcp/api.example.com/443" && echo "open" || echo "closed" # من داخل مجموعة Kubernetes — تحقق من قدرة Pod على الوصول لـ upstream kubectl exec -it <pod-name> -- nc -zv upstream-svc 8080 kubectl exec -it <pod-name> -- curl -sf http://upstream-svc:8080/health
استخدم mtr للمشكلات المتقطعة في التوجيه. traceroute الواحد يُظهر مساراً واحداً في لحظة واحدة. mtr --report-cycles 60 يعمل لدقيقة كاملة ويُظهر نسب فقدان الحزم لكل قفزة، مما يكشف المسارات المتذبذبة والروابط ذات الفقد العالي التي لن يرصدها تتبع واحد.

الخطوة الثالثة — التعمق في DNS

أعطال DNS تظهر بطرق خفية: أخطاء متقطعة (تسميم جزئي للذاكرة المؤقتة)، استجابات بطيئة (过载 المحلّل)، أو عناوين IP قديمة بعد failover لم يتشر بسبب TTL طويل.

# تحقق من TTL الحالي — هل هو منخفض بما يكفي للـ failover السريع؟ dig api.example.com +ttl | grep -E "^api|IN" # تتبع سلسلة تفويض DNS الكاملة من الجذر dig +trace api.example.com # تحقق من فشل التحقق من DNSSEC dig api.example.com +dnssec +cd # +cd يعطّل التحقق؛ قارن مع بدون +cd # استعلام كل خادم أسماء مباشرةً — هل جميع الخوادم السلطوية متوافقة؟ for ns in $(dig NS api.example.com +short); do echo "=== $ns ===" dig @$ns api.example.com +short done # ابحث عن التخزين المؤقت السلبي (NXDOMAIN) على محلّل وسيط dig api.example.com @your-internal-resolver +norecurse

نمط فشل شائع: أجريت failover لعنوان IP موازن التحميل لكن TTL الـ DNS كان 3600 ث (ساعة واحدة). العناوين القديمة مخزنة في محلّلات DNS حول العالم. يجب أن تكون TTL الإنتاج للسجلات الحرجة 60–300 ث في العمليات العادية؛ اخفضها إلى 30 ث قبل الـ failover المخطط، لا خلال الحادثة.

الخطوة الرابعة — فحص TLS والشهادات

إذا اتصل TCP لكن HTTPS فشل، فـ TLS هو المشتبه به. انتهاء صلاحية الشهادة، والوسطاء الناقصون، وعدم تطابق اسم المضيف — هذه الأسباب الثلاثة الأولى.

# سطر واحد: تاريخ الانتهاء + SANs + عمق السلسلة من مضيف حي echo | openssl s_client -servername api.example.com \ -connect api.example.com:443 2>/dev/null \ | openssl x509 -noout -dates -ext subjectAltName # هل يتم تقديم السلسلة الكاملة؟ عدّ كتل PEM (يجب أن تكون 2 أو 3) openssl s_client -connect api.example.com:443 -showcerts 2>/dev/null \ | grep -c "BEGIN CERTIFICATE" # ما إصدار TLS ومجموعة التشفير المتفق عليها؟ curl -vI https://api.example.com 2>&1 | grep -E "TLS|SSL|cipher" # إجبار TLS 1.2 لاختبار التوافق curl --tlsv1.2 --tls-max 1.2 -I https://api.example.com # من داخل المجموعة — هل تُثق شهادة CA الداخلية؟ curl --cacert /etc/ssl/certs/internal-ca.crt https://api.internal:8443/health

الخطوة الخامسة — فحص طبقة HTTP

TCP متاح، مصافحات TLS تنجح، لكن التطبيق يُعيد 5xx. الآن أنت في طبقة التطبيق. تحقق من سجلات وصول موازن التحميل، وصحة الـ upstream، ورؤوس الطلبات.

# curl المفصّل: يُظهر رؤوس الطلب المُرسلة والاستجابة المستلمة curl -vvI https://api.example.com/health 2>&1 # أرسل طلباً واقعياً — ضمّن رؤوس المصادقة التي يرسلها العملاء في الإنتاج curl -s -w "\n%{http_code} %{time_total}s" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" \ https://api.example.com/v1/users | tail -1 # تحقق من أوقات استجابة الـ upstream في سجل الوصول بـ nginx tail -200 /var/log/nginx/access.log | awk '{print $NF}' | sort -n | tail -20 # لخدمة موزونة التحميل — أي خلفية تُعيد الأخطاء؟ grep " 502 " /var/log/nginx/access.log | awk '{print $8}' | sort | uniq -c | sort -rn # تحقق من سجلات التطبيق مباشرة journalctl -u myapp --since "10 minutes ago" --no-pager | grep -i "error\|exception\|timeout" kubectl logs -l app=api --since=10m --prefix | grep -i "error\|5[0-9][0-9]"

الخطوة السادسة — ربط الأحداث بالتغييرات الأخيرة

في غياب سبب واضح في أي طبقة، السؤال الأقوى هو: ما الذي تغيّر؟ الغالبية العظمى من حوادث الإنتاج تسببها نشرة حديثة، أو دفع إعداد، أو تدوير شهادة، أو تحديث اعتماد، أو تغيير بنية تحتية — لا عطل عشوائي في الأجهزة.

# تحقق من النشرات الأخيرة في Kubernetes kubectl rollout history deployment/api # ما الذي تغيّر في آخر 30 دقيقة؟ (نشرات قائمة على git) git log --oneline --since="30 minutes ago" # هل تغيّرت قاعدة جدار ناري أو مجموعة أمان؟ (مثال AWS) aws ec2 describe-security-groups --group-ids sg-12345 \ --query "SecurityGroups[*].IpPermissions" # تحقق من إعادات تشغيل خدمة systemd الأخيرة systemctl status myapp --no-pager journalctl -u myapp -n 50 --no-pager | grep "Started\|Stopped\|Failed" # فحص تدوير الشهادات — متى تغيّرت الشهادة؟ echo | openssl s_client -connect api.example.com:443 2>/dev/null \ | openssl x509 -noout -startdate
التراجع غالباً هو أسرع حل، لا إصلاح السبب الجذري. إذا كانت النشرة تترافق مع وقت بدء الحادثة، تراجع فوراً لاستعادة الخدمة — ثم حقق في السبب الجذري بتأنٍّ. قضاء 20 دقيقة في تصحيح نشرة مكسورة بينما المستخدمون يتأثرون هو المقايضة الخاطئة. التراجع أولاً؛ post-mortem ثانياً.

الجمع بينها — تشريح نتيجة حقيقية

في سيناريو هذا المشروع، يكشف التحقيق: dig @8.8.8.8 api.example.com يُعيد IP موازن التحميل الصحيح. يتصل TCP على المنفذ 443 في 2 مللي ثانية. مصافحة TLS تنجح. لكن grep " 502 " /var/log/nginx/access.log يُظهر أن 100% من أخطاء 502 تشير إلى عنوان IP خلفية واحد — 10.0.1.45. هذا الـ Pod أُعيد تشغيله خلال نشرة تدريجية منذ 12 دقيقة. نقطة نهاية فحص صحته لم تكن تُعيد 200 بعد (JVM كان لا يزال في مرحلة الإحماء)، لكن موازن التحميل أضافه مجدداً للمجموعة بعد 5 ث فقط — قبل أن يكون التطبيق جاهزاً فعلاً. الإصلاح: زيادة initialDelaySeconds في مسبار الجاهزية Kubernetes من 5 إلى 30 ثانية. النشر، ومشاهدة معدل الخطأ ينخفض إلى الصفر خلال 90 ثانية من جاهزية الـ Pod الجديد.

# إصلاح مسبار الجاهزية في مانيفست Deployment apiVersion: apps/v1 kind: Deployment metadata: name: api spec: template: spec: containers: - name: api readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 # كان 5 -- غير كافٍ لإحماء JVM periodSeconds: 10 failureThreshold: 3 livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 60 periodSeconds: 15
وثّق نتائجك في تذكرة الحادثة قبل إغلاقها. اكتب: الإنذار الذي أُطلق، والأوامر التي نفّذتها ومخرجاتها، والطبقة التي وجدت فيها الكسر، والسبب الجذري، والحل المؤقت المطبّق، والإصلاح الدائم. يُصبح هذا مُدخل post-mortem ويدرّب المهندس المناوب التالي. على مستوى Google، كل حادثة P1/P2 لها post-mortem مكتوب يُنشر للفريق خلال 48 ساعة — لا لوم، فقط حقائق وبنود إجراءات.

أدوات التصحيح في لمحة

ابنِ ذاكرة عضلية لهذه الأدوات — إنها مستجيبوك الأوائل في كل طبقة:

  • تحديد النطاق: curl -w "%{http_code}"، لوحات مراقبة الجاهزية، رسوم بيانية معدل الخطأ
  • IP / التوجيه: ping، mtr، traceroute، ip route get
  • DNS: dig +trace، dig @resolver، فحص TTLs، استعلام كل NS سلطوي
  • TCP / المنافذ: nc -zv، ss -tlnp، telnet host port
  • TLS / الشهادات: openssl s_client -showcerts، openssl x509 -dates -ext subjectAltName، curl -vI
  • HTTP / التطبيق: curl -vv، سجلات وصول nginx/التطبيق، رموز خطأ upstream، تتبع على مستوى الطلب
  • ربط التغييرات: تاريخ النشر، git log، سجلات التدقيق السحابية، طوابع وقت تدوير الشهادات

التصحيح المتدرج المنهجي هو المهارة التي تُفرّق بين المهندسين الأقدم والمبتدئين. المبتدئون يخمّنون؛ الأقدمون يقيسون. الهدف هو امتلاك دليل في كل طبقة قبل الانتقال للطبقة التالية — بحيث عندما تجد الكسر، تستطيع إثباته، لا مجرد الاشتباه به.