Предположим, вам необходимо мигрировать ваш кластер k8s в другой vlan или просто сменить ip адреса. Насколько это необходимо каждый решает сам. Будем считать, что перенос нагрузки ямлами на другой кластер затруднён или у кластера специфические настройки.

В качестве начальных условий: кластер on prem с root доступом на узлы, установлен через kubeadm или kubespray, calico как cni. В нашем случае под ногами ubuntu на узлах кластера. Балансировщик kube api отсутствует. Если у вас кластер установлен the hard way , то статья вам не нужна, вы сами всё умеете.

Перемещать лучше начинать с третьей мастер ноды, т.к. бывает, что все ноды смотрят именно на первую в качестве api сервера. Последовательно перемещаем control plane ноды, далее перемещаем по одной рабочей ноде.

Перенос control plane ноды

Очищаем мастер ноду, которая будет переноситься (выполнять с любой мастер ноды или хоста с kubectl доступом до кластера):

sudo kubectl drain mycluster-m3.domain.ru --ignore-daemonsets=true --delete-emptydir-data=true

На переносимой ноде бакапим конфиги и etcd (etcd не обязательно):

sudo cp -r /etc/kubernetes{,.back}
sudo cp -r /var/lib/etcd{,.back}

Очищаем ноду от кубера (на переносимой ноде):

sudo kubeadm reset

Дальше тушим ноду, меняем адреса на гипервизоре или железке, поднимаем, проверяем наличие сетевой связности нод.

Убиваем ноду из кластера. Не обязательно, тогда при передобавлении ноды с тем же именем сохранятся метки на ноде и время создания ноды.

sudo kubectl delete node mycluster-m3.domain.ru

Правим /etc/hosts на перемещаемом хосте и остальных нодах, обновляем адрес перемещаемой ноды (kubespray любит прописывать статиком адреса нод в /etc/hosts). Тут можно использовать bash/ansible по желанию.
Если используется nginx proxy на worker нодах (вместо внешнего балансировщика kube api), то нужно поправить адреса control plane в конфиге nginx.conf, обычно живёт в /etc/nginx/nginx.conf. Аналогично bash/ansible в помощь.

Если менялся nginx.conf, рекомендуется перечитать настройки:

for pod in $(sudo kubectl get pods -n kube-system -l k8s-app=kube-nginx --no-headers -o jsonpath='{.items[*].metadata.name}');do sudo kubectl exec -n kube-system $pod -- nginx -s reload;done

Далее чиним пере��есённую мастер ноду.
Нужно поправить адрес в /etc/kubernetes/kubelet.env на перемещаемом хосте (прописать адрес ноды).
Поправить kubeadm-config в кластере. Обратить внимание на ip адрес новой ноды в разделе certSANs и адрес в controlPlaneEndpoint (тут указать адрес любой рабочей мастер ноды или адрес балансировщика kube api).

sudo kubectl -n kube-system edit cm kubeadm-config

Меняем адрес api сервера в конфигмапе cluster-info, из него kubelet получает адрес сервера для присоединения и создания конфига. Ставим адрес любой рабочей мастер ноды или адрес балансировщика.

sudo kubectl edit cm -n kube-public cluster-info

Меняем адрес (на перемещаемой ноде) api сервера в конфиге /etc/kubernetes/admin.conf, или проверяем его корректность, т.к. kubeadm использует этот конфиг для авторизации в кластере. Адрес ставим на любую рабочую мастер ноду.

Создаём токен присоединения и получаем команду присоединения (на мастер ноде):

sudo kubeadm token create --print-join-command

Создаём секрет в kube-system с сертификатами кластера:

sudo kubeadm init phase upload-certs --upload-certs

Присоединяем старую ноду как новую. Используем полученное значение из команды создания секрета, указываем в разделе --certificate-key
в --apiserver-advertise-address указываем ip адрес ноды
в --node-name имя хоста
токен получили из команды создания токена
В качестве адреса присоединения используем любой адрес рабочей мастер ноды или балансировщик.
Пример команды:

sudo kubeadm join 10.10.10.10:6443 /
--token k7or1a.1yozzaqnqylq7h5z /
--discovery-token-ca-cert-hash sha256:17eb90f281daeea2f47057216dd72182edd43c2fcd1188d64cc819239f728b0f /
--control-plane /
--certificate-key fda49053d60170092bc149624b56c977cf51263ac02d18fbe812ef7bc0833879 /
--apiserver-advertise-address 10.10.10.11 /
--node-name mycluster-m3.domain.ru

Если использовался адрес одного из мастеров, то он прописался в конфиг kubelet. Лучше его заменить на localhost.
Нужно поправить конфиг /etc/kubernetes/kubelet.conf в разделе server поставить https://localhost:6443 и перезапустить kubelet:

sudo systemctl restart kubelet.service

Осталось поправить манифесты.
Правим манифест на перемещаемом хосте /etc/kubernetes/manifests/etcd.yaml:
в разделе --initial-cluster оставляем только адрес перемещённой ноды (можно взять за образец строку из манифеста в /etc/kubernetes.back), убираем раздел --initial-cluster-state=existing
Под с etcd перезапустится, контролируем корректность запуска.
Правим манифест на перемещаемом хосте /etc/kubernetes/manifests/kube-apiserver.yaml если были расхождения с бакапом в /etc/kubernetes.back (например дополнительные опции запуска).

Перенос k8s из одной control plane ноды

Если у вас кубер состоит из одного узла, то проблем чуть больше.
Останавливаем kubelet и бакапим манифесты и etcd:

sudo systemctl disable kubelet.service --now
sudo cp -r /etc/kubernetes{,.back}
sudo cp -r /var/lib/etcd{,.back}

Меняем адреса у хоста на гипервизоре или железке, правим /etc/hosts на перемещаемом хосте при необходимости.

Правим адрес в конфиге kubelet и манифестах кубера (apiserver и etcd).
Файлы:
/etc/kubernetes/kubelet.env
/etc/kubernetes/manifests/kube-apiserver.yaml
/etc/kubernetes/manifests/etcd.yaml

Смотрим прошлый сертификат у apiserver, обратить внимание на раздел X509v3 Subject Alternative Name и Subject:

sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text

Удаляем старый сертификат от apiserver (файл /etc/kubernetes/ssl/apiserver.*) и создаём новый сертификат. Учитываем cidr у сервисов (можно посмотреть в манифесте apiserver, настройка service-cidr) и extra sans (берём из старого сертификата, но убираем "DNS:" и "IP Address:", просто список через запятую). В качестве ip адреса прописываем новый адрес ноды, старый убираем.
Пример:

sudo kubeadm init phase certs apiserver /
--service-cidr "10.233.0.0/16" /
--apiserver-cert-extra-sans "kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster.local,lb-apiserver.kubernetes.local,localhost,mycluster,mycluster.domain.ru,10.233.0.1,10.10.10.10,127.0.0.1"

Всё, осталось стартовать kubelet:

sudo systemctl enable kubelet.service --now

Если надумаете позже присоединять ещё ноды, то стоит поправить конфиги cluster-info и kubeadm-config в кластере. Смотреть в предыдущем разделе.

В качестве бонуса или в случае отсутствия kubeadm. Возможно создать новые сертификаты вручную через openssl:

sudo openssl req /
-x509 /
-newkey rsa:2048 /
-keyout /etc/kubernetes/pki/apiserver.key /
-sha256 /
-out /etc/kubernetes/pki/apiserver.crt /
-days 1500 /
-noenc /
-CA /etc/kubernetes/pki/ca.crt /
-CAkey /etc/kubernetes/pki/ca.key /
-subj "/CN=kube-apiserver" /
-addext "basicConstraints=critical,CA:FALSE" /
-addext "keyUsage=critical,digitalSignature,keyEncipherment" /
-addext "extendedKeyUsage=serverAuth" /
-addext "subjectAltName=DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:lb-apiserver.kubernetes.local, DNS:localhost, DNS:mycluster, DNS:mycluster.domain.ru, IP:10.233.0.1, IP:10.10.10.10, IP:127.0.0.1"

Перенос worker ноды

Кордоним и очищаем ноду, смотрим что нагрузка переместилась на другие ноды:

sudo kubectl drain mycluster-n1.domain.ru --ignore-daemonsets=true --delete-emptydir-data=true

Бакапим конфиги:

sudo cp -r /etc/kubernetes{,.back}

Очищаем ноду от кубера (на переносимой ноде):

sudo kubeadm reset

Убираем ноду из списка нод:

sudo kubectl delete node mycluster-n1.domain.ru

Меняем адреса у хоста на гипервизоре или железке, правим /etc/hosts на всех нодах, если там прописаны адреса статикой.

Правим /etc/kubernetes/kubelet.env на перемещаемом хосте, меняем адрес ноды.

Возвращаем на место манифест nginx proxy из папки /etc/kubernetes.back/manifests если используется nginx proxy. Команда kubeadm reset очищает папку с манифестами.

На мастер ноде создаём токен присоединения. Если несколько нод перемещаем одновременно, то можно использовать один токен, время жизни токена два часа.

sudo kubeadm token create --print-join-command

Присоединяем старую ноду как новую, пример команды (получена из предыдущего шага, нужно поменять ip адрес на адрес мастер ноды или балансировщика):

sudo kubeadm join 10.10.10.10:6443 /
--token 40j37s.bupenjhou2bx5ydv /
--discovery-token-ca-cert-hash sha256:17eb90f281daeea2f47057216dd72182edd43c2fcd1188d64cc819239f728b0f

Если использвался адрес одного из мастеров, то он прописался в конфиг kubelet. Лучше его заменить на localhost если используется nginx-proxy для kube api. Правим /etc/kubernetes/kubelet.conf и рестартим kubelet:

sudo systemctl restart kubelet.service

Заключение

В целом переезд кластера происходит без простоя и незаметен для пользователей (при наличии нескольких подов на сервис). Если у вас используется deckhouse kubernetes platform, то там переезд осуществляется проще. При наличии пожеланий, позже можно расширить статью на этот случай.

В целом статья написана больше для обучения и лучшего понимания устройства кластера для желающих посмотреть под капот. Если есть методы проще и удобнее, прошу поделиться в комментариях.