3 December 2019

Наши руки не для скуки: восстановление кластера Rook в K8s

Флант corporate blogSystem administrationData storageDevOpsKubernetes


Мы уже рассказывали, как/почему нам нравится Rook: в заметной мере он упрощает работу с хранилищами в кластерах Kubernetes. Однако с этой простотой приходят и определённые сложности. Надеемся, новый материал поможет лучше разбираться в таких сложностях ещё до того, как они себя проявят.

А чтобы читать было интереснее, начнём с последствий гипотетической проблемы в кластере.

«Всё пропало!»


Представьте себе, что вы однажды настроили и запустили в своем K8s-кластере Rook, он радовал своей работой, однако в какой-то «прекрасный» момент происходит следующее:

  • Новые pod’ы не могут примонтировать RBD-образы из Ceph.
  • Команды вроде lsblk и df не отрабатывают на узлах Kubernetes. Это автоматически означает: «что-то не так» с примонтированными на узлы RBD-образами. Не получается их прочитать, что указывает на недоступность мониторов…
  • Да, в кластере нет рабочих мониторов. Более того — нет даже ни pod’ов с OSD, ни pod’а MGR.

Когда был запущен pod rook-ceph-operator? Не так давно, как его деплоили. Почему? Rook-operator решил сделать новый кластер… Как же нам теперь восстановить работу кластера и данные в нём?

Для начала пойдем более длинным интересным путем, проведя вдумчивое расследование по «внутренностям» Rook и пошаговое восстановление его компонентов. Конечно, есть и более короткий правильный путь: использование бэкапов. Как известно, админы делятся на два типа: на тех, кто не делает бэкапы, и тех, кто их уже делает… Но об этом — после расследования.

Немного практики, или Длинный путь


Осмотримся и восстановим мониторы


Итак, посмотрим на список ConfigMap’ов: там есть необходимые для резервирования rook-ceph-config и rook-config-override. Они появляются при успешном деплое кластера.

NB: В новых версиях, после принятия этого PR, ConfigMap’ы перестали быть показателем успешности деплоя кластера.

Для выполнения дальнейших действий нам необходим жёсткий ребут всех серверов, на которых присутствуют примонтированные RBD-образы (ls /dev/rbd*). Его надо произвести через sysrq (или «пешком» в ЦОД). Это требование вызвано задачей отсоединить примонтированные RBD, для чего штатный ребут не подойдет (будет безуспешно пытаться отмонтировать их нормально).

Театр начинается с вешалки, а Ceph-кластер — с мониторов. Посмотрим на них.

Rook монтирует в pod монитора вот такие сущности:

Volumes:
 rook-ceph-config:
   Type:      ConfigMap (a volume populated by a ConfigMap)
   Name:      rook-ceph-config
 rook-ceph-mons-keyring:
   Type:        Secret (a volume populated by a Secret)
   SecretName:  rook-ceph-mons-keyring
 rook-ceph-log:
   Type:          HostPath (bare host directory volume)
   Path:          /var/lib/rook/kube-rook/log
 ceph-daemon-data:
   Type:          HostPath (bare host directory volume)
   Path:          /var/lib/rook/mon-a/data
Mounts:
  /etc/ceph from rook-ceph-config (ro)
  /etc/ceph/keyring-store/ from rook-ceph-mons-keyring (ro)
  /var/lib/ceph/mon/ceph-a from ceph-daemon-data (rw)
  /var/log/ceph from rook-ceph-log (rw)

Посмотрим, что в секрете rook-ceph-mons-keyring:

kind: Secret
data:
 keyring: LongBase64EncodedString=

Декодируем и получим обычный keyring с правами для админа и мониторов:

[mon.]
       key = AQAhT19dlUz0LhBBINv5M5G4YyBswyU43RsLxA==
       caps mon = "allow *"
[client.admin]
       key = AQAhT19d9MMEMRGG+wxIwDqWO1aZiZGcGlSMKp==
       caps mds = "allow *"
       caps mon = "allow *"
       caps osd = "allow *"
       caps mgr = "allow *"

Запомним. А теперь посмотрим на keyring в секрете rook-ceph-admin-keyring:

kind: Secret
data:
 keyring: anotherBase64EncodedString=

Что в нём?

[client.admin]
       key = AQAhT19d9MMEMRGG+wxIwDqWO1aZiZGcGlSMKp==
       caps mds = "allow *"
       caps mon = "allow *"
       caps osd = "allow *"
       caps mgr = "allow *"

Тот же. Посмотрим ещё… Вот, например, секрет rook-ceph-mgr-a-keyring:

[mgr.a]
       key = AQBZR19dbVeaIhBBXFYyxGyusGf8x1bNQunuew==
       caps mon = "allow *"
       caps mds = "allow *"
       caps osd = "allow *"

В конечном итоге мы находим ещё несколько секретов в ConfigMap’е rook-ceph-mon:

kind: Secret
data:
 admin-secret: AQAhT19d9MMEMRGG+wxIwDqWO1aZiZGcGlSMKp==
 cluster-name: a3ViZS1yb29r
 fsid: ZmZiYjliZDMtODRkOS00ZDk1LTczNTItYWY4MzZhOGJkNDJhCg==
 mon-secret: AQAhT19dlUz0LhBBINv5M5G4YyBswyU43RsLxA==

И это изначальный список с keyring’ами, откуда берутся все описанные выше секреты.

Как известно (см. dataDirHostPath в документации), Rook хранит такие данные в двух местах. Поэтому давайте сходим на узлы, чтобы посмотреть на keyring’и, лежащие в каталогах, что примонтированы в pod’ы с мониторами и OSD. Для этого найдём на узлах /var/lib/rook/mon-a/data/keyring и увидим:

# cat /var/lib/rook/mon-a/data/keyring
[mon.]
       key = AXAbS19d8NNUXOBB+XyYwXqXI1asIzGcGlzMGg==
       caps mon = "allow *"

Внезапно тут секрет оказался другим — не как в ConfigMap’ах.

А что насчет админского keyring’а? Он тоже у нас есть:

# cat /var/lib/rook/kube-rook/client.admin.keyring
[client.admin]
       key = AXAbR19d8GGSMUBN+FyYwEqGI1aZizGcJlHMLgx= 
       caps mds = "allow *"
       caps mon = "allow *"
       caps osd = "allow *"
       caps mgr = "allow *"

Вот тут и проблема. Произошел некий сбой: кластер пересоздался… но в действительности нет.

Становится понятно, что в секретах хранятся заново сгенерированные keyring’и, и они не от нашего старого кластера. Поэтому:

  • берём keyring от монитора из файла /var/lib/rook/mon-a/data/keyring (либо из бекапа);
  • изменяем keyring в секрете rook-ceph-mons-keyring;
  • прописываем keyring от админа и монитора в ConfigMap’е rook-ceph-mon;
  • удаляем контроллеры pod’ов с мониторами.

Чудо не заставит себя долго ждать: мониторы появятся и запустятся. Ура, начало положено!

Восстановим OSD


Заходим в pod rook-operator: вызов ceph mon dump показывает, что все мониторы на месте, а ceph -s — на то, что они в кворуме. Однако, если посмотреть на дерево OSD (ceph osd tree), увидим в нём нечто странное: OSD’шки начали появляться, но они пусты. Получается, их тоже нужно как-то восстановить. Но как?

Тем временем, в ConfigMap’ах появились так нужные нам rook-ceph-config и rook-config-override, а также множество других ConfigMap’ов с именами вида rook-ceph-osd-$nodename-config. Посмотрим в них:

kind: ConfigMap
data:
 osd-dirs: '{"/mnt/osd1":16,"/mnt/osd2":18}'

Всё не так, всё перепутано!

Отскейлим в нуль pod оператора, удалим сгенерированные Deployment’ы pod’ов с OSD и исправим эти ConfigMap’ы. Но откуда взять правильную карту OSD по узлам?

  • Попробуем снова покопаться в директориях /mnt/osd[1-2] на узлах — в надежде, что сможем там за что-то зацепиться.
  • В каталоге /mnt/osd1 есть 2 подкаталога: osd0 и osd16. Последний — это ведь как раз тот ID, что указан в ConfigMap (16)?
  • Проверим по размерам и увидим, что osd0 намного больше osd16.

Приходим к выводу, что osd0 — это и есть нужный OSD, что был указан как /mnt/osd1 в ConfigMap (ведь мы используем directory based osd.)

Шаг за шагом проверяем все узлы и правим ConfigMap’ы. После всех указаний можно запустить pod Rook-оператора и прочитать его логи. А в них всё замечательно:

  • я оператор кластера;
  • я нашел диски на узлах;
  • я нашел мониторы;
  • мониторы подружились, т.е. образовали кворум;
  • запускаю деплойменты OSD…

Снова зайдем в pod оператора Rook и проверим живость кластера… да, мы немного ошиблись с выводами про имена OSD на некоторых узлах! Не беда: снова поправили ConfigMap’ы, удалили лишние каталоги от новых OSD и пришли к долгожданному состоянию HEALTH_OK!

Проверим образы в пуле:

# rbd ls -p kube
pvc-9cfa2a98-b878-437e-8d57-acb26c7118fb
pvc-9fcc4308-0343-434c-a65f-9fd181ab103e
pvc-a6466fea-bded-4ac7-8935-7c347cff0d43
pvc-b284d098-f0fc-420c-8ef1-7d60e330af67
pvc-b6d02124-143d-4ce3-810f-3326cfa180ae
pvc-c0800871-0749-40ab-8545-b900b83eeee9
pvc-c274dbe9-1566-4a33-bada-aabeb4c76c32
…

Всё на месте — кластер спасён!

Я ленивый делаю бекапы, или Быстрый путь


Если бэкапы для Rook делались, то процедура восстановления становится значительно проще и сводится к следующей:

  1. Масштабируем в нуль deployment Rook-оператора;
  2. Удаляем все деплойменты, кроме Rook-оператора;
  3. Восстанавливаем из бэкапа все secret’ы и ConfigMap’ы;
  4. Восстанавливаем содержимое директорий /var/lib/rook/mon-* на узлах;
  5. Восстанавливаем (если вдруг потеряли) CRD CephCluster, CephFilesystem, CephBlockPool, CephNFS, CephObjectStore;
  6. Обратно масштабируем deployment Rook-оператора в 1.

Полезные советы


Делайте бэкапы!

А чтобы избегать ситуаций, когда понадобится восстановление из них:

  1. Перед масштабными работами с кластером, заключающимися в перезагрузках серверов, масштабируйте в нуль Rook-оператора, чтобы он не делал лишнего.
  2. На мониторы заранее добавляйте nodeAffinity.
  3. Уделите внимание предварительной настройке таймаутов ROOK_MON_HEALTHCHECK_INTERVAL и ROOK_MON_OUT_TIMEOUT.

Вместо заключения


Нет смысла спорить, что Rook, будучи дополнительной «прослойкой» (в общей схеме организации хранилищ в Kubernetes), как многое упрощает, так и добавляет и новые сложности и потенциальные проблемы в инфраструктуре. Дело остаётся за «малым»: сделать взвешенный, обоснованный выбор между этими рисками с одной стороны и той пользой, которую решение приносит в вашем конкретном случае, — с другой.

Кстати, недавно в документацию Rook был добавлен раздел «Adopt an existing Rook Ceph cluster into a new Kubernetes cluster». В нём более подробно расписано, что нужно делать, чтобы переехать имеющимися данными в новый кластер Kubernetes или же восстановить работу кластера, развалившегося по той или иной причине.

P.S.


Читайте также в нашем блоге:

Tags:RookKubernetes
Hubs: Флант corporate blog System administration Data storage DevOps Kubernetes
+33
4.5k 24
Comments 23