DevOps

Ошибка 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 выполняет несколько фаз: создаёт 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‑хосте.

  1. Проверка 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 ничего не показывает, возможно вы используете другой сокет.

  1. Убедиться, что вы обращаетесь к правильному 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 в дальнейших командах.

  1. Посмотреть статические манифесты и попытки kubeadm их создать
ls -l /etc/kubernetes/manifests
sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml

Если манифесты отсутствуют — kubeadm не смог их записать; смотрите вывод kubeadm init (–v=5).

  1. Проверить, создались ли контейнеры 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).

  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 не форвардит.

  1. Проверить 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
  1. Проверить сертификаты apiserver (есть ли VIP как SAN)
sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | sed -n '/Subject Alternative Name/,$p'
  1. Проверить переменные 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 может уже считать сервер недоступным. Рекомендации:

  1. Делайте 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.

  1. Для отладки временно уменьшите требования (rise/fall) или отключите проверку (check → временно убрать check, чтобы HAProxy всегда посылал трафик). Только для инициализации — не как постоянное решение.

  2. Убедитесь, что 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 ничего не показывает, скорее всего контейнерный рантайм не работает или вы обращаетесь к не тому сокету. Действуйте так:

  1. Найдите правильный сокет и используйте его:
# искать сокет
ps aux | egrep "containerd|dockerd|cri-dockerd"
sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps -a
  1. Если контейнер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.

  1. Пред‑подтянуть образы, если сеть/registry ограничены:
sudo kubeadm config images pull --cri-socket unix:///run/containerd/containerd.sock
  1. Если 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 таймаутит»:

  1. Временное упрощение HAProxy: убрать/ослабить health‑checks на время init (или поставить очень мягкие параметры rise/fall), затем вернуть безопасные настройки. Быстро и работает, но менее безопасно.

  2. Инициализировать первый мастер напрямую (без control-plane-endpoint / с node IP), но включить VIP в apiserver SAN:

  1. Использовать kube-vip / keepalived, чтобы VIP был доступен на мастер‑ноде во время init (kube‑vip запускается как static pod или systemd unit и даёт VIP на ноде, пока apiserver разворачивается) — пример: https://dev.to/achu1612/ha-k8s-cluster-using-kube-vip-48jn

  2. Для дебага: 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) — иначе старые неконсистентные данные будут мешать.


Источники


Заключение

Коротко: ошибка 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, помогу разобрать их и предложу конкретные правки конфигурации.

Авторы
Проверено модерацией
Модерация
Ошибка wait-control-plane при kubeadm init с HAProxy VIP