حل أخطاء المنفذ 10250 في kubelet على Ubuntu
تعرّف على سبب خطأ Address already in use عند تنفيذ kubeadm init، وكيف تمنع الجدار الناري من تعطيل kubectl logs وkubectl exec عبر المنفذ 10250.
ما هو المنفذ 10250
المنفذ 10250 هو واجهة kubelet البرمجية، وكل خطأ يذكره يشير إلى إحدى مشكلتين متعاكستين. إما أن هناك عملية أخرى تستخدم المنفذ، لذلك يرفض kubeadm init التشغيل. أو لا يستطيع أي مكوّن الوصول إلى المنفذ، لذلك يفشل kubectl logs وkubectl exec عند العمل مع عقدة تبدو سليمة تماماً.
kubelet هو الوكيل الذي يشغّله Kubernetes على كل عقدة. يبدأ الحاويات ويرسل حالتها إلى مستوى التحكم. كما يستمع على TCP 10250 ويقدّم واجهة HTTPS تستدعيها مكوّنات مستوى التحكم. يفتح خادم API اتصالاً بهذا المنفذ عند تشغيل kubectl logs أو kubectl exec أو kubectl attach أو kubectl port-forward. ويجمع metrics-server المقاييس من /metrics/resource على المنفذ نفسه، وهذا ما يجعل kubectl top node يعمل.
تتطلب واجهة API هذه المصادقة. يعطّل kubeadm الوصول المجهول ويوجّه kubelet إلى CA العنقود (المرجع المصدّق للشهادات)، لذلك يُجاب عن الطلب الذي لا يتضمن بيانات اعتماد بـ Unauthorized بدلاً من منحك shell داخل إحدى حاوياتك. تذكّر هذه التفاصيل، لأنها أيضاً أسرع طريقة لإثبات إمكانية الوصول إلى المنفذ. إذا كانت المنافذ جديدة عليك عموماً، فتشرح ما هو المنفذ فعلياً في Linux النموذج الذي يفترضه هذا الدليل.
ينتج كلا نمطي الفشل عن متطلب واحد. يجب أن يكون المنفذ 10250 خالياً قبل بدء kubelet، وأن يكون قابلاً للوصول من مستوى التحكم بعد تشغيله.
أيّ المشكلتين تواجه؟
نفّذ هذه الأوامر على العقدة المعنية. كل أمر أدناه تنفّذه بنفسك على خادمك.
sudo ss -lntp | grep 10250
sudo systemctl status kubelet --no-pagerيعرض ss -lntp مقابس TCP التي تستمع للاتصالات، مع العملية المرتبطة بكل مقبس. يعني -l أن المقبس في وضع الاستماع، ويحافظ -n على عرض أرقام المنافذ بصيغتها الرقمية، ويقيّد -t النتائج بـTCP، ويعرض -p العملية المالكة. يتطلب هذا الخيار الأخير صلاحيات root؛ وإلا فسيظهر عمود العملية فارغاً ولن تحصل على معلومات مفيدة.
يشير السطر المنتهي بـusers:(("kubelet",pid=1043,fd=23)) إلى أن kubelet يعمل ويشغل المنفذ. إذا كنت تتوقع أن يكون المنفذ متاحاً، فهذا يفسّر المشكلة. إذا لم يعرض ss أي نتيجة، وكانت لوحة التحكم لا تزال عاجزة عن الوصول إلى هذه العقدة، فلا علاقة لجدار الحماية بالمشكلة بعد، لأن لا شيء يستقبل الاتصالات على المنفذ أصلاً. اعرف سبب توقف kubelet قبل تعديل أي قواعد.
يوفّر systemctl status kubelet النصف الآخر من الصورة. من الطبيعي أن يعرض active (running) وقت بدء يعود إلى بضع دقائق مضت. ومن الطبيعي أيضاً أن يعيد kubelet التشغيل كل بضع ثوانٍ قبل تنفيذ kubeadm init أو kubeadm join: تبدأ الوحدة المثبّتة مع النظام، ولا تجد إعدادات، ثم تتوقف. توضّح الوثائق الرسمية أن حلقة التعطل هذه متوقعة، بينما ينتظر kubelet أن يحدد له kubeadm ما يجب فعله. إذا لم تكن آلية إعادة التشغيل في systemd مألوفة لك، فراجع كيفية عمل أنواع خدمات systemd وسياسات إعادة التشغيل للحصول على الخلفية اللازمة لهذا القسم.
لماذا يكون المنفذ 10250 مستخدماً عند تشغيل kubeadm init
يجري kubeadm init فحوصات ما قبل التنفيذ قبل أن يكتب أي شيء على القرص. يحاول أحد هذه الفحوصات ربط كل منفذ تحتاج إليه طبقة التحكم، ويتوقف مع ظهور خطأ يذكر المنفذ 10250 عندما يفشل هذا الربط. هذا ليس خللاً. يرفض kubeadm إنشاء عنقود ثانٍ فوق بقايا عنقود أول.
تتسبب أربعة أمور في ذلك عملياً:
- تنفيذ سابق لـ
kubeadm initأوkubeadm joinتوقف في منتصف العملية. حصل kubelet على إعدادات، ولذلك فهو يعمل ويحتفظ بالمنفذ. - وجود
kubeadm resetبدأ تشغيله لكنه لم يكتمل. يوقف reset خدمة kubelet، لكنه لا يعطّل الوحدة، ولذلك تعيد عملية الإقلاع التالية تشغيل المستمع. - تثبيت k3s أو توزيعة Kubernetes أخرى على الخادم نفسه. يضم k3s مكوّناً مدمجاً من kubelet، ويربط kubelet هذا المنفذ 10250 أيضاً.
- تثبيت حزمة
kubeletبواسطة apt وتشغيلها عبر وحدة systemd الخاصة بها، على خادم لم تشغّل عليه kubeadm بعد.
حدّد السبب قبل تغيير أي شيء:
sudo ss -lntp 'sport = :10250'
systemctl list-units --type=service --state=running | grep -Ei 'kubelet|k3s|k0s'إذا كان المستمع تابعاً إلى k3s، فأوقفه وحدد أي عنقود تريد فعلياً. لا يمكن لـk3s وkubeadm مشاركة خادم واحد، لأنهما يحجزان المنافذ نفسها ودليل CNI نفسه (واجهة شبكة الحاويات). يترك مُثبّت k3s سكربت إلغاء التثبيت في /usr/local/bin/k3s-uninstall.sh على عقدة الخادم، وفي k3s-agent-uninstall.sh على عقدة العامل.
لماذا لا يؤدي إيقاف kubelet بالقوة إلى تحرير المنفذ
يحرّر sudo pkill kubelet المنفذ 10250 لمدة عشر ثوانٍ تقريباً. تضبط الوحدة المضمّنة سياسة لإعادة التشغيل، لذلك يشغّل systemd نسخة جديدة من kubelet، فتربط المنفذ نفسه مرة أخرى. يمكنك عرض السياسة بنفسك:
systemctl show kubelet -p Restart -p RestartSec
sudo systemctl stop kubelet
sudo ss -lntp | grep 10250إن استخدام Restart=always مع RestartSec=10 هو الإعداد الذي تأتي به الوحدة، ولذلك يبدو kill وكأنه نجح ثم يتوقف أثره. يُعد systemctl stop الطريقة الصحيحة لتحرير المنفذ، لأن systemd يتوقف عن إعادة تشغيل الوحدة التي طلبت منه إيقافها.
لا يكفي تحرير المنفذ على عقدة تستضيف نصف عنقود. لا يزال /var/lib/kubelet/config.yaml، والشهادات الموجودة تحت /etc/kubernetes/pki، وأي ملفات بيان static pod في /etc/kubernetes/manifests موجودة كلها. ستفشل فحوصات المتطلبات المسبقة اللاحقة بسبب هذه الملفات، وتجاوز الفحوصات بالقوة يترك لك عنقوداً لا تتطابق شهاداته مع إعداداته. أعد ضبط العقدة بالطريقة الصحيحة بدلاً من ذلك.
إعادة ضبط العقدة بشكل نظيف
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d
rm -rf $HOME/.kube
sudo systemctl stop kubelet
sudo ss -lntp | grep -E '10250|6443|2379'يتجاوز -f مطالبة التأكيد. تحاول عملية reset التراجع عن التغييرات التي أجراها init أو join قدر الإمكان. وهي تزيل الملفات والإعدادات المحلية، وتزيل عضو etcd المحلي من عقدة control plane، وتنظف الشهادات في /etc/kubernetes/pki، وتزيل إعدادات kubelet والـmanifests.
توضح الوثائق صراحةً ما الذي تتركه عملية reset، وكل عنصر من هذه العناصر قد يسبب مشكلات غير متوقعة. فهي لا تنظف /etc/cni/net.d، لذلك يبقى إعداد إضافة CNI القديمة وتقرأه المجموعة الجديدة. ولا تنظف أي قواعد iptables أو nftables أو IPVS طبّقها kube-proxy على المضيف. ولا تلمس $HOME/.kube، لذلك يستمر kubectl في الاتصال بمجموعة لم تعد موجودة، ويعرض أخطاء شهادات تبدو كأنها مشكلة جديدة.
تُعد قواعد الحزم المتبقية الجزء الأكثر إرباكاً. يؤدي تفريغ الجداول يدوياً أيضاً إلى حذف القواعد التي ثبّتها ufw، لأن ufw في Ubuntu يكتب عبر الواجهة الخلفية نفسها. وبذلك يبقى الخادم من دون تصفية حتى تشغّل sudo ufw reload. إذا كنت تعيد بناء العقدة على أي حال، فأعد تشغيلها بعد reset. تؤدي إعادة التشغيل إلى إزالة قواعد التشغيل التي أضافها kube-proxy، وتستغرق وقتاً أقل من معالجة مجموعة قواعد جرى تفريغها جزئياً. يشرح سبب ظهور قواعد iptables وقواعد nftables في مخرجات بعضها بعضاً ما يحدث في الخلفية.
يجب أن يعرض الأمر الأخير ss أي مخرجات. عدم وجود مستمع على 10250 أو 6443 أو 2379 يعني أن العقدة جاهزة لـkubeadm init جديدة.
لماذا تنتهي مهلة kubectl logs وkubectl exec على المنفذ 10250
هذه شكوى معاكسة، ولا تشير بوضوح إلى مشكلة في منفذ. تبدأ مكونات العنقود بالعمل. وتصبح العقد Ready. وتعمل Pods. ثم يفشل أحد الأوامر:
Error from server: Get "https://10.0.0.12:10250/containerLogs/default/web-0/web": dial tcp 10.0.0.12:10250: i/o timeoutاقرأ هذه الرسالة من نهايتها. حاول API server فتح اتصال TCP مع العقدة على المنفذ 10250، لكنه لم يتلقَّ أي استجابة. يعني i/o timeout أن الحزم أُسقطت بصمت، ولذلك يحجبها شيء ما: جدار الحماية على المضيف في العقدة، أو جدار الحماية الشبكي المنفصل لدى مزود الخدمة في لوحة التحكم. ويعني connect: connection refused في الموضع نفسه العكس. وصلت الحزمة، ولم يكن هناك شيء يستمع، ولذلك فإن kubelet متوقف. هذا هو الزوج نفسه من الأسباب الموضح في رفض الاتصال مقابل انتهاء مهلة الاتصال، لكن هنا على منفذ مختلف.
تبقى العقد Ready طوال ذلك لأن حالة العقدة تنتقل في الاتجاه الآخر. يتصل kubelet من الخارج بـ API server على المنفذ 6443 ويرسل نبضة الحياة الخاصة به، ولا يتطلب ذلك أي اتصال وارد على 10250. لذلك يؤدي حجب 10250 إلى عنقود يجدول Pods بصورة طبيعية، لكنه يفشل فقط في logs وexec وport-forward وmetrics.
يعني kubectl top node الذي يجيب على error: Metrics API not available العطل نفسه كما يظهر عبر metrics-server، إذ يذكر سجله العقدة والمنفذ:
unable to fully scrape metrics from node worker-1: unable to fetch metrics from node worker-1: Get "https://10.0.0.12:10250/metrics/resource": dial tcp 10.0.0.12:10250: i/o timeoutاختبر المسار قبل تعديل أي قاعدة في جدار الحماية
شغّل هذا الأمر من عقدة control plane، باستخدام عنوان worker:
nc -zv 10.0.0.12 10250
curl -sk -o /dev/null -w '%{http_code}\n' https://10.0.0.12:10250/healthzيفتح nc -z اتصالاً، ثم يغلقه، ويطبع succeeded! عندما يقبل المنفذ الاتصال. أما سطر curl فهو الاختبار الأفضل، لأنه يثبت أن kubelet يقدّم الخدمة، بدلاً من إثبات أن المنفذ مفتوح فحسب. ويطبع 401. هذه هي النتيجة السليمة: اكتملت مصافحة TLS (أمان طبقة النقل)، ثم رفض kubelet طلباً غير موثّق، وهذا ما ينبغي أن يفعله تحديداً. يتجاوز -k التحقق من الشهادة، وهذا مناسب لأنك تختبر المسار، لا سلسلة الثقة.
يعني التوقف الطويل الذي ينتهي بمهلة زمنية أن الحزم يجري إسقاطها. وعودة curl: (7) Failed to connect فوراً تعني أن المنفذ مغلق على مضيف يمكن الوصول إليه. اختبر من عقدة control plane، وليس من حاسوبك المحمول، لأن control plane هي الآلة الوحيدة التي يهم وصولها هنا.
المنافذ التي يحتاج إليها كل من control plane وworker
هذه هي المنافذ الواردة التي تدرجها الوثائق upstream. على عقدة control plane، يجب فتح TCP 6443 لخادم API أمام كل ما يشغّل kubectl. ويُستخدم TCP 2379 إلى 2380 لواجهة API الخاصة بعملاء etcd ونظرائهم، ويحتاج إليه خادم API وetcd نفسه. ويُستخدم TCP 10250 لواجهة API الخاصة بـkubelet، وتحتاج إليه العقدة نفسها وcontrol plane. ويُستخدم TCP 10259 بواسطة kube-scheduler، وTCP 10257 بواسطة kube-controller-manager، ولا تستخدمهما إلا العقدة نفسها.
على عقدة worker، يُستخدم TCP 10250 لواجهة API الخاصة بـkubelet، وتحتاج إليه العقدة نفسها وcontrol plane. ويُستخدم TCP 10256 بواسطة kube-proxy، وتحتاج إليه العقدة نفسها وموازنات التحميل التي تجري فحوصات الصحة. ويُستخدم TCP وUDP من 30000 إلى 32767 لخدمات NodePort. وهذا هو النطاق الافتراضي، ويمكن الوصول إليه من الجهات التي تحتاج إلى هذه الخدمات.
يضيف مكوّن CNI الخاص بك منافذه إلى هذه القائمة، ولا تكون هذه المنافذ مذكورة فيها. يحتاج Flannel وCalico في وضع VXLAN إلى UDP 4789 بين العقد. ويحتاج Calico مع BGP إلى TCP 179. راجع وثائق المكوّن الذي تستخدمه وافتح هذه المنافذ بين العقد، وإلا فلن تتواصل الـpods الموجودة على عقد مختلفة مع بعضها، حتى إذا كانت جميع المنافذ الواردة في هذا القسم مفتوحة.
فتح المنفذ 10250 دون تعريضه للإنترنت
يمكن لواجهة kubelet API بدء عملية داخل أي حاوية على تلك العقدة. اعتبر فتح المنفذ 10250 بمثابة وصول root إلى العقدة، وقيّده حسب عنوان المصدر. لا تسمح بالوصول إليه من أي مكان.
sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp comment 'kubelet API'
sudo ufw allow from 10.0.0.0/24 to any port 10256 proto tcp comment 'kube-proxy'
sudo ufw status numberedاستبدل 10.0.0.0/24 بالشبكة المشتركة بين عقدك. يعرض ufw status numbered القواعد النشطة مع فهرس، لذا يمكنك حذف القاعدة الخاطئة باستخدام sudo ufw delete <number>. يشرح أساسيات ufw لخادم VPS قواعد الترتيب التي تحدد أي من إدخالاتك ينطبق فعلياً.
يتسبب أحد إعدادات ufw في تعطيل Kubernetes بمفرده. تمر حركة مرور Pod عبر العقدة بإعادة التوجيه، ولا تُسلَّم محلياً، ويسقط ufw الحزم المُعاد توجيهها افتراضياً. عيّن DEFAULT_FORWARD_POLICY="ACCEPT" في /etc/default/ufw ثم شغّل sudo ufw reload. من دون ذلك، قد يكون المنفذ 10250 مكشوفاً بالكامل، ومع ذلك يفشل مرور حركة Pod بين العقد.
تحقق أيضاً من جدار الحماية لدى موفر الخدمة. تحتوي معظم لوحات VPS على جدار حماية على مستوى الشبكة يسبق الخادم ولا يظهر في ufw status. لن تغيّر القاعدة التي أضفتها على العقدة شيئاً إذا لم تصل الحزمة إليها أصلاً.
عندما يكون المنفذ قابلاً للوصول ويستمر الطلب في الفشل
تظهر بعض حالات الفشل على المنفذ 10250 فوراً بدلاً من الانتظار، وهذا يعني أن الاتصال نجح وأن الطلب رُفض. يعني x509: certificate signed by unknown authority في سجل metrics-server أن kubelet يقدّم شهادة موقّعة ذاتياً لا يثق بها scraper. الحل المعتاد هو تفعيل تدوير شهادات الخدمة في kubelet لكي توقّعها CA الخاصة بالعنقود، ثم الموافقة على طلب توقيع الشهادة، أو قبول المخاطر في عنقود تجريبي وتشغيل metrics-server باستخدام --kubelet-insecure-tls.
تُعد الرسالة التي تحتوي على Forbidden مع nodes/proxy أو nodes/metrics فشلاً في RBAC (التحكم في الوصول المستند إلى الأدوار). وصل المستدعي إلى kubelet، ثم سأل kubelet خادم API عمّا إذا كانت تلك الهوية تملك صلاحية استخدام المورد الفرعي، وكانت الإجابة لا. أصلح ClusterRole الخاص بالمستدعي. لن يفيد أي تغيير في الجدار الناري، لأن شيئاً لم يُحظر.
إذا كنت تريد عنقوداً صغيراً واحداً فقط
إذا واجهت هذه الأخطاء أثناء إعداد kubeadm للمرة الأولى على VPS واحد، ففكّر فيما إذا كنت تحتاج إلى kubeadm أساساً. يوفّر لك عنقود k3s بعقدة واحدة على VPS واجهة Kubernetes API عاملة بأمر واحد، مع إعداد kubelet وkube-proxy وCNI معاً. يظل المنفذ 10250 موجوداً هناك، وتنطبق عليه القواعد نفسها، لكنك لن تضطر بعد ذلك إلى تجميع مستوى التحكم بنفسك.
FAQ
فيمَ يُستخدم المنفذ 10250 في Kubernetes؟
إنه واجهة HTTPS API موثَّقة لـkubelet على كل عقدة، سواء كانت عقدة control plane أو عقدة worker. يتصل بها API server لتنفيذ kubectl logs وkubectl exec وkubectl attach وkubectl port-forward، ويجمع metrics-server المقاييس /metrics/resource منها لتوفير kubectl top. لا تستخدم حالة العقدة هذا المنفذ، لأن kubelet يرسل نبضة الحياة الصادرة إلى API server عبر المنفذ 6443. لذلك، يؤدي حظر 10250 إلى ظهور العقد بالحالة Ready، بينما تفشل عمليات عرض السجلات والتنفيذ.
كيف أعرف ما الذي يستمع على المنفذ 10250؟
شغّل sudo ss -lntp | grep 10250 على العقدة. يحدّد الحقل users:((...)) في نهاية السطر اسم العملية ومعرّف PID الخاص بها. يهم sudo، لأن عمود العملية يكون فارغاً من دون root. إذا كان المالك هو kubelet، يوضح لك sudo systemctl status kubelet --no-pager ما إذا كان kubelet يعمل بصورة سليمة أو يعاد تشغيله في حلقة. إذا كان المالك هو k3s، فهذا يعني أن توزيعتَي Kubernetes مثبتتان على خادم واحد، وعليك إزالة إحداهما.
هل يجب أن أفتح المنفذ 10250 في جدار الحماية؟
نعم، بين عقدك. يجب أن يتمكن control plane من الوصول إلى 10250 على كل عقدة، بما في ذلك نفسه، وإلا فستفشل عمليات عرض السجلات والتنفيذ وإعادة توجيه المنافذ وجمع المقاييس. قيّد الوصول حسب المصدر إلى الشبكة المشتركة بين عقدك، مثل sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp. لا تفتح المنفذ للإنترنت، لأن أي جهة يمكنها المصادقة على هذا المنفذ تستطيع تشغيل عملية داخل أي حاوية على العقدة.
لماذا تفشل عملية kubectl logs لحاويات pods الموجودة على عقدة واحدة فقط؟
لأن الحظر يخص عقدة محددة، ولأن API server يتصل بالعقدة التي تستضيف الـpod المعني. اقرأ نص الخطأ، فهو يتضمن عنوان IP للعقدة التي حاول الوصول إليها. ثم شغّل nc -zv <node-ip> 10250 من عقدة control plane. يشير انتهاء المهلة إلى وجود مشكلة في جدار الحماية على تلك العقدة أو في جدار الحماية الشبكي لدى مزود الخدمة. يشير connection refused إلى أن kubelet لا يعمل على تلك العقدة، لذا تحقّق من systemctl status kubelet عليها بدلاً من ذلك.