شبكات Kubernetes والتخزين

نظام DNS في الكلاستر واكتشاف الخدمات

18 دقيقة الدرس 6 من 31

نظام DNS في الكلاستر واكتشاف الخدمات

يمرّ كل حركة المرور الداخلية في كلاستر Kubernetes الإنتاجي عبر نظام فرعي واحد بالغ الأهمية: CoreDNS. حين تستدعي الـ pod الخاصة بك العنوان http://payment-svc:8080، فهي لا تتصل بـ IP ثابت — بل تُحلِّل اسم DNS تترجمه CoreDNS إلى الـ ClusterIP الخاص بالـ Service في اللحظة ذاتها. إتقان هذا المسار من البداية إلى النهاية هو الفارق بين التخمين عند التعامل مع أعطال الشبكة وإصلاحها في أقل من خمس دقائق.

CoreDNS: المحلِّل الموثوق للكلاستر

حلّت CoreDNS محل kube-dns بوصفها إضافة DNS الافتراضية منذ الإصدار 1.13 من Kubernetes. تعمل كـ Deployment (عادةً نسختان لضمان التوافر العالي) في نيمسبيس kube-system، ويُعرِّضها الـ Service المسمى kube-dns عبر ClusterIP ثابت — عادةً 10.96.0.10. يكتب kubelet هذا العنوان في ملف /etc/resolv.conf داخل كل pod.

تُضبَط CoreDNS عبر ConfigMap باسم coredns في kube-system. لغة الإعداد تُسمى Corefile. يبدو Corefile الافتراضي كالتالي:

kubectl -n kube-system get configmap coredns -o yaml # قسم Corefile النموذجي: # .:53 { # errors # health { # lameduck 5s # } # ready # kubernetes cluster.local in-addr.arpa ip6.arpa { # pods insecure # fallthrough in-addr.arpa ip6.arpa # ttl 30 # } # prometheus :9153 # forward . /etc/resolv.conf { # max_concurrent 1000 # } # cache 30 # loop # reload # loadbalance # }

الإضافات الأساسية التي يجب فهمها: kubernetes — تُجيب على استعلامات نطاق الكلاستر؛ forward — ترسل كل ما سواها إلى المحلِّل الأعلى (محلِّل العقدة نفسها)؛ cache — تخزين مؤقت بـ TTL لتجنّب الوصول للـ API Server عند كل استعلام؛ loop — يكتشف حلقات التوجيه وينهار بأمان؛ health/ready — يُعرِّضان نقطتي نهاية للـ liveness والـ readiness.

الأسماء الكاملة (FQDNs)

يتبع نظام DNS في Kubernetes اصطلاح تسمية هرمياً صارماً. تحصل كل Service على إدخال DNS ضمن نطاق الكلاستر المُعدَّل (الافتراضي: cluster.local).

Kubernetes DNS FQDN hierarchy cluster.local. svc.cluster.local. default.svc.cluster.local. payments.svc.cluster.local. nginx.default.svc.cluster.local. api.payments.svc .cluster.local. Short form inside same namespace: "nginx" → nginx.default.svc.cluster.local.
هرمية FQDN في Kubernetes DNS: نطاق الكلاستر ← svc ← النيمسبيس ← اسم الـ Service.

تتيح لك نطاقات البحث المُعرَّفة في /etc/resolv.conf داخل كل pod استخدام الأسماء المختصرة. يمكن لـ pod في نيمسبيس payments الوصول إلى Service باسم api فقط، إذ يجرّب المحلِّل api.payments.svc.cluster.local. أولاً. للوصول عبر النيمسبيسات، استخدم api.payments. النموذج الكامل المنتهي بنقطة يتجاوز توسيع نطاقات البحث دائماً.

أشكال FQDN من الأقصر إلى الأطول:
  • nginx — داخل النيمسبيس نفسه فقط
  • nginx.default — اختصار عبر النيمسبيسات
  • nginx.default.svc — تضمين القطعة svc صراحةً
  • nginx.default.svc.cluster.local — FQDN الكامل
  • nginx.default.svc.cluster.local. — المطلق (النقطة تمنع نطاقات البحث)

الـ Headless Services وسجلات DNS للـ Pods

حين تضبط clusterIP: None، تصبح الـ Service بلا رأس (headless). تُعيد CoreDNS عناوين IP الـ pods فردياً (سجلات A لكل pod) بدلاً من VIP واحد. تستثمر StatefulSets هذا الأمر: تحصل كل pod على اسم DNS ثابت بالشكل <pod-name>.<service-name>.<namespace>.svc.cluster.local. على سبيل المثال، postgres-0.postgres-headless.data.svc.cluster.local يُحلَّل دائماً إلى IP الـ pod الرئيسية — حتى بعد إعادة التشغيل، طالما ظل اسم الـ pod ثابتاً.

تشخيص مشكلات تحليل DNS

تتجلى أعطال DNS في Kubernetes بأشكال خفية: اتصال مرفوض (IP خاطئ)، لا مضيف بهذا الاسم (NXDOMAIN)، أو مهل منتهية (pods CoreDNS غير سليمة). تعتمد سير العمل المعيارية للتشخيص على pod مؤقتة تشغّل dnsutils.

# 1. تشغيل pod للتشخيص kubectl run dnsdbg --image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3 \ --restart=Never --rm -it -- bash # 2. فحص /etc/resolv.conf داخل الـ pod cat /etc/resolv.conf # الناتج المتوقع: # nameserver 10.96.0.10 # search default.svc.cluster.local svc.cluster.local cluster.local # options ndots:5 # 3. حل اسم Service nslookup nginx nslookup nginx.default.svc.cluster.local # 4. مراجعة سجلات CoreDNS kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50 # 5. التحقق من تشغيل pods CoreDNS kubectl -n kube-system get pods -l k8s-app=kube-dns # 6. مراجعة مقاييس CoreDNS kubectl -n kube-system port-forward svc/kube-dns 9153:9153 # ثم: curl http://localhost:9153/metrics | grep coredns_dns_request
خيار ndots:5 فخ أداء إنتاجي. مع ndots:5، أي اسم يحتوي على أقل من 5 نقاط يُطلِق ما يصل إلى 6 استعلامات DNS قبل الوصول للشكل المطلق. للأسماء الخارجية مثل api.stripe.com، يجرّب المحلِّل api.stripe.com.default.svc.cluster.local. أولاً، ثم api.stripe.com.svc.cluster.local. وهكذا. على نطاق واسع، يضاعف هذا معدل استعلامات DNS أو يضاعفه ثلاثاً. الحل: أضف نقطة في النهاية للأسماء الخارجية في إعدادات تطبيقك، أو اضبط dnsConfig.options[ndots: 1] على pods تُجري استدعاءات خارجية فقط.

تخصيص DNS لكل Pod

يمكنك تجاوز سلوك DNS لكل pod باستخدام حقلَي dnsConfig وdnsPolicy. dnsPolicy: ClusterFirst هو الافتراضي (استخدم CoreDNS، ارجع لمحلِّل العقدة عند الفشل). dnsPolicy: None يتيح تحديد محلِّل مخصص بالكامل.

apiVersion: v1 kind: Pod metadata: name: custom-dns-pod spec: dnsPolicy: "None" dnsConfig: nameservers: - 8.8.8.8 # استخدام Google DNS أو المحلِّل الداخلي searches: - payments.svc.cluster.local - svc.cluster.local options: - name: ndots value: "2" # يقلل من استعلامات البحث غير الضرورية - name: timeout value: "2" containers: - name: app image: myapp:latest

ضبط ConfigMap لـ CoreDNS في الإنتاج

على نطاق واسع (500+ عقدة، عشرات الآلاف من الـ pods)، يمكن أن تصبح CoreDNS عنق زجاجة. أفضل الممارسات الإنتاجية:

  • تشغيل نسختين على الأقل، يُفضَّل 3-4، موزّعة عبر عقد مختلفة بـ pod anti-affinity.
  • زيادة TTL للتخزين المؤقت — الافتراضي cache 30 متحفظ. ارفعه إلى cache 120 للـ Services الثابتة لتقليل حركة المرور الصاعدة 4 مرات.
  • Horizontal Pod Autoscaler — اربط CoreDNS بـ HPA يقيس على عدد استعلامات DNS عبر Prometheus adapter.
  • NodeLocal DNSCache — DaemonSet يشغّل cache محلياً على كل عقدة، يعترض استعلامات DNS قبل وصولها لـ Service الـ CoreDNS. يُقلّص زمن الاستجابة من ~2ms إلى ~0.1ms للمُدخلات المخزّنة ويزيل الضغط على جدول conntrack من UDP DNS.
تعديلات ConfigMap لـ CoreDNS تسري بعد دورة إعادة تحميل، وليس فوراً. تستطلع إضافة reload التغييرات كل 30 ثانية افتراضياً. إذا غيّرت Corefile ورأيت سلوكاً غير متوقع، انتظر 30 ثانية أو احذف pods CoreDNS لإجبارها على إعادة التشغيل. تحذير إضافي: تعديل ConfigMap بينما يحتوي على Corefile معطوب سيُعطّل CoreDNS عند إعادة التحميل — تحقق دائماً من الصحة البنيوية بـ corefile-tool validate قبل التطبيق.

اكتشاف الخدمات خارج DNS

يُعدّ DNS الآلية الرئيسية للاكتشاف، لكن Kubernetes يُعرِّض الـ Services أيضاً عبر متغيرات بيئة تُحقن عند إنشاء الـ pod (مثل NGINX_SERVICE_HOST وNGINX_SERVICE_PORT). هذه آلية قديمة: تعكس فقط الـ Services التي كانت موجودة قبل بدء الـ pod، وتنمو قائمة المتغيرات بلا حدود في الكلاسترات الكبيرة. افضل دائماً DNS لاكتشاف الخدمات في الكود الجديد.

على مستوى التطبيق، تُطبّق الأنظمة الإنتاجية خدمة mesh (Istio أو Linkerd) فوق DNS تعترض الاتصالات بعد التحليل وتُطبّق mTLS والإعادة وقطع الدائرة بشفافية. تظل CoreDNS نفسها الجهة الموثوقة للتحليل؛ تلتف الـ mesh على الاتصال فقط بعد التحليل.