رفع خطای پورت 10250 در kubelet روی Ubuntu
اگر با خطای address already in use هنگام اجرای kubeadm init یا مسدود شدن دسترسی kubectl logs و exec مواجه هستید، این راهنما نحوه تنظیم صحیح فایروال و سرویس kubelet را آموزش میدهد.
پورت 10250 چیست
پورت 10250 مربوط به API سرویس kubelet است و هر خطایی که به آن اشاره میکند، ناشی از یکی از دو مشکل متضاد است. یا سرویس دیگری از قبل این پورت را اشغال کرده است و در نتیجه kubeadm init از اجرا باز میماند، یا هیچچیز نمیتواند به این پورت دسترسی پیدا کند و در نتیجه kubectl logs و kubectl exec در برابر نودی که در ظاهر کاملاً سالم است، با شکست مواجه میشوند.
سرویس kubelet عاملی است که Kubernetes روی تمام نودها اجرا میکند. این سرویس کانتینرها را راهاندازی کرده و وضعیت آنها را به control plane گزارش میدهد. همچنین kubelet روی پورت TCP 10250 گوش میدهد و یک API مبتنی بر HTTPS ارائه میدهد که توسط control plane فراخوانی میشود. زمانی که شما دستورات kubectl logs، kubectl exec، kubectl attach یا kubectl port-forward را اجرا میکنید، API server یک اتصال به این پورت برقرار میکند. سرویس metrics-server نیز دادههای /metrics/resource را از همین پورت جمعآوری میکند که باعث کارکرد صحیح kubectl top node میشود.
این API احراز هویت میشود. kubeadm دسترسی ناشناس (anonymous) را غیرفعال کرده و kubelet را به سمت CA (مرجع صدور گواهی) کلاستر هدایت میکند؛ بنابراین به درخواستی که فاقد اعتبارنامه باشد، به جای باز کردن یک shell در داخل یکی از کانتینرها، پاسخ Unauthorized داده میشود. این جزئیات را به خاطر بسپارید، زیرا سریعترین راه برای اثبات در دسترس بودن پورت نیز همین است. اگر مفهوم پورتها برای شما جدید است، اینکه پورت در لینوکس واقعاً چیست مدلی را که این راهنما بر اساس آن نوشته شده است، پوشش میدهد.
هر دو حالت شکست از یک الزام ناشی میشوند. پورت 10250 باید پیش از شروع به کار kubelet آزاد باشد و پس از اجرا، از سمت control plane قابل دسترسی باشد.
با کدامیک از دو مشکل مواجه هستید
این دستورات را روی نود مورد نظر اجرا کنید. هر دستوری که در ادامه آمده است، دستوری است که باید شخصاً روی سرور خود اجرا کنید.
sudo ss -lntp | grep 10250
sudo systemctl status kubelet --no-pagerss -lntp سوکتهای TCP در حال گوش دادن (listening) را به همراه پردازش مربوط به هر کدام فهرست میکند. -l به معنای وضعیت listening است، -n پورتها را به صورت عددی نگه میدارد، -t خروجی را به TCP محدود میکند و -p پردازش مالک پورت را نمایش میدهد. برای استفاده از این فلگ آخر به دسترسی root نیاز دارید، در غیر این صورت ستون مربوط به پردازش خالی میماند و اطلاعاتی به دست نمیآورید.
خطی که به users:(("kubelet",pid=1043,fd=23)) ختم میشود، نشان میدهد که kubelet در حال اجراست و پورت را اشغال کرده است. اگر انتظار داشتید پورت آزاد باشد، پاسخ مشکل شما همین است. اگر ss هیچ خروجیای نمایش نمیدهد و control plane همچنان نمیتواند به این نود متصل شود، یعنی هنوز فایروالی در کار نیست، زیرا اصلاً سرویسی روی آن پورت در حال اجرا نیست. پیش از دست زدن به هر قانونی، دلیل پایین بودن kubelet را پیدا کنید.
systemctl status kubelet نیمهٔ دیگر تصویر را نشان میدهد. active (running) با زمان شروعی که مربوط به چند دقیقه پیش باشد، وضعیت عادی است. ریاستارت شدن kubelet در هر چند ثانیه، پیش از آنکه kubeadm init یا kubeadm join را اجرا کرده باشید نیز عادی است: واحد بستهبندیشده (packaged unit) در زمان نصب شروع به کار میکند، فایلی برای پیکربندی نمییابد و خارج میشود. مستندات رسمی این وضعیت crash loop را به عنوان رفتار مورد انتظار در حالی که kubelet منتظر دستورات kubeadm است، معرفی میکنند. اگر با رفتار ریاستارت systemd آشنا نیستید، نحوه عملکرد انواع سرویسها و سیاستهای ریاستارت در systemd پیشزمینهٔ لازم برای این بخش است.
چرا هنگام اجرای kubeadm init پورت 10250 از قبل در حال استفاده است
kubeadm init پیش از نوشتن هرگونه داده روی دیسک، بررسیهای اولیه (preflight checks) را انجام میدهد. یکی از این بررسیها تلاش میکند تا تمام پورتهای مورد نیاز control plane را bind کند؛ اگر این عملیات با شکست مواجه شود، فرآیند متوقف شده و خطایی مبنی بر اشغال بودن پورت 10250 نمایش داده میشود. این یک باگ نیست، بلکه رفتار پیشفرض kubeadm است که از ایجاد یک کلاستر جدید روی بقایای کلاستر قبلی جلوگیری میکند.
در عمل، چهار عامل باعث بروز این مشکل میشوند:
- اجرای ناقص یک
kubeadm initیاkubeadm joinقبلی. در این حالت kubelet تنظیمات را دریافت کرده، در حال اجراست و پورت را اشغال کرده است. - اجرای یک
kubeadm resetکه شروع شده اما به پایان نرسیده است. دستور reset سرویس kubelet را متوقف میکند اما unit آن را غیرفعال نمیکند؛ بنابراین با reboot بعدی، listener دوباره فعال میشود. - نصب k3s یا توزیع دیگری از Kubernetes روی همان سرور. k3s شامل یک kubelet داخلی است که آن هم پورت 10250 را bind میکند.
- بسته
kubeletکه توسط apt نصب شده و توسط unit اختصاصی خود در systemd شروع به کار کرده است، در حالی که شما هنوز kubeadm را اجرا نکردهاید.
پیش از انجام هرگونه تغییر، ابتدا مشخص کنید کدامیک از موارد فوق عامل مشکل است:
sudo ss -lntp 'sport = :10250'
systemctl list-units --type=service --state=running | grep -Ei 'kubelet|k3s|k0s'اگر listener متعلق به k3s است، ابتدا تصمیم بگیرید که به کدام کلاستر نیاز دارید. k3s و kubeadm نمیتوانند روی یک سرور مشترک باشند، زیرا هر دو پورتهای یکسان و دایرکتوری CNI (رابط شبکه کانتینر) یکسانی را اشغال میکنند. نصبکننده k3s یک اسکریپت حذف در مسیر /usr/local/bin/k3s-uninstall.sh روی گرههای سرور و در مسیر k3s-agent-uninstall.sh روی گرههای agent قرار میدهد.
چرا کشتن kubelet پورت را آزاد نمیکند
sudo pkill kubelet پورت 10250 را برای حدود 10 ثانیه آزاد میکند. واحد بستهبندیشده دارای یک سیاست راهاندازی مجدد است، بنابراین systemd یک kubelet جدید را شروع میکند و آن دوباره به همان پورت متصل میشود. شما میتوانید این سیاست را شخصاً مشاهده کنید:
systemctl show kubelet -p Restart -p RestartSec
sudo systemctl stop kubelet
sudo ss -lntp | grep 10250Restart=always همراه با RestartSec=10 همان چیزی است که واحد با آن عرضه میشود، و دقیقاً به همین دلیل است که kill به نظر میرسد کار کرده است اما بعداً متوقف میشود. systemctl stop روش صحیح برای آزاد کردن پورت است، زیرا systemd دیگر واحدی را که به آن دستور توقف دادهاید، مجدداً راهاندازی نمیکند.
یک پورت آزاد در نودی که نیمی از یک کلاستر را حمل میکند، کافی نیست. /var/lib/kubelet/config.yaml، گواهیهای موجود در /etc/kubernetes/pki و هرگونه مانیفست static pod در /etc/kubernetes/manifests همگی همچنان در آنجا باقی میمانند. بررسیهای پیشنیاز (preflight checks) در مراحل بعدی به این فایلها برخورد میکنند و نادیده گرفتن اجباری این بررسیها، کلاستری را به شما میدهد که گواهیهای آن با پیکربندیاش مطابقت ندارد. نود را بهدرستی reset کنید.
بازنشانی تمیز گره
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 را حذف مینماید.
مستندات بهصراحت بیان میکنند که reset چه مواردی را باقی میگذارد و هر کدام از این موارد میتواند برای کاربران دردسرساز شود. این دستور مسیر /etc/cni/net.d را پاکسازی نمیکند، بنابراین پیکربندی قدیمی CNI plugin باقی میماند و کلاستر جدید شما آن را میخواند. همچنین هیچکدام از قوانین iptables، nftables یا IPVS که توسط kube-proxy روی میزبان اعمال شدهاند را پاک نمیکند. این دستور به $HOME/.kube دست نمیزند، بنابراین kubectl همچنان با کلاستری که دیگر وجود ندارد ارتباط برقرار میکند و خطاهای گواهی بازمیگرداند که ممکن است به اشتباه یک مشکل جدید تلقی شوند.
قوانین باقیمانده در شبکه بخش دشوار ماجرا هستند. پاکسازی دستی جداول (flush)، قوانین نصبشده توسط ufw را نیز از بین میبرد، زیرا ufw در Ubuntu از همان backend استفاده میکند؛ این کار باعث میشود سرور تا زمانی که sudo ufw reload را اجرا نکنید، بدون فیلتر باقی بماند. در گرهای که قصد بازسازی آن را دارید، پس از reset حتماً سیستم را reboot کنید. راهاندازی مجدد، قوانین runtime اضافه شده توسط kube-proxy را پاک میکند و زمان کمتری نسبت به اصلاح یک مجموعه قوانین نیمهپاکشده میگیرد. مطلب چرا قوانین iptables و nftables در خروجی یکدیگر ظاهر میشوند توضیح میدهد که در لایههای زیرین چه اتفاقی در حال رخ دادن است.
آخرین دستور ss نباید هیچ خروجیای داشته باشد. نبود هیچ listener روی پورتهای 10250، 6443 یا 2379 به این معنی است که گره برای یک kubeadm init تازه آماده است.
چرا دستورات kubectl logs و kubectl exec روی پورت 10250 با خطای timeout مواجه میشوند
این مورد، شکایت معکوسی است و خود را به عنوان یک مشکل پورت معرفی نمیکند. کلاستر بالا میآید. وضعیت نودها Ready است. پادها اجرا میشوند. سپس یک دستور با شکست مواجه میشود:
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 تلاش کرد یک اتصال TCP به نود روی پورت 10250 برقرار کند و پاسخی دریافت نکرد. i/o timeout به این معنی است که بستهها در سکوت دور ریخته شدهاند، بنابراین چیزی در حال فیلتر کردن آنهاست: فایروال میزبان روی نود، یا فایروال شبکه جداگانه ارائهدهنده شما در پنل کنترل. connect: connection refused در همان موقعیت به معنای عکس آن است. بسته رسیده اما چیزی در حال گوش دادن نبوده است، بنابراین kubelet از کار افتاده است. این همان جفت علتی است که در تفاوت connection refused و connection timed out توضیح داده شده است، که در اینجا روی یک پورت متفاوت بررسی میشود.
نودها در تمام این مدت Ready باقی میمانند زیرا وضعیت نود در جهت مخالف ارسال میشود. kubelet به سمت بیرون و به سرور API روی پورت 6443 متصل میشود و heartbeat خود را ارسال میکند، و این کار نیازی به هیچ ورودی روی پورت 10250 ندارد. بنابراین مسدود بودن پورت 10250 باعث میشود کلاستری داشته باشید که پادها را به طور عادی زمانبندی میکند و فقط در لاگها، exec، port-forward و متریکها دچار مشکل میشود.
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پیش از تغییر هرگونه قانون فایروال، مسیر را تست کنید
این دستور را از روی یک node کنترل پلین (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/healthznc -z یک اتصال برقرار کرده، آن را میبندد و در صورت پذیرش پورت، succeeded! را چاپ میکند. خط دستور curl تست بهتری است، زیرا بهجای اثبات صرفِ باز بودن پورت، ثابت میکند که kubelet در حال سرویسدهی است. این دستور 401 را چاپ میکند. این نتیجهٔ سالم است: دستدادن (handshake) پروتکل TLS (امنیت لایه انتقال) تکمیل شده و سپس kubelet یک درخواست احراز هویت نشده را رد کرده است؛ که دقیقاً همان رفتاری است که باید داشته باشد. -k بررسی گواهی را نادیده میگیرد، که چون شما در حال تست مسیر هستید و نه زنجیره اعتماد، مشکلی ندارد.
وقفهٔ طولانی که به timeout ختم میشود، به این معنی است که بستهها در حال drop شدن هستند. بازگشت فوری curl: (7) Failed to connect به این معنی است که پورت روی یک میزبانِ در دسترس، بسته است. تست را از روی node کنترل پلین انجام دهید، نه از لپتاپ خود، زیرا کنترل پلین تنها ماشینی است که دسترسی آن در اینجا اهمیت دارد.
پورتهای مورد نیاز برای control plane و worker
اینها پورتهای ورودی هستند که در مستندات بالادستی ذکر شدهاند. روی یک گره control plane، پورت TCP 6443 برای API server باید برای تمام مواردی که kubectl را اجرا میکنند، باز باشد. پورتهای TCP 2379 تا 2380 برای etcd client و peer API استفاده میشوند که توسط API server و خود etcd به کار میروند. پورت TCP 10250 برای kubelet API است که توسط خود گره و control plane استفاده میشود. پورت TCP 10259 برای kube-scheduler و پورت TCP 10257 برای kube-controller-manager است که هر دو فقط توسط خود گره استفاده میشوند.
روی یک گره worker، پورت TCP 10250 برای kubelet API است که توسط خود گره و control plane استفاده میشود. پورت TCP 10256 برای kube-proxy است که توسط خود گره و load balancerها برای بررسی سلامت (health check) استفاده میشود. پورتهای TCP و UDP در محدوده 30000 تا 32767 برای سرویسهای NodePort هستند که محدوده پیشفرض بوده و برای هر کسی که به آن سرویسها نیاز دارد، قابل دسترسی است.
پلاگین CNI شما پورتهای اختصاصی خود را به این لیست اضافه میکند که در اینجا ذکر نشدهاند. Flannel و Calico در حالت VXLAN به پورت UDP 4789 بین گرهها نیاز دارند. Calico با BGP به پورت TCP 179 نیاز دارد. مستندات پلاگین خود را بررسی کنید و این پورتها را بین گرهها باز کنید، در غیر این صورت پادها در گرههای مختلف نمیتوانند با یکدیگر ارتباط برقرار کنند، حتی اگر تمام پورتهای ذکر شده در این بخش باز باشند.
باز کردن پورت 10250 بدون قرار دادن آن در معرض اینترنت
API مربوط به kubelet میتواند فرآیندی را درون هر کانتینری روی آن نود اجرا کند. پورت 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 که از نود عبور میکند، فوروارد (forward) میشود و نه تحویل محلی، و ufw بهطور پیشفرض بستههای فوروارد شده را drop میکند. مقدار 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 در حال ارائه یک گواهی خودامضا (self-signed) است که scraper به آن اعتماد ندارد. راهحلهای معمول این است که قابلیت چرخش گواهی (certificate rotation) در kubelet فعال شود تا CA کلاستر آن را امضا کند و سپس درخواست امضای گواهی (CSR) تأیید شود، یا در یک کلاستر آزمایشگاهی، ریسک را بپذیرید و metrics-server را با پرچم --kubelet-insecure-tls اجرا کنید.
پیامی که حاوی Forbidden به همراه nodes/proxy یا nodes/metrics باشد، نشاندهنده شکست در RBAC (کنترل دسترسی مبتنی بر نقش) است. فراخواننده به kubelet رسیده است، kubelet از API server پرسیده که آیا آن هویت اجازه استفاده از subresource را دارد یا خیر، و پاسخ منفی بوده است. ClusterRole فراخواننده را اصلاح کنید. هیچ تغییری در فایروال کمکی نخواهد کرد، زیرا هیچ چیزی مسدود نشده است.
اگر فقط به یک کلاستر کوچک نیاز دارید
اگر هنگام راهاندازی اولیه kubeadm روی یک VPS واحد با این خطاها مواجه میشوید، بررسی کنید که آیا اصلاً به kubeadm نیاز دارید یا خیر. یک کلاستر تکنود k3s روی VPS به شما یک Kubernetes API فعال را تنها با یک دستور میدهد، که در آن kubelet، kube-proxy و یک CNI از قبل به هم متصل شدهاند. پورت 10250 همچنان در آنجا وجود دارد و همان قوانین برای آن اعمال میشود، اما دیگر نیازی نیست خودتان control plane را سرهم کنید.
FAQ
پورت 10250 در Kubernetes برای چه کاری استفاده میشود؟
این پورت، API احراز هویت شده HTTPS مربوط به kubelet روی تمامی نودها، اعم از control plane و worker است. API server برای kubectl logs، kubectl exec، kubectl attach و kubectl port-forward به آن متصل میشود و metrics-server نیز برای تأمین kubectl top، دادهها را از /metrics/resource روی این پورت جمعآوری میکند. وضعیت نود از این پورت استفاده نمیکند، زیرا kubelet ضربان قلب (heartbeat) خود را به صورت خروجی به سمت API server روی پورت 6443 ارسال میکند. به همین دلیل است که مسدود بودن پورت 10250 باعث میشود نودها وضعیت Ready را نشان دهند، در حالی که لاگها و دستور exec با شکست مواجه میشوند.
چگونه بفهمم چه چیزی روی پورت 10250 در حال گوش دادن است؟
دستور sudo ss -lntp | grep 10250 را روی نود اجرا کنید. فیلد users:((...)) در انتهای خط، نام پردازش و PID آن را مشخص میکند. استفاده از sudo اهمیت دارد، زیرا بدون دسترسی root، ستون مربوط به پردازش خالی خواهد بود. اگر مالک پردازش kubelet باشد، sudo systemctl status kubelet --no-pager به شما میگوید که آیا kubelet سالم است یا در یک حلقه بازراهاندازی (restart loop) گیر کرده است. اگر مالک k3s باشد، یعنی شما دو توزیع Kubernetes روی یک سرور نصب کردهاید و باید یکی از آنها را حذف کنید.
آیا باید پورت 10250 را در فایروال باز کنم؟
بله، بین نودهای خود. control plane باید بتواند به پورت 10250 روی تمامی نودها (از جمله روی خودش) دسترسی داشته باشد، در غیر این صورت لاگها، دستور exec، port-forward و متریکها همگی با خطا مواجه میشوند. دسترسی را بر اساس مبدأ به شبکهای که نودهای شما در آن مشترک هستند محدود کنید، برای مثال sudo ufw allow from 10.0.0.0/24 to any port 10250 proto tcp. آن را برای اینترنت باز نگذارید: هر چیزی که بتواند به آن پورت احراز هویت کند، میتواند در هر کانتینری روی آن نود، پردازشی را اجرا کند.
چرا دستور kubectl logs فقط برای پادهای یک نود خاص با خطا مواجه میشود؟
زیرا مسدودسازی به صورت نود به نود است و API server به نود خاصی که میزبان پاد است متصل میشود. متن خطا را بخوانید: این متن شامل آدرس IP نودی است که تلاش کرده به آن متصل شود. سپس دستور nc -zv <node-ip> 10250 را از یک نود control plane اجرا کنید. بروز timeout نشاندهنده وجود مشکل در فایروال همان نود یا فایروال شبکه ارائهدهنده خدمات شماست. خطای connection refused نشان میدهد که kubelet در آنجا در حال اجرا نیست، بنابراین به جای آن، وضعیت systemctl status kubelet را روی آن نود بررسی کنید.