Используя Ceph как сетевое хранилище в разных по нагруженности проектах, мы можем столкнуться с различными задачами, которые с первого взгляда не кажутся простыми или тривиальными. Например:
- миграция данных из старого Ceph в новый с частичным использованием предыдущих серверов в новом кластере;
- решение проблемы распределения дискового пространства в Ceph.
Разбираясь с такими задачами, мы сталкиваемся с необходимостью корректно извлечь OSD без потери данных, что особенно актуально при больших объемах данных. Об этом и пойдет речь в статье.
Описанные ниже способы актуальны для любых версий Ceph. Кроме того, будет учтен тот факт, что в Ceph может храниться большой объем данных: для предотвращения потерь данных и других проблем некоторые действия будут «дробиться» на несколько других.
Предисловие про OSD
Поскольку два из трёх рассматриваемых рецептов посвящены OSD (Object Storage Daemon), перед погружением в практическую часть — вкратце о том, что это вообще такое в Ceph и почему он так важен.
В первую очередь следует сказать, что весь Ceph-кластер состоит из множества OSD. Чем их больше, тем больше свободный объем данных в Ceph. Отсюда легко понять главную функцию OSD: он сохраняет данные объектов Ceph на файловых системах всех узлов кластера и предоставляет сетевой доступ к ним (для чтения, записи и прочих запросов).
На этом же уровне устанавливаются параметры репликации за счет копирования объектов между разными OSD. И здесь можно столкнуться с различными проблемами, о решении которых и будет рассказано далее.
Кейс №1. Безопасное извлечение OSD из кластера Ceph без потери данных
Необходимость в извлечении OSD может быть вызвана выводом сервера из кластера — например, для замены на другой сервер, — что и случилось у нас, послужив поводом к написанию статьи. Таким образом, конечная цель манипуляций — извлечь все OSD и mon’ы на данном сервере, чтобы его можно было остановить.
Для удобства и исключения ситуации, где мы в процессе выполнения команд ошибемся с указанием нужного OSD, зададим отдельную переменную, значением которой и будет номер удаляемой OSD. Назовем её ${ID}
— здесь и далее такая переменная заменяет номер OSD, с которой мы работаем.
Посмотрим на состояние перед началом работ:
root@hv-1 ~ # ceph osd tree
ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF
-1 0.46857 root default
-3 0.15619 host hv-1
-5 0.15619 host hv-2
1 ssd 0.15619 osd.1 up 1.00000 1.00000
-7 0.15619 host hv-3
2 ssd 0.15619 osd.2 up 1.00000 1.00000
Чтобы инициировать удаление OSD, потребуется плавно выполнить reweight
на ней до нуля. Таким образом мы снижаем количество данных в OSD за счет балансировки в другие OSD. Для этого выполняются следующие команды:
ceph osd reweight osd.${ID} 0.98
ceph osd reweight osd.${ID} 0.88
ceph osd reweight osd.${ID} 0.78
… и так далее до нуля.
Плавная балансировка необходима, чтобы не потерять данные. Это особенно актуально, если в OSD находится большой объем данных. Чтобы точно убедиться, что после выполнения команд reweight
все прошло успешно, можно выполнить ceph -s
или же в отдельном окне терминала запустить ceph -w
для того, чтобы наблюдать за изменениями в реальном времени.
Когда OSD «опустошена», можно приступить к стандартной операции по ее удалению. Для этого переведем нужную OSD в состояние down
:
ceph osd down osd.${ID}
«Вытащим» OSD из кластера:
ceph osd out osd.${ID}
Остановим сервис OSD и отмонтируем его раздел в ФС:
systemctl stop ceph-osd@${ID}
umount /var/lib/ceph/osd/ceph-${ID}
Удалим OSD из CRUSH map:
ceph osd crush remove osd.${ID}
Удалим пользователя OSD:
ceph auth del osd.${ID}
И, наконец, удалим саму OSD:
ceph osd rm osd.${ID}
Примечание: если вы используете версию Ceph Luminous или выше, то вышеописанные действия по удалению OSD можно свести к двум командам:
ceph osd out osd.${ID}
ceph osd purge osd.${ID}
Если после выполнения описанных выше действий выполнить команду ceph osd tree
, то должно быть видно, что на сервере, где производились работы, больше нет OSD, для которых выполнялись операции выше:
root@hv-1 ~ # ceph osd tree
ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF
-1 0.46857 root default
-3 0.15619 host hv-1
-5 0.15619 host hv-2
-7 0.15619 host hv-3
2 ssd 0.15619 osd.2 up 1.00000 1.00000
Попутно заметим, что состояние кластера Ceph перейдет в HEALTH_WARN
, а также увидим уменьшение количества OSD и объема доступного дискового пространства.
Далее будут описаны действия, которые потребуются, если вы хотите полностью остановить сервер и, соответственно, удалить его из Ceph. В таком случае важно помнить, что перед отключением сервера необходимо извлечь все OSD на этом сервере.
Если на этом сервере не осталось больше OSD, то после их удаления нужно исключить из карты OSD сервер hv-2
, выполнив следующую команду:
ceph osd crush rm hv-2
Удаляем mon
с сервера hv-2
, запуская команду ниже на другом сервере (т.е. в данном случае — на hv-1
):
ceph-deploy mon destroy hv-2
После этого можно останавливать сервер и приступать к последующим действиям (его повторному развёртыванию и т.п.).
Кейс №2. Распределение дискового пространства в уже созданном Ceph-кластере
Вторую историю начну с предисловия про PG (Placement Groups). Основная роль PG в Ceph заключается в первую очередь в агрегации Ceph-объектов и дальнейшей репликации в OSD. Формула, с помощью которой можно посчитать необходимое количество PG, находится в соответствующем разделе документации Ceph. Там же этот вопрос разобран и на конкретных примерах.
Так вот: одна из распространенных проблем во время эксплуатации Ceph — несбалансированное количество OSD и PG между пулами в Ceph.
Во-первых, из-за этого может возникнуть ситуация, когда указывается слишком большое количество PG в небольшом по объему пуле, что по сути является нерациональным использованием дискового пространства в кластере. Во-вторых, на практике получается более серьезная проблема: переполнение данных в одной из OSD. Это влечет за собой переход кластера сначала в состояние HEALTH_WARN
, а потом и HEALTH_ERR
. Всему виной то, что Ceph при расчете доступного объема данных (узнать его можно по MAX AVAIL
в выводе команды ceph df
для каждого пула по отдельности) опирается на объем доступных данных в OSD. Если хотя бы в одном OSD будет недостаточно места, то больше записать данные не получится, пока данные не будут распределены должным образом между всеми OSD.
Стоит уточнить, что эти проблемы в большей степени решаются на этапе конфигурации Ceph-кластера. Один из инструментов, которым можно воспользоваться, — Ceph PGCalc. С его помощью наглядно рассчитывается необходимое количество PG. Впрочем, к нему можно прибегнуть и в ситуации, когда кластер Ceph уже настроен неправильно. Здесь стоит уточнить, что в рамках работ по исправлению вам, вероятнее всего, понадобится уменьшать количество PG, а эта возможность недоступна в старых версиях Ceph (она появилась только с версии Nautilus).
Итак, представим следующую картину: у кластера — статус HEALTH_WARN
из-за того, что в одном из OSD заканчивается место. Об этом будет свидетельствовать ошибка HEALTH_WARN: 1 near full osd
. Ниже представлен алгоритм по выходу из такой ситуации.
В первую очередь требуется распределить имеющиеся данные между остальными OSD. Подобную операцию мы уже выполняли в первом кейсе, когда «осушали» узел — с той лишь разницей, что теперь потребуется незначительно уменьшить reweight
. Например, до 0.95:
ceph osd reweight osd.${ID} 0.95
Так освобождается дисковое пространство в OSD и исправляется ошибка в ceph health. Однако, как уже говорилось, данная проблема преимущественно возникает по причине некорректной настройки Ceph на начальных этапах: очень важно сделать реконфигурацию, чтобы она не проявлялась в будущем.
В нашем конкретном случае все упиралось в:
- слишком большое значение
replication_count
в одном из пулов, - слишком высокое количество PG в одном пуле и слишком в маленькое — в другом.
Воспользуемся уже упомянутым калькулятором. В нем наглядно показано, что нужно вводить и, в принципе, нет ничего сложного. Задав необходимые параметры, получаем следующие рекомендации:
Примечание: если вы настраиваете Ceph-кластер с нуля, еще одной полезной функцией калькулятора окажется генерация команд, которые создадут пулы с нуля с параметрами, указанными в таблице.
Сориентироваться помогает последний столбец — Suggested PG Count. В нашем случае полезен еще и второй, где указан параметр репликации, поскольку мы решили изменить и множитель репликации.
Итак, сначала потребуется изменить параметры репликации — это стоит делать в первую очередь, так как, уменьшив множитель, мы освободим дисковое пространство. В процессе выполнения команды можно заметить, что значение доступного дискового объема будет увеличиваться:
ceph osd pool $pool_name set $replication_size
А после её завершения — изменяем значения параметров pg_num
и pgp_num
следующим образом:
ceph osd pool set $pool_name pg_num $pg_number
ceph osd pool set $pool_name pgp_num $pg_number
Важно: мы должны последовательно в каждом пуле изменить количество PG и не изменять значения в других пулах, пока не исчезнут предупреждения «Degraded data redundancy» и «n-number of pgs degraded».
Проверить, что все прошло успешно, можно также по выводам команд ceph health detail
и ceph -s
.
Кейс №3. Миграция виртуальной машины с LVM в Ceph RBD
В ситуации, когда в проекте используются виртуальные машины, установленные на арендованные серверы bare-metal, часто встает вопрос с отказоустойчивым хранилищем. А еще очень желательно, чтобы места в этом хранилище было достаточно… Другая распространенная ситуация: есть виртуальная машина с локальным хранилищем на сервере и необходимо расширить диск, но некуда, поскольку на сервере не осталось свободного дискового пространства.
Проблему можно решить разными способами — например, миграцией на другой сервер (если таковой имеется) или добавлением новых дисков на сервер. Но не всегда получается это сделать, поэтому миграция из LVM в Ceph может стать отличным решением данной проблемы. Выбрав такой вариант, мы также упрощаем дальнейший процесс миграции между серверами, так как не нужно будет перемещать локальное хранилище с одного гипервизора на другой. Единственная загвоздка — придется остановить ВМ на время проведения работ.
В качестве приводимого далее рецепта взята статья с этого блога, инструкции которой были опробованы в действии. К слову, там же описан и способ беспростойной миграции, однако в нашем случае он просто не потребовался, поэтому мы его не проверяли. Если же это критично для вашего проекта — будем рады узнать о результатах в комментариях.
Приступим к практической части. В примере мы используем virsh и, соответственно, libvirt. Для начала убедитесь, что пул Ceph’а, в который будут мигрированы данные, подключен к libvirt:
virsh pool-dumpxml $ceph_pool
В описании пула должны быть данные подключения к Ceph с данными для авторизации.
Следующий этап заключается в том, что LVM-образ конвертируется в Ceph RBD. Время выполнения зависит в первую очередь от размера образа:
qemu-img convert -p -O rbd /dev/main/$vm_image_name rbd:$ceph_pool/$vm_image_name
После конвертации останется LVM-образ, который будет полезен в случае, если мигрировать ВМ в RBD не получится и придется откатывать изменения. Также — для возможности быстро откатить изменения — сделаем бэкап конфигурационного файла виртуальной машины:
virsh dumpxml $vm_name > $vm_name.xml
cp $vm_name.xml $vm_name_backup.xml
… и отредактируем оригинал (vm_name.xml
). Найдем блок с описанием диска (начинается со строки <disk type='file' device='disk'>
и заканчивается на </disk>
) и приведем его к следующему виду:
<disk type='network' device='disk'>
<driver name='qemu'/>
<auth username='libvirt'>
<secret type='ceph' uuid='sec-ret-uu-id'/>
</auth>
<source protocol='rbd' name='$ceph_pool/$vm_image_name>
<host name='10.0.0.1' port='6789'/>
<host name='10.0.0.2' port='6789'/>
</source>
<target dev='vda' bus='virtio'/>
<alias name='virtio-disk0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</disk>
Разберем некоторые детали:
- В протокол
source
указывается адрес до хранилища в Ceph RBD (это адрес с указанием названия Ceph-пула и RBD-образа, который определялся на первом этапе). - В блоке
secret
указывается типceph
, а также UUID секрета для подключения к нему. Его uuid можно узнать с помощью командыvirsh secret-list
. - В блоке
host
указываются адреса до мониторов Ceph.
После редактирования конфигурационного файла и завершения конвертации LVM в RBD можно применить измененный конфигурационный файл и запустить виртуальную машину:
virsh define $vm_name.xml
virsh start $vm_name
Самое время проверить, что виртуальная машина запустилась корректно: это можно узнать, например, подключившись к ней по SSH или через virsh
.
Если виртуальная машина работает корректно и вы не обнаружили других проблем, то можно удалить LVM-образ, который больше не используется:
lvremove main/$vm_image_name
Заключение
Со всеми описанными случаями мы столкнулись на практике — надеемся, что инструкции помогут и другим администраторам решить схожие проблемы. Если у вас есть замечания или другие подобные истории из опыта эксплуатации Ceph — будем рады увидеть их в комментариях!
P.S.
Читайте также в нашем блоге:
- «Наши руки не для скуки: восстановление кластера Rook в K8s»;
- «Rook или не Rook — вот в чём вопрос»;
- «Rook — „самообслуживаемое“ хранилище данных для Kubernetes»;
- «Создаём постоянное хранилище с provisioning в Kubernetes на базе Ceph».
Автор: Ilya Andreev