Ошибка wait-control-plane при kubeadm init с HAProxy VIP
Диагностика и решение ошибки wait-control-plane в kubeadm init с HAProxy VIP для control-plane-endpoint в Kubernetes. Проверки kubelet, crictl, health checks HAProxy, сертификаты SAN и обходные пути для HA кластера.
Почему инициализация Kubernetes кластера с помощью kubeadm init завершается ошибкой wait-control-plane при использовании HAProxy VIP для control-plane-endpoint?
Пытаюсь инициализировать мастер-ноду командой:
kubeadm init --pod-network-cidr=172.16.0.0/16 \
--control-plane-endpoint "10.2.26.17:6443" \
--upload-certs
где 10.2.26.17 — VIP-адрес HAProxy.
В логе ошибки:
[control-plane-check] Waiting for healthy control plane components. This can take up to 4m0s
[control-plane-check] Checking kube-apiserver at https://10.1.62.52:6443/livez
[control-plane-check] Checking kube-controller-manager at https://127.0.0.1:10257/healthz
[control-plane-check] Checking kube-scheduler at https://127.0.0.1:10259/livez
[control-plane-check] kube-controller-manager is not healthy after 4m0.000626064s
[control-plane-check] kube-scheduler is not healthy after 4m0.000944424s
[control-plane-check] kube-apiserver is not healthy after 4m0.000945396s
error: error execution phase wait-control-plane: failed while waiting for the control plane to start: [kube-controller-manager check failed..., kube-scheduler check failed..., kube-apiserver check failed...]
Команда crictl --runtime-endpoint unix:///var/run/containerd/containerd.sock ps -a | grep kube | grep -v pause ничего не выводит.
Конфигурация HAProxy:
frontend kube-apiserver-front
bind 10.2.26.17:6443
mode tcp
option tcplog
default_backend kube-apiserver-back
backend kube-apiserver-back
mode tcp
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
server node1 10.1.62.52:6443 check check-ssl verify none inter 10000
server node2 10.1.62.53:6443 check check-ssl verify none inter 10000
server node3 10.6.28.53:6443 check check-ssl verify none inter 10000
В чём может быть проблема с запуском control plane компонентов (kube-apiserver, kube-controller-manager, kube-scheduler)? Как диагностировать и исправить ошибку?
kubeadm init завершается ошибкой wait-control-plane с HAProxy VIP обычно потому, что kube-apiserver / остальные static‑pods не становятся «доступными» через указанный control-plane-endpoint — либо HAProxy помечает бэкенды down (health checks/TLS), либо kubelet/CRI не создаёт контейнеры статических подов, либо есть проблема с сертификатами (SAN для VIP), прокси или фаерволом. Проверяйте сначала состояние kubelet и контейнерного рантайма, потом логи статических манифестов и статус HAProxy (health checks, таймауты и verify/SSL). Пробный набор действий: посмотреть systemctl/journalctl для kubelet и containerd, запустить crictl с правильным socket, проверить /etc/kubernetes/manifests и HAProxy health checks, и при необходимости добавить VIP в apiserver cert через --apiserver-cert-extra-sans.
Содержание
- Почему возникает ошибка wait-control-plane при kubeadm init с HAProxy VIP
- Пошаговая диагностика: как найти причину (kubeadm init, wait-control-plane)
- Проблемы с HAProxy VIP и их исправление (health checks, timeouts)
- Проблемы с kubelet, контейнерным рантаймом и статическими подами
- Сертификаты и control-plane-endpoint: SAN для VIP
- Временные обходные пути и рекомендации перед повторным init
- Источники
- Заключение
Почему возникает ошибка wait-control-plane при kubeadm init с HAProxy VIP
kubeadm init выполняет несколько фаз: создаёт static‑pod манифесты в /etc/kubernetes/manifests (kube-apiserver, etcd, controller-manager, scheduler) и затем ждёт, пока компоненты станут «healthy». Фаза wait-control-plane периодически опрашивает /livez у kube-apiserver и локальные health‑endpoints у controller-manager и scheduler. Если kubeadm не получает ответ — оно таймаутит и выдаёт вашу ошибку.
Частые причины при использовании HAProxy VIP для control-plane-endpoint:
- HAProxy помечает backend‑ы как DOWN (health‑checks не проходят) → VIP не форвардит запросы к мастеру в момент проверки; классический «курица-яйцо» (apiserver ещё не поднялся → HAProxy считает бэкенд down → kubeadm не может проверить apiserver).
- kubelet/CRI не работают (контейнеры static‑pods не созданы) — тогда вообще нет процесса kube-apiserver на ноде.
- TLS / сертификаты не включают VIP в SAN → TLS‑проверка/health‑check ломается.
- Proxy/NO_PROXY или firewall блокируют доступ HAProxy ↔ master или kubeadm ↔ VIP.
- etcd упал/не стартовал, поэтому kube-apiserver падает сразу после запуска.
За подробностями про поведение kubeadm смотрите официальную документацию по init и HA: kubeadm init и HA с kubeadm.
Пошаговая диагностика: как найти причину (kubeadm init, wait-control-plane)
Ниже — минимальный набор проверок в порядке очередности. Выполняйте на проблемной мастер‑ноды и на HAProxy‑хосте.
- Проверка kubelet и CRI (очень часто причина — рантайм не работает)
sudo systemctl status kubelet
sudo journalctl -u kubelet -xe --no-pager -n 200
sudo systemctl status containerd
sudo journalctl -u containerd -xe --no-pager -n 200
# найти сокет CRI (частые варианты)
ps aux | grep -E "containerd|dockerd|cri-dockerd"
Если systemctl показывает, что kubelet или containerd упали — сначала устраните их. Если crictl ничего не показывает, возможно вы используете другой сокет.
- Убедиться, что вы обращаетесь к правильному CRI‑socket:
# распространённые варианты сокета
sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps -a
sudo crictl --runtime-endpoint unix:///var/run/containerd/containerd.sock ps -a
Если один из этих выводит контейнеры — используйте этот socket в дальнейших командах.
- Посмотреть статические манифесты и попытки kubeadm их создать
ls -l /etc/kubernetes/manifests
sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml
Если манифесты отсутствуют — kubeadm не смог их записать; смотрите вывод kubeadm init (–v=5).
- Проверить, создались ли контейнеры static‑pods
crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps -a | grep kube
# если появились - получить логи
crictl --runtime-endpoint unix:///run/containerd/containerd.sock logs <CONTAINER-ID>
Если контейнеров нет — смотрите логи kubelet (шаг 1).
- Проверить доступность API через VIP и напрямую
# с любой машины
curl -k -v https://10.2.26.17:6443/healthz
# напрямую к каждому backend (с HAProxy-хоста или с мастера)
curl -k -v https://10.1.62.52:6443/healthz
Если прямое обращение к node IP возвращает OK, а VIP — нет, значит HAProxy не форвардит.
- Проверить HAProxy (статус бэкендов / логи)
- Если у вас включён stats socket:
echo "show stat" | socat stdio /var/run/haproxy.sock - Либо смотреть /var/log/haproxy.log, или запустить на HAProxy‑хосте:
openssl s_client -connect 10.1.62.52:6443 -servername 10.2.26.17 </dev/null
# или отправить GET через TLS (проверка healthz)
echo -e "GET /healthz HTTP/1.1\r\nHost: 10.2.26.17\r\n\r\n" | openssl s_client -connect 10.1.62.52:6443 -servername 10.2.26.17 -quiet
- Проверить сертификаты apiserver (есть ли VIP как SAN)
sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | sed -n '/Subject Alternative Name/,$p'
- Проверить переменные proxy (HTTP(S)_PROXY, NO_PROXY)
env | egrep -i "proxy"
# NO_PROXY должен включать: VIP, IPы нод, сервис-CIDR (10.96.0.0/12)
Проблемы с proxy — частая причина, когда kubeadm видит «падение» apiserver из‑за прокси перехвата (см. обсуждение в сообществе: https://stackoverflow.com/questions/54301210/kubeadm-init-stuck-at-health-check-when-deploying-ha-kubernetes-master-with-hapr).
Проблемы с HAProxy VIP и их исправление (health checks, timeouts)
Что чаще всего ломает связку HAProxy ↔ kubeadm:
- health check в конфиге HAProxy некорректен для TLS‑бэкенда и помечает сервера down;
- слишком большие inter/fall/rise или наоборот — слишком строгая проверка;
- HAProxy ожидает TLS‑сертификат с определённым SAN и отбрасывает (если включена проверка).
Ваш текущий backend использует check check-ssl verify none inter 10000 — интервал 10s очень большой, но главное — когда kubeadm запускает apiserver, HAProxy может уже считать сервер недоступным. Рекомендации:
- Делайте health check совместимым с TLS: для HAProxy 2.x вариант
backend kube-apiserver-back
mode tcp
option tcplog
option ssl-hello-chk
balance roundrobin
server node1 10.1.62.52:6443 check inter 2000 rise 2 fall 2 verify none
server node2 10.1.62.53:6443 check inter 2000 rise 2 fall 2 verify none
server node3 10.6.28.53:6443 check inter 2000 rise 2 fall 2 verify none
ssl-hello-chk проверяет TLS‑handshake; verify none отключает валидацию сертификата (удобно при init). Если хотите проверять HTTP /healthz через TLS — настройте TCP‑check с отправкой GET после TLS‑handshake или используйте option httpchk вместе с ssl.
-
Для отладки временно уменьшите требования (rise/fall) или отключите проверку (
check→ временно убратьcheck, чтобы HAProxy всегда посылал трафик). Только для инициализации — не как постоянное решение. -
Убедитесь, что HAProxy действительно видит backend UP после запуска apiserver. На HAProxy‑хосте:
curl -k https://10.2.26.17:6443/healthz
Если ответ проходит с VIP после коррекции конфигурации — retry kubeadm init.
Практические материалы по HAProxy+Keepalived для k8s: пример развертывания — DEV.to: HA K8s cluster using Keepalived and HAProxy и разбор с kube-vip: DEV.to: kube-vip.
Проблемы с kubelet, контейнерным рантаймом и статическими подами
Если crictl ... ps -a ничего не показывает, скорее всего контейнерный рантайм не работает или вы обращаетесь к не тому сокету. Действуйте так:
- Найдите правильный сокет и используйте его:
# искать сокет
ps aux | egrep "containerd|dockerd|cri-dockerd"
sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps -a
- Если контейнерd работает, но kubelet жалуется на cgroup driver — синхронизируйте:
- для containerd: включите systemd cgroup
sudo containerd config default > /etc/containerd/config.toml
# в [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] установить:
# SystemdCgroup = true
sudo systemctl restart containerd
sudo systemctl restart kubelet
Это частая причина, описанная в issue‑ах Kubernetes (cgroup mismatch → kubelet не создаёт поды) — см. обсуждения: https://github.com/kubernetes/kubernetes/issues/76112.
- Пред‑подтянуть образы, если сеть/registry ограничены:
sudo kubeadm config images pull --cri-socket unix:///run/containerd/containerd.sock
- Если static‑pods создаются, но контейнеры падают — смотрите логи контейнера (crictl logs) для etcd и kube-apiserver; исправляйте исходные ошибки (настройки etcd, ошибки cmd‑args, permissions).
Если после этих шагов контейнеры корректно создаются и apiserver слушает на 6443 (ss -ltnp | grep 6443), но VIP по‑прежнему недоступен — смотреть HAProxy.
Сертификаты и control-plane-endpoint: SAN для VIP
kubeadm при init генерирует сертификаты apiserver; чтобы VIP работал корректно в TLS‑проверках и SNI, добавьте VIP в SAN. Пример:
kubeadm init \
--pod-network-cidr=172.16.0.0/16 \
--control-plane-endpoint "10.2.26.17:6443" \
--apiserver-cert-extra-sans=10.2.26.17 \
--upload-certs
После неудачной попытки перед повторной сборкой выполните:
sudo kubeadm reset -f
sudo rm -rf /etc/kubernetes/pki
# затем повторный kubeadm init с apiserver-cert-extra-sans
Проверить SAN в сгенерированном сертификате:
sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | sed -n '/Subject Alternative Name/,$p'
Подробности в официальной документации kubeadm: kubeadm init.
Временные обходные пути и рекомендации перед повторным init
Нужно выбрать один из практичных путей, чтобы выйти из цикла «HAProxy не форвардит → kubeadm таймаутит»:
-
Временное упрощение HAProxy: убрать/ослабить health‑checks на время init (или поставить очень мягкие параметры rise/fall), затем вернуть безопасные настройки. Быстро и работает, но менее безопасно.
-
Инициализировать первый мастер напрямую (без control-plane-endpoint / с node IP), но включить VIP в apiserver SAN:
- init на первом мастере:
kubeadm init --apiserver-cert-extra-sans=10.2.26.17 ... - затем настроить HAProxy и присоединить остальные control‑plane ноды. После этого обновите cluster-info (если нужно) — см. обсуждение о переводе non‑HA → HA: https://stackoverflow.com/questions/65505137/how-to-convert-a-kubernetes-non-ha-control-plane-into-an-ha-control-plane
-
Использовать kube-vip / keepalived, чтобы VIP был доступен на мастер‑ноде во время init (kube‑vip запускается как static pod или systemd unit и даёт VIP на ноде, пока apiserver разворачивается) — пример: https://dev.to/achu1612/ha-k8s-cluster-using-kube-vip-48jn
-
Для дебага: curl напрямую на localhost/127.0.0.1 на мастере:
curl -k https://127.0.0.1:6443/healthz
Если это работает, но VIP — нет, проблема 100% в HAProxy/сети.
Если вы повторяете init, не забывайте полностью сбросить старые pki и state через kubeadm reset и очистить /etc/kubernetes/manifests и /var/lib/etcd (при stacked etcd) — иначе старые неконсистентные данные будут мешать.
Источники
- https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/
- https://dev.to/achu1612/ha-k8s-cluster-using-keepalived-and-haproxy-439
- https://dev.to/achu1612/ha-k8s-cluster-using-kube-vip-48jn
- https://stackoverflow.com/questions/54301210/kubeadm-init-stuck-at-health-check-when-deploying-ha-kubernetes-master-with-hapr
- https://github.com/kubernetes/kubernetes/issues/76112
- https://github.com/kubernetes/kubeadm/issues/2853
- https://www.flatcar.org/docs/latest/container-runtimes/high-availability-kubernetes/
- https://medium.com/@salwan.mohamed/building-a-production-grade-high-availability-kubernetes-cluster-with-kubeadm-a-platform-3951dea0fa9a
- https://medium.com/@khaloakbari/deploying-a-highly-available-kubernetes-cluster-with-kubeadm-haproxy-and-keepalived-on-ubuntu-22-0-11e15f30ae18
- https://habr.com/ru/articles/725640/
- https://github.com/kube-vip/kube-vip/issues/251
- https://github.com/kubernetes/kubernetes/issues/125275
- https://stackoverflow.com/questions/61459820/kubeadm-init-stuck-at-waiting-for-the-kubelet-to-boot-up-the-control-plane
Заключение
Коротко: ошибка wait-control-plane при запуске kubeadm init с HAProxy VIP чаще всего связана с тем, что kube-apiserver не отвечает через указанный control-plane-endpoint — из‑за HAProxy health‑checks, проблем с kubelet/CRI или TLS‑сертификатов (SAN для VIP). Начните с проверки kubelet и containerd, затем проверьте static‑pods и логи, и только после этого отлаживайте HAProxy (health checks, verify/SSL, таймауты). Если нужно быстро обойти проблему — временно ослабьте health‑checks или инициализируйте первый мастер напрямую с добавлением VIP в apiserver SAN, затем включите HAProxy. Удачи в отладке — и если пришлёте выводы systemctl/journalctl и результат crictl, помогу разобрать их и предложу конкретные правки конфигурации.