Ошибка порта 10250 в kubelet: решение проблем
Устранение ошибки address already in use при запуске kubeadm и настройка правил firewall для корректной работы команд kubectl logs и exec через API порт 10250 в Kubernetes.
Что такое порт 10250
Порт 10250 используется API kubelet, и любая ошибка, связанная с ним, указывает на одну из двух противоположных проблем. Либо порт уже занят другим процессом, из-за чего kubeadm init не может запуститься. Либо порт недоступен извне, из-за чего kubectl logs и kubectl exec завершаются с ошибкой при обращении к узлу, который в остальном выглядит полностью исправным.
Kubelet — это агент Kubernetes, работающий на каждом узле. Он запускает контейнеры и сообщает об их состоянии в control plane. Он также ожидает входящие соединения по TCP 10250 и предоставляет HTTPS API, к которому обращается control plane. API server открывает соединение с этим портом, когда вы выполняете kubectl logs, kubectl exec, kubectl attach или kubectl port-forward. metrics-server собирает данные с /metrics/resource через тот же порт, что обеспечивает работу kubectl top node.
Этот API требует аутентификации. kubeadm отключает анонимный доступ и указывает kubelet использовать CA (центр сертификации) кластера, поэтому на запрос без учетных данных сервер ответит Unauthorized, а не предоставит доступ к оболочке внутри одного из ваших контейнеров. Запомните эту деталь: это самый быстрый способ проверить, доступен ли порт. Если концепция портов для вас нова, в статье что такое порт в Linux на самом деле описана модель, на которую опирается данное руководство.
Оба сценария сбоя вытекают из одного требования. Порт 10250 должен быть свободен до запуска kubelet и доступен со стороны control plane после того, как он начал работу.
Какая из двух проблем у вас возникла
Выполните эти команды на соответствующем узле. Каждую команду из списка ниже вы запускаете самостоятельно на своем сервере.
sudo ss -lntp | grep 10250
sudo systemctl status kubelet --no-pagerss -lntp выводит список прослушиваемых TCP-сокетов с указанием процесса для каждого из них. -l означает прослушивание, -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, это тоже нормально: пакетный юнит запускается при установке, не находит конфигурацию и завершается. Разработчики upstream указывают, что такой цикл перезапуска (crash loop) является ожидаемым поведением, пока kubelet ждет указаний от kubeadm. Если вы не знакомы с поведением systemd при перезапуске, принципы работы типов сервисов и политик перезапуска systemd послужат базой для этого раздела.
Почему порт 10250 уже занят при запуске kubeadm init
kubeadm init выполняет предварительные проверки перед записью любых данных на диск. Одна из этих проверок пытается занять каждый порт, необходимый для control plane, и останавливается с ошибкой, указывающей на порт 10250, если привязка не удалась. Это не ошибка в программе. Это поведение kubeadm, который отказывается создавать второй кластер поверх остатков первого.
На практике это вызывают четыре причины:
- Ранее запущенный
kubeadm initилиkubeadm join, который завершился с ошибкой на полпути. kubelet уже получил конфигурацию, поэтому он запущен и удерживает порт. kubeadm reset, который был запущен, но не завершён. Команда reset останавливает kubelet, но не отключает юнит, поэтому после следующей перезагрузки слушатель снова активируется.- k3s или другой дистрибутив Kubernetes, установленный на том же сервере. k3s содержит встроенный 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 (container network interface). Установщик 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 10250Restart=always с RestartSec=10 — это параметры, с которыми поставляется юнит, и именно поэтому kill выглядит так, будто команда сработала, а затем оказывается, что нет. systemctl stop — это правильный способ освободить порт, так как systemd перестает перезапускать юнит, который вы приказали остановить.
Свободного порта недостаточно на узле, который обслуживает половину кластера. /var/lib/kubelet/config.yaml, сертификаты в /etc/kubernetes/pki и любые манифесты статических подов в /etc/kubernetes/manifests по-прежнему остаются на месте. Последующие предварительные проверки (preflight checks) будут прерываться из-за этих файлов, а принудительный обход проверок приведет к созданию кластера, сертификаты которого не соответствуют его конфигурации. Вместо этого выполните корректный сброс узла.
Чистая очистка узла
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-плагина сохраняется, и ваш новый кластер считывает ее. Она не удаляет правила 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. Поды работают. Но одна команда завершается с ошибкой:
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 не запущен. Это та же пара причин, что описана в connection refused против connection timed out, прочитайте об этом применительно к другому порту.
Узлы остаются в статусе Ready на протяжении всего этого времени, так как информация о состоянии узла передаётся в другом направлении. Kubelet устанавливает исходящее соединение с API server на порт 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Проверьте путь перед изменением правил межсетевого экрана
Выполните эту команду с узла 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. Это корректный результат: TLS (transport layer security) рукопожатие завершено, после чего kubelet отклонил неавторизованный запрос, что и является ожидаемым поведением. -k пропускает проверку сертификата, что допустимо, так как вы тестируете путь, а не цепочку доверия.
Длительная пауза, заканчивающаяся таймаутом, означает, что пакеты отбрасываются. Если curl: (7) Failed to connect возвращается мгновенно, это означает, что порт закрыт на доступном хосте. Выполняйте проверку с узла control plane, а не с вашего локального компьютера, так как в данном случае имеет значение доступ именно с узла управления.
Какие порты необходимы для 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, к которому обращаются сам узел и балансировщики нагрузки для проверки состояния (health checks). TCP и UDP в диапазоне 30000–32767 используются для сервисов типа NodePort; это диапазон по умолчанию, и он должен быть доступен всем, кому требуются эти сервисы.
Ваш CNI-плагин добавляет собственные порты к этому списку, и они здесь не указаны. Flannel и Calico в режиме VXLAN требуют UDP 4789 между узлами. Calico с использованием BGP требует TCP 179. Изучите документацию вашего плагина и откройте соответствующие порты между узлами, иначе поды на разных узлах не смогут обмениваться трафиком, даже если все порты из этого раздела будут открыты.
Открытие порта 10250 без доступа из Интернета
API kubelet позволяет запустить процесс внутри любого контейнера на узле. Считайте открытый порт 10250 эквивалентом root-доступа к узлу и ограничьте его по IP-адресу источника. Никогда не разрешайте доступ к нему отовсюду.
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. Трафик подов, проходящий через узел, пересылается (forwarded), а не доставляется локально, при этом ufw по умолчанию отбрасывает пересылаемые пакеты. Установите DEFAULT_FORWARD_POLICY="ACCEPT" в файле /etc/default/ufw и выполните sudo ufw reload. Без этого порт 10250 может быть полностью открыт, но обмен трафиком между подами на разных узлах всё равно будет невозможен.
Также проверьте настройки брандмауэра у вашего провайдера. Большинство панелей управления VPS имеют сетевой брандмауэр, который работает перед сервером и не виден для ufw status. Правило, добавленное на узле, не даст результата, если пакет не доходит до сервера.
Когда порт доступен, но запрос всё равно завершается ошибкой
Некоторые сбои на порту 10250 возвращаются мгновенно, а не зависают. Это означает, что соединение было установлено, но запрос был отклонен. Ошибка x509: certificate signed by unknown authority в логах metrics-server указывает на то, что kubelet использует самоподписанный сертификат, которому не доверяет сборщик метрик. Стандартные решения: включить ротацию сертификатов kubelet, чтобы их подписывал CA кластера, а затем одобрить запрос на подпись сертификата (CSR), либо, если это тестовый кластер, принять риски и запустить 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 по-прежнему используется, и к нему применяются те же правила, но вам больше не нужно собирать control plane самостоятельно.
FAQ
Для чего в Kubernetes используется порт 10250?
Это аутентифицированный HTTPS API kubelet, работающий на каждом узле, как на управляющем, так и на рабочем. API server подключается к нему для kubectl logs, kubectl exec, kubectl attach и kubectl port-forward, а metrics-server опрашивает /metrics/resource на этом порту для получения kubectl top. Статус узла не использует этот порт, так как kubelet отправляет сигналы heartbeat (пульс) на API server через порт 6443. Именно поэтому при блокировке порта 10250 узлы отображаются как Ready, а команды получения логов и выполнения команд в контейнерах завершаются ошибкой.
Как узнать, какой процесс слушает порт 10250?
Выполните sudo ss -lntp | grep 10250 на узле. Поле users:((...)) в конце строки указывает имя процесса и его PID. sudo имеет значение, так как без прав root столбец с именем процесса будет пустым. Если владельцем является kubelet, sudo systemctl status kubelet --no-pager покажет, работает ли он корректно или находится в цикле перезагрузки. Если владелец — k3s, значит, на одном сервере установлены два дистрибутива Kubernetes, и один из них необходимо удалить.
Нужно ли открывать порт 10250 в межсетевом экране?
Да, между узлами. Управляющий узел должен иметь доступ к порту 10250 на каждом узле, включая самого себя, иначе получение логов, выполнение команд, проброс портов и сбор метрик работать не будут. Ограничьте доступ по IP-адресам сети, которую используют ваши узлы, например, с помощью 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 с управляющего узла. Ошибка тайм-аута указывает на межсетевой экран на этом узле или на сетевой экран вашего провайдера. Ошибка connection refused указывает на то, что kubelet на этом узле не запущен, поэтому проверьте systemctl status kubelet непосредственно на этом узле.