Как восстановить работу кластера etcd из резервной копии

В РОСА Кубис можно создать резервную копию etcd с помощью объекта NCI. Резервное копирование данных кластера etcd может понадобиться для восстановления кластера. Рекомендуется выполнять резервное копирования в период наименьшей нагрузки на кластер.

Создание резервной копии etcd

Для создания резервной копии etcd нужно:

  1. подготовить скрипт для резервного копирования, например, с именем etcdbackup.sh. Пример скрипта etcdbackup.sh:
#!/bin/sh

# Проверка запуска из-под root
if [[ $EUID -ne 0 ]]; then
  echo "This script must berun as root"
  exit 1
fi

# Проверка установки etcdctl для выполнения резервного копирования
if which etcdctl > /dev/null; then
  echo etcdctl found
else
fi

function usage {
  echo 'Path to backup dir required: ./cluster-backup.sh <path-to-backup-dir>'
  exit 1
}

# If the first argument is missing, or it is an existing file, then print usage and exit
if [ -z "$1" ] || [ -f "$1" ]; then
  usage
fi

# Проверка существования манифестов
function checkManifests {
  echo 'No manifsts found in /etc/kubernetes/manifests'
  exit 1
}

if [ -z "$(ls /etc/kubernetes/manifests/)" ];then
  checkManifests
fi

function checkStatus () {
  status=$?
  if test $status -eq 0
  then
    echo "passed $1"
  else
    echo "failed $1"
    exit 1
  fi
}

# Объявление переменных
archive="$(hostname)-$(date '+%F_%H%M%S').tar.gz" # Имя резервной копии etcd
backupdate="$1/backup/"  # Расположение резервной копии etcd
mkdir -p $backupdate/manifests # Директория для резервной копии манифестов etcd
mkdir -p $backupdate/etcd # Директория для резервной копии etcd

# Получение данных для подключения и создание резервной копии (snapshot)
etcdCTL_API=3 \
etcdCTL_CACERT='/etc/kubernetes/pki/etcd/ca.crt' \
etcdCTL_CERT='/etc/kubernetes/pki/etcd/server.crt' \
etcdCTL_KEY='/etc/kubernetes/pki/etcd/server.key' \
etcdCTL_ENDPOINTS='https://XX.XX.XX.XX:2379' \
/usr/local/bin/etcdctl snapshot save $backupdate/etcd/etcd.snapshot ;

checkStatus "etcd backup"

cp -r /etc/kubernetes/manifests/* $backupdate/manifests/
checkStatus "Create copies of static manifests"

echo "Manifests saved at $backupdate/manifests"
# Бэкапирование secrets_encryption.yaml необходимо для кластеров с расширенными параметрами безопасности (secure=true)
cp /etc/kubernetes/secrets_encryption.yaml $backupdate/etcd/ >/dev/null 2>&1
echo "Create copies of secrets_encryption.yaml"

echo "Archiving backup...."
tar -zcf $1/$archive $backupdate 2> /dev/null
checkStatus "archiving file"
echo "Backup archiving finished"
chmod 666 $1/$archive

echo "Backup archive is located in $1/archive"

# Удаление архивов с резервной копией старше 3-х дней
find /tmp/ -name "*.tar.gz" -mtime +3 -delete
checkStatus "removing old backups"

echo Backup finished
exit 0
  1. в графическом интерфейсе кластера открыть страницу "Конфигурация узлов" раздела "Администрирование", нажать + Добавить NCI. На странице добавления NCI:
  • задать имя, например etcdbackup, и в селекторе узлов из выпадающего списка выбрать ключ лейбла node-role.kubernetes.io/control-plane. Значение для лейбла не устанавливать.
  • в блоке "Настраиваемые разделы" выбрать "Файлы и директории" и "Пакеты для установки", нажать Продолжить (рисунок 129);

Рисунок 129 ‒ Блок "Настраиваемые разделы"

  • в открывшемся окне на шаге конфигурации раздела "Пакеты для установки" нажать + и указать пакет etcdhelper (рисунок 130), выбрать целевое состояние пакета Present (рисунок 131);

Рисунок 130 ‒ Указание пакетов

Рисунок 131 ‒ Выбор целевого пакета

  • нажать Добавить и затем Продолжить, чтобы перейти к разделу "Файлы и директории";
  • на шаге "Файлы и директории" нажать + и в поле "Путь к файлу или директории" прописать путь до создаваемого файла со скриптом резервного копирования. В типе файла/директории должно быть выбрано "Файл";
  • в поле права доступа указать необходимые права, например 0755, что обеспечит владельцу полный доступ к файлу, группе владельца, другим пользователям ‒ доступ на чтение и выполнение. Владелец и группа должны быть root;
  • в поле "Содержимое файла" вставить содержимое подготовленного скрипта из файла etcdbackup.sh, нажать Добавить (рисунки 132‒133);

Рисунок 132 ‒ Раздел "Файлы и директории"

Рисунок 133 ‒ Добавленный скрипт

  • нажать + и в поле "Путь к файлу или директории" прописать путь до файла с расписанием запуска резервного копирования;
  • в поле права доступа указать необходимые права, например 0600, что обеспечит правами только владельца файла на чтение и запись. (владелец и группа должны быть root);
  • в поле "Содержимое файла" указать конфигурацию запуска резервного копирования: расписание в cron-формате, путь до файла со скриптом и путь до создаваемой резервной копии etcd. В поле "Содержимое файла" после записи конфигурации запуска требуется выполнить перенос строки;
  • нажать Добавить (рисунки 134‒135).

Рисунок 134 ‒ Добавление скрипта

Рисунок 135 ‒ Добавленный скрипт

Следует обратить внимание, что рекомендуется хранить резервную копию etcd в сетевой файловой системе не на Control Plane-узлах, а, например, в /nfs/share. Это может быть полезным в случае недоступности Control Plane-узлов.

Пример конфигурации запуска:

30 18 * * * /opt/etcdbackup.sh /tmp/
  • нажать Завершить.

Пример NCI:

apiVersion: node.shturval.tech/v1beta2
kind: NodeConfigItem
metadata:
  annotations:
    shturval.tech/name: etcdbackup
  finalizers:
  ‒ node.shturval.tech/finalizer
  name: etcdbackup
spec:
  packages:
    ‒ name: etcdhelper
      state: present
  files:
  ‒ content: |
#!/bin/sh

if [[ $EUID -ne 0 ]]; then
  echo "This script must berun as root"
  exit 1
fi

if which etcdctl > /dev/null; then
  echo etcdctl found
else
  echo etcdctl not found
fi

function usage {
  echo 'Path to backup dir required: ./cluster-backup.sh <path-to-backup-dir>'
  exit 1
}

# If the first argument is missing, or it is an existing file, then print usage and exit
if [ -z "$1" ] || [ -f "$1" ]; then
  usage
fi

function checkManifests {
  echo 'No manifsts found in /etc/kubernetes/manifests'
  exit 1
}

if [ -z "$(ls /etc/kubernetes/manifests/)" ];then
  checkManifests
fi

function checkStatus () {
  status=$?
  if test $status -eq 0
  then
    echo "passed $1"
  else
    echo "failed $1"
    exit 1
  fi
}

archive="$(hostname)-$(date '+%F_%H%M%S').tar.gz"
backupdate="$1/backup/"
mkdir -p $backupdate/manifests
mkdir -p $backupdate/etcd

etcdCTL_API=3 \
etcdCTL_CACERT='/etc/kubernetes/pki/etcd/ca.crt' \
etcdCTL_CERT='/etc/kubernetes/pki/etcd/server.crt' \
etcdCTL_KEY='/etc/kubernetes/pki/etcd/server.key' \
etcdCTL_ENDPOINTS='https://XX.XX.XX.XX:2379' \
/usr/local/bin/etcdctl snapshot save $backupdate/etcd/etcd.snapshot ;

checkStatus "etcd backup"

cp -r /etc/kubernetes/manifests/* $backupdate/manifests/
checkStatus "Create copies of static manifests"

echo "Manifests saved at $backupdate/manifests"

cp /etc/kubernetes/secrets_encryption.yaml $backupdate/etcd/ >/dev/null 2>&1
echo "Create copies of secrets_encryption.yaml"

echo "Archiving backup...."
tar -zcf $1/$archive $backupdate 2> /dev/null
checkStatus "archiving file"
echo "Backup archiving finished"
chmod 666 $1/$archive

echo "Backup archive is located in $1/archive"

find /tmp/ -name "*.tar.gz" -mtime +3 -delete
checkStatus "removing old backups"

echo Backup finished
exit 0
    group: root
    mode: "0755"
    owner: root
    path: /opt/etcdbackup.sh
    type: file
  ‒ content: "30 18 * * * /opt/etcdbackup.sh /tmp/"
    group: root
    mode: "0600"
    owner: root
    path: /var/spool/cron/root
    type: file
  nodeconfigselector:
    node-role.kubernetes.io/control-plane: "
  priority: 100

В результате успешного выполнения скрипта по расписанию для каждого Control Plane-узла будет создана резервная копия etcd.

Восстановить резервную копию etcd

Чтобы восстановить из резервной копии etcd, для каждого Control Plane-узла необходимо выполнить восстановление:

  1. на Control Plane-узле переместить манифест пода с именем файла kube-apiserver.yaml из etc/kubernetes/manifests/ в другую папку, например /root, чтобы остановить работу API-сервера. После перемещения необходимо дождаться удаления пода (результат команды — на рисунке 136):

Рисунок 136 ‒ Результат команды

# Перемещение пода в папку /root/
mv /etc/kubernetes/manifests/kube-apiserver.yaml  /root/
# Получение информации о поде API-сервера
crictl ps | grep api
  1. выполнить восстановление из резервной копии etcd (результат — на рисунке 137):

Рисунок 137 ‒ Результат команды

export etcdCTL_API=3
/usr/local/bin/etcdctl --data-dir /var/lib/etcd snapshot restore /tmp/backup/etcd/etcd.snapshot

где вместо /var/lib/etcd указать каталог, который будет создан в процессе восстановления резервной копии etcd, а вместо /tmp/backup/etcd/etcd.snapshot ‒ путь до резервной копии. Следует обратить внимание, что, если указывается каталог /var/lib/etcd, нужно удалить его до выполнения восстановления из резервной копии etcd.

Важно ‒ В случае восстановления etcd на Control Plane-узлах нового созданного кластера необходимо восстановить secrets_encryption.yaml из архива. Сохранение secrets_encryption.yaml требуется для кластеров с расширенными параметрами безопасности (secure=true):

cp status/backup/etcd/secrets_encryption.yaml /etc/kubernetes/

где вместо status/backup/etcd/secrets_encryption.yaml указать путь до сохраненного файла secrets_encryption.yaml.

  1. перезапустить под API-сервера, переместив файл kube-apiserver.yaml обратно в etc/kubernetes/manifests/ на узле (результат — на рисунке 138):
mv /root/kube-apiserver.yaml /etc/kubernetes/manifests/
# Получение информации о поде API-сервера
crictl ps | grep api

Рисунок 138 ‒ Результат команды

Рекомендуется перезапустить kube-scheduler, kube-controller-manager на Control Plane-узлах. Для этого нужно поочередно переместить файлы с манифестами в папку root, проверить, что под удален, после чего вернуть манифест обратно в /etc/kubernetes/manifests/.

Команда перезапуска kube-scheduler:

# Перемещение пода в папку /root/
mv /etc/kubernetes/manifests/kube-scheduler.yaml /root/
# Получение информации о поде kube-scheduler. дождаться удаления пода
crictl ps | grep scheduler
# Возвращение пода kube-scheduler
mv /root/kube-scheduler.yaml /etc/kubernetes/manifests/

Команда перезапуска kube-controller-manager:

# Перемещение пода в папку /root/
mv /etc/kubernetes/manifests/kube-controller-manager.yaml  /root/
# Получение информации о поде kube-controller-manager. дождаться удаления пода
crictl ps | grep controller
# Возвращение пода kube-controller-manager
mv /root/kube-controller-manager.yaml /etc/kubernetes/manifests/

Затем следует перезапустить kubelet:

sudo systemctl restart kubelet
sudo systemctl status kubelet

Восстановление работы кластера etcd с помощью etcd-helper

Установка модуля

Восстановление работы кластера etcd с помощью модуля etcd-helper возможно только в высокодоступном кластере (HA):

  • при недоступности узла кластера etcd;
  • при изменении IP-адреса Control Plane узла;
  • при изменении IP-адресов всех Control Plane узлов.

Для начала работы с модулем:

  1. установить пакет с модулем etcd-helper, содержащий утилиты edtcdctl, etcdutl, etcdhelper:
yum install etcdhelper

Модуль etcd-helper будет загружен в /usr/local/bin (рисунок 139).

Рисунок 139 ‒ Загузка модуля etcd-helper

  1. чтобы выполнить запрос к кластеру etcd, запустить etcdctl с необходимыми параметрами, с помощью команды etcdhelper exec. Требуемые параметры будут получены из /etc/kubernetes/manifests/etcd.yaml.

Команда (результат — на рисунке 140):

Рисунок 140 ‒ Результат команды

/usr/local/bin/etcdhelper exec -- member list -w table

где параметр "--" необходим для корректной передачи ключей в скрипте.

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

Если IP-адрес и имя API-сервера не изменились, но на Control Plane-узле повреждены данные, нужно пересоздать узел в кластере etcd, выполнив команду (результат — на рисунке 141):

/usr/local/bin/etcdhelper rejoin

Рисунок 141 ‒ Результат команды

Важно ‒ Если контейнер etcd на узле в статусе "Running", то команда etcdhelper rejoin не может быть выполнена.

В результате выполнения команды произойдет:

  • удаление нерабочего узла из кластера etcd;
  • остановка Kubelet;
  • удаление контейнера etcd;
  • переименование каталога с данными etcd;
  • добавление нового узла в кластер etcd;
  • запуск Kubelet.

Восстановление при изменении IP-адреса Control Plane-узла

Если IP-адрес одного из Control Plane-узлов изменен, то можно восстановить работу кластера etcd, использовав команду (результат — на рисунке 142):

/usr/local/bin/etcdhelper ip --nodeip=ВВЕДИТЕ-НОВЫЙ-IP-АДРЕС-УЗЛА

Рисунок 142 ‒ Результат команды

В результате команды будет выполнено:

  • резервное копирование манифестов и сертификатов;
  • изменение IP-адреса узла в манифестах;
  • удаление сертификатов etcd, за исключением сертификата CA;
  • генерация сертификатов, в том числе для API-сервера;
  • изменение IP-адреса узла в кластере etcd.

Восстановление при изменении IP-адресов всех Control Plane-узлов

Для восстановления работоспособности кластера etcd нужно последовательно выполнить действия:

  1. на первом Control Plane-узле выполнить команду для подготовки конфигурации после смены IP:
/usr/local/bin/etcdhelper prepare --nodeip=ВВЕДИТЕ-НОВЫЙ-IP-АДРЕС-УЗЛА
  1. добавить опцию --force-new-cluster в манифест /etc/kubernetes/manifests/etcd.yaml;
  2. перезагрузить первый Control Plane-узел;
  3. отредактировать запись об узле в кластере etcd:
# Посмотреть ID узла
/usr/local/bin/etcdhelper exec -- member list -w table
# Обновить запись об узле 
/usr/local/bin/etcdhelper exec -- member update ВВЕДИТЕ-ID-УЗЛА --peer-urls=https://ВВЕДИТЕ-НОВЫЙ-IP-АДРЕС-УЗЛА:2380
  1. удалить опцию --force-new-cluster из манифеста /etc/kubernetes/manifests/etcd.yaml;
  2. перезагрузить первый узел;
  3. убедиться, что кластер Kubernetes работает;
  4. при восстановлении кластера управления убедиться, что все поды в неймспейсе shturval-backend запущены. Если shturval-backend-redis или shturval-backend-valkey не запущены, выполнить принудительное удаление для перезапуска;
  5. при восстановлении кластера (клиентского, управления) с провайдером Shturval v2 подготовить новые хосты и добавить их в экземпляр провайдера;
  6. в графическом интерфейсе кластера открыть раздел "Администрирование" и перейти на страницу "Управление узлами", нажать на "Конфигурация группы Control Plane". На вкладке "Конфигурация ClusterAPI" установить "1" в поле "Запрошено реплик";

Примеры команды для применения в интерфейсе командной строки:

# Изменить количество реплик в KubeadmControlPlane 
kubectl edit KubeadmControlPlane -n ИМЯ-КЛАСТЕРА ИМЯ-КЛАСТЕРА-control-plane
  1. в spec KubeadmControlPlane установить значение "1" для параметра replicas;
  2. вернуться на страницу "Управление узлами" и скопировать имена узлов в статусе NotReady;

Пример команды для применения в интерфейсе командной строки:

# Получить имена узлов кластера
kubectl get node
  1. получить список Machines и удалить Machines, соответствующие Control Plane-узлам в статусе NotReady;

Примеры команд

# Получить все Machines кластера
kubectl get machines -n ИМЯ-КЛАСТЕРА
# Удалить поочередно Machines, соответствующие Control Plane узлам в статусе `NotReady`
kubectl delete machines -n ИМЯ-КЛАСТЕРА ИМЯ-MACHINE

Удаление занимает некоторое время. В результате будут удалены Machines, узлы и ВМ, если кластер развернут с провайдером отличным от Shturval v2.

Важно ‒ В случае длительного удаления, например более 15 минут, можно удалить Machines ручным способом:

  • отредактировать удаляемый ресурс Machine, удалив finalizer;
  • удалить узел из кластера и ВМ в платформе виртуализации.
  1. после удаления всех Machines в графическом интерфейсе кластера открыть раздел "Администрирование" и перейти на страницу "Управление узлами", нажать на "Конфигурация группы" Control Plane. На вкладке "Конфигурация ClusterAPI" установить "3" в поле "Запрошено реплик".

Примеры команд для применения в интерфейсе командной строки

# Изменить количество реплик в KubeadmControlPlane
kubectl edit KubeadmControlPlane -n ИМЯ-КЛАСТЕРА ИМЯ-КЛАСТЕРА-control-plane
  1. в spec KubeadmControlPlane установить значение "3" для параметра replicas.