Замена LCM-узла и его дисков

В режиме высокой доступности LCM развёрнут на трёх узлах controller+worker, образующих Kubernetes-кластер k0s со встроенным etcd. Каждый узел одновременно является участником кворума etcd, управляющим узлом Kubernetes и (при использовании встроенного хранилища) узлом Ceph с OSD на диске sdb; система и данные k0s (etcd и containerd) размещаются на диске sda. Данный раздел описывает процедуру замены одного узла и применяется в двух случаях:

  • отказ узла — узел вышел из строя без возможности восстановления (отказ оборудования, повреждение системного диска);

  • плановая замена дисков — работоспособный узел выводится из эксплуатации планово, например для замены дисков sda и sdb на более производительные (с защитой от потери питания, PLP). В этом случае с узла сначала корректно вытесняется рабочая нагрузка, после чего он проходит ту же процедуру повторного ввода в кластер.

Этапы вывода узла из кластера различаются для этих двух случаев (см. Вывод узла из кластера); последующий ввод нового узла одинаков. Замена диска sda подразумевает переустановку ОС, поэтому в обоих случаях узел выводится из кластера и вводится заново.

Предупреждение

Кворум etcd сохраняется, пока работоспособны не менее двух из трёх узлов. Заменяйте узлы строго по одному, полностью завершив процедуру и дождавшись восстановления кластера, прежде чем выводить из эксплуатации следующий узел. Если одновременно отказали два или три узла, кворум etcd потерян — в этом случае замена узла не применяется, выполняйте восстановление из резервной копии (см. Аварийное восстановление).

Примечание

Новый узел рекомендуется вводить под тем же FQDN, IP-адресом и DNS-записями, что и заменяемый узел. Имена узлов зафиксированы в параметре control_plane файла lcm-config.yaml и в SAN-сертификатах кластера, а VIP управляется keepalived — повторное использование тех же имени и адреса исключает рассогласование конфигурации.

Особый случай: отказ seed-узла

Seed-узел — это узел, с которого запускаются installer.sh и task (на нём находятся каталог инсталлятора, lcm-config.yaml, приватный SSH-ключ и инструменты k0sctl / gomplate). В k0s это не выделенная роль: все три узла равноправны (controller+worker), поэтому при отказе seed-узла кластер продолжает работать на двух оставшихся узлах с сохранением кворума. Дополнительно к обычной замене требуется восстановить рабочее окружение оператора на одном из работоспособных узлов.

Если отказал не seed-узел, пропустите этот раздел и перейдите к Оценка состояния кластера.

Если отказал seed-узел, выполните следующие действия:

  1. Выберите любой работоспособный узел в качестве нового управляющего узла — все дальнейшие команды выполняются на нём. Узел является контроллером, поэтому kubectl доступен через sudo k0s kubectl.

  2. Восстановите lcm-config.yaml из сохранённой в кластере копии:

    $ sudo k0s kubectl get configmap lcm-config -n k0s-system \
        -o jsonpath='{.data.lcm-config\.yaml}' > lcm-config.yaml
    

    Важно

    Параметр keepalived_passwd в ConfigMap не сохраняется — добавьте его в lcm-config.yaml вручную. Проверьте также остальные значения конфигурации.

  3. Восстановите доступ по SSH ко всем узлам. k0sctl использует приватный ключ ~/.ssh/id_rsa управляющего узла (параметр keyPath в шаблоне k0sctl). Восстановите ключ seed-узла из резервной копии в ~/.ssh/id_rsa либо сгенерируйте новую пару и добавьте публичный ключ в ~/.ssh/authorized_keys на всех работоспособных узлах и на узле-замене.

  4. Разверните каталог инсталлятора на управляющем узле (из дистрибутива или резервной копии) и поместите рядом восстановленный lcm-config.yaml.

После восстановления окружения выполните обычную процедуру замены, рассматривая отказавший seed-узел как обычный отказавший узел: вернитесь к Оценка состояния кластера, далее используйте вкладку «Отказ узла» в разделе Вывод узла из кластера. Подготовку узла-замены выполняет installer.sh — он же установит task и инструменты на управляющий узел.

Совет

Чтобы такое восстановление проходило быстро, заранее храните вне seed-узла резервную копию приватного SSH-ключа (~/.ssh/id_rsa) и дистрибутив инсталлятора. lcm-config.yaml восстанавливается из кластера, но keepalived_passwd в нём не хранится — держите его отдельно.

Оценка состояния кластера

Перед заменой убедитесь, что из эксплуатации выводится только один узел и кворум сохранён. Далее выполните следующие действия:

  1. Определите заменяемый узел и его состояние:

    $ kubectl get nodes -o wide
    
  2. Проверьте состояние участников etcd. Должны присутствовать три участника:

    $ sudo k0s etcd member-list
    
  3. При использовании встроенного хранилища проверьте состояние Ceph и определите, какой OSD (osd.<N>) размещён на заменяемом узле:

    $ kubectl -n ceph-cluster exec deploy/rook-ceph-tools -- ceph status
    $ kubectl -n ceph-cluster exec deploy/rook-ceph-tools -- ceph osd tree
    

    Идентификатор OSD потребуется при выводе узла из кластера (см. Вывод узла из кластера).

Вывод узла из кластера

Последовательность вывода зависит от состояния узла. Выберите соответствующую вкладку.

  1. Запретите планирование новых подов на узел и вытесните с него существующие:

    $ kubectl cordon <fqdn-узла>
    $ kubectl drain <fqdn-узла> --ignore-daemonsets --delete-emptydir-data --timeout=10m
    

    Убедитесь, что на узле не осталось рабочих подов, кроме подов DaemonSet:

    $ kubectl get pods -A -o wide --field-selector spec.nodeName=<fqdn-узла>
    
  2. При использовании встроенного хранилища выведите OSD узла из Ceph и дождитесь стабилизации:

    $ kubectl -n ceph-cluster exec deploy/rook-ceph-tools -- ceph osd out osd.<N>
    $ kubectl -n ceph-cluster exec deploy/rook-ceph-tools -- ceph status
    

    Предупреждение

    При трёх узлах и размере пула size=3 Ceph не может создать третью копию данных на другом узле (других узлов нет), поэтому после вывода OSD кластер остаётся в состоянии HEALTH_WARN (undersized / degraded) до возврата узла. При min_size=2 данные доступны и ввод-вывод не блокируется.

    Не выводите из эксплуатации второй узел или второй OSD одновременно.

    Подробнее о предупреждении too many PGs per OSD, которое усиливается при уменьшении числа OSD, — см. Предупреждение «too many PGs per OSD».

  3. Выведите узел из etcd. Команда выполняется с работоспособного узла с указанием peer-адреса заменяемого узла:

    $ sudo k0s etcd leave --peer-address <ip-узла>
    
  4. Удалите объект Node из Kubernetes:

    $ kubectl delete node <fqdn-узла>
    
  5. Окончательно удалите OSD из Ceph (диск sdb будет заменён на новый):

    $ kubectl -n ceph-cluster exec deploy/rook-ceph-tools -- ceph osd purge <N> --yes-i-really-mean-it
    $ kubectl -n ceph-cluster delete deployment rook-ceph-osd-<N> --ignore-not-found
    

Примечание

После удаления узла кластер работает на двух узлах. Реплики StatefulSet с жёстким anti-affinity «по одному поду на узел» (Ceph mon и OSD, redis-ha, mariadb-galera, garage и другие), которые размещались на выведенном узле, перейдут в состояние Pending и не будут запланированы до возврата третьего узла. Это ожидаемо: кворум и доступность данных обеспечиваются оставшимися репликами.

Данные не привязаны к узлу — тома хранятся в Ceph (RBD/CephFS) и монтируются по сети, поэтому поды свободно переезжают между узлами без копирования данных; OSD на новом узле при этом не переносится, а создаётся заново. При возврате узла планировщик сразу разместит на нём ожидающие реплики.

Чтобы отфильтровать затронутые поды (на выведенном узле и неназначенные), подставьте короткое имя узла вместо <узел>:

$ kubectl get pods -A -o wide | awk 'NR==1 || ($4!="Running" && $4!="Completed" && ($8 ~ /<узел>/ || $8=="<none>"))'

Подготовка нового узла

Важно

Если заменяется seed-узел, то есть узел, на котором находятся каталог инсталлятора, lcm-config.yaml и инструменты, на новом узле не будет ни инсталлятора, ни task. Предварительно восстановите рабочее окружение оператора на работоспособном узле — см. Особый случай: отказ seed-узла.

Подготовьте новый сервер так же, как при первичной установке (см. требования в разделе Требования к инфраструктуре), под тем же FQDN, IP-адресом и DNS-записями, что и заменяемый узел. Убедитесь, что подготовленный сервер удовлетворяет следующим требованиям:

  • установлена ОС на диск sda, диск sdb присутствует и не содержит разделов. Если новый узел повторно использует ранее работавший диск sdb с остатками прежней разметки или метаданных OSD, Rook не примет его — заранее приведите диск к чистому состоянию (см. Ceph не создаёт OSD: диск не подготовлен);

  • создан непривилегированный пользователь (например, kolla) с правом sudo без пароля;

  • публичный SSH-ключ с первого LCM-узла добавлен в ~/.ssh/authorized_keys нового узла;

  • отключён swap;

  • настроены сетевые интерфейсы, IP-адрес и маршруты идентично заменяемому узлу.

Остальную настройку ОС (sysctl, пакеты, SELinux, ulimits, chrony/NTP, resolv.conf, auditd) выполнять вручную не нужно. С seed-узла из каталога инсталлятора запустите:

$ ./installer.sh lcm-k0s/lcm-config.yaml

Скрипт по SSH применяет ОС-настройки на всех узлах из control_plane, включая новый, и устанавливает инструменты (task, yq) на seed-узле.

Примечание

Утилита task (и весь процесс установки) запускается только на seed-узле; на самом узле-замене её устанавливать не требуется. Подготовка нового узла и установка k0s на него выполняются с seed-узла по SSH (installer.sh и k0sctl).

Ввод нового узла в кластер

Новый узел присутствует в control_plane (под тем же именем, что и заменяемый узел), поэтому повторное применение манифеста k0sctl развернёт на нём k0s и присоединит его к кластеру как controller+worker.

  1. Из каталога инсталлятора повторно запустите установку k0s-кластера:

    $ task k0s-install-multi-node
    

    Задача формирует манифест k0sctl и выполняет k0sctl apply: устанавливает k0s на новый узел, загружает на него airgap-бандл образов в /var/lib/k0s/images/ и присоединяет узел к etcd. Операция идемпотентна — узлы, уже входящие в кластер, не переустанавливаются. Дополнительно задача повторно применяет базовый CNI (calico) и обновляет kubeconfig; инфраструктурные и прикладные чарты при этом не затрагиваются.

  2. Дождитесь, пока узел перейдёт в статус Ready, а в etcd снова будет три участника:

    $ kubectl get nodes
    $ sudo k0s etcd member-list
    

Распаковка образов на новом узле

k0sctl загружает airgap-бандл на узел, но не распаковывает образы в подсистему хранения образов containerd. При первом запуске подов на новом узле распаковка крупных образов может не уложиться в таймаут kubelet и привести к ошибке CreateContainerError. Заранее распакуйте образы на новом узле по процедуре из раздела Поды не стартуют: образы из airgap-бандла не распакованы.

После распаковки поды, назначенные планировщиком на новый узел, перейдут в статус Running без ошибок создания контейнеров.

Примечание

Пока образы не распакованы, а mon и OSD нового узла не подняты, Ceph остаётся в деградированном состоянии. Из-за этого отдельные поды (в том числе на других узлах) могут показывать CreateContainerError и временные ошибки монтирования томов вида MountVolume.MountDevice failed context deadline exceeded. Это ожидаемо и устраняется после распаковки образов и восстановления Ceph (см. Восстановление OSD на новом узле).

Восстановление OSD на новом узле

Примечание

Шаг выполняется только при использовании встроенного хранилища (multi-node, Rook-Ceph).

На чистом диске sdb нового узла оператор Rook автоматически создаёт новый OSD и запускает перебалансировку данных. Дождитесь, пока кластер вернётся в состояние HEALTH_OK, а все PG — в active+clean:

$ kubectl -n ceph-cluster exec deploy/rook-ceph-tools -- ceph status
$ kubectl -n ceph-cluster exec deploy/rook-ceph-tools -- ceph osd tree

Если новый OSD не появляется (на диске остались метаданные предыдущего OSD), приведите диск к чистому состоянию по процедуре из раздела Ceph не создаёт OSD: диск не подготовлен (скрипт zap_rook_disk.sh с проверками безопасности), затем перезапустите оператор Rook, чтобы он заново просканировал устройства и создал OSD:

$ kubectl -n rook-ceph-system rollout restart deploy/rook-ceph-operator

Проверка результата

Выполните следующие шаги проверки:

  1. Все три узла в статусе Ready:

    $ kubectl get nodes
    
  2. В etcd три участника, все отвечают:

    $ sudo k0s etcd member-list
    
  3. Ceph в состоянии HEALTH_OK, три OSD в строю (multi-node):

    $ kubectl -n ceph-cluster exec deploy/rook-ceph-tools -- ceph status
    
  4. Все поды в статусе Running или Completed:

    $ kubectl get pods -A | grep -vE 'Running|Completed'
    
  5. Веб-интерфейсы сервисов LCM (GitLab, NetBox, Nexus, Vault) открываются и отвечают.

При возникновении ошибок запуска подов на новом узле обратитесь к разделу Диагностика и устранение неполадок LCM.