Мы уже рассказывали, как/почему нам нравится 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 делались, то процедура восстановления становится значительно проще и сводится к следующей:
- Масштабируем в нуль deployment Rook-оператора;
- Удаляем все деплойменты, кроме Rook-оператора;
- Восстанавливаем из бэкапа все secret’ы и ConfigMap’ы;
- Восстанавливаем содержимое директорий
/var/lib/rook/mon-*
на узлах; - Восстанавливаем (если вдруг потеряли) CRD
CephCluster
,CephFilesystem
,CephBlockPool
,CephNFS
,CephObjectStore
; - Обратно масштабируем deployment Rook-оператора в 1.
Полезные советы
Делайте бэкапы!
А чтобы избегать ситуаций, когда понадобится восстановление из них:
- Перед масштабными работами с кластером, заключающимися в перезагрузках серверов, масштабируйте в нуль Rook-оператора, чтобы он не делал лишнего.
- На мониторы заранее добавляйте nodeAffinity.
- Уделите внимание предварительной настройке таймаутов
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.
Читайте также в нашем блоге:
- «Rook или не Rook — вот в чём вопрос»;
- «Rook — „самообслуживаемое“ хранилище данных для Kubernetes»;
- «Longhorn, распределённое хранилище для K8s от Rancher, передано в CNCF».
- «Создаём постоянное хранилище с provisioning в Kubernetes на базе Ceph».
Автор: broken-ufa