Для тех, кому не хватило денег на интерпрайз, придумали костыли в виде ПОКХД, но и здесь всё не так просто, поскольку костыли эти поставляются не в готовом виде, а вполне себе в стиле ИКЕА - вот тебе ведро болтов с левой резьбой и хитрым шлицем, пачка гаек с 3 и 5 гранями и инструкция в 8 томах. Но умные люди нашли способ собирать костыли быстрее - они собрали костыли для костылей и имя им - ceph-deploy, но обо всё по порядку.
Перед прочтением, и уж тем более использованием, обязательно ознакомьтесь с главной страницей этого бложика. Желательно до просветлвения.
Сам же CEPH можно использовать как:
Сам по себе CEPH состоит из нескольких, вполне себе автономных, кусков, как то
да и, кажется, всё. Остальное уже идёт поверх данного набора. Соответственно как размазать эти роли по серверам - задача того, кто разворачивает систему. Можно даже сделать всё на одном сервере, но об отказоустойчивости можно будет забыть. За сим обычно разворачивают так: в сете из, минимум, 4 серверов - 3 mon, у каждого сервера свои osd, mds по вкусу. Дополнительно, например, можно ещё и журналы osd вынести на отдельные диски, но это уже как приятные излишества.1)
Интересный факт - в репозиториях CentOS, который якобы бесплатный RHEL, есть даже отдельная ветка с ceph, однако ceph-deploy, который в ней представлен, не умеет в systemd, хотя поделку Лёни, вроде бы, затащили давно. Ceph-deploy из апстрима же вполне себе всё умеет, поэтому имеет смысл склонить более свежие пакеты.
Ответом же на этот интересный факт являются два других факта. Первый - в RHEL до сих пор hammer, ceph-deploy выпускается по принципу rooling release.
В любом случае я использую апстрим, который можно взять вот тут:
rsync://download.ceph.com/ceph/rpm-jewel/el7/noarch/ rsync://download.ceph.com/ceph/rpm-jewel/el7/x86_64/
Как и в большинстве случаев при работе с CentOS производим обычную подготовку:
# echo 'SELINUX=disabled' > /etc/sysconfig/selinux # systemctl disable firewalld NetworkManager # yum remove firewalld NetworkManager
После чего настриваем сети, подаём vlan'ы и проверяем, что всё хорошо с сетью. Можно поиграться в bonding/teaming и прочие попытки засунуть слона в холодильник, но об этом будет в другой статье. Сейчас нас интересует полностью рабочий L3. Лично я подглядел как сделано у коллег, поэтому использую два изолированных VLAN'a - один для связи с OpenStack, второй для технологических целей самого ceph'a. (Ну и третий, вестимо, менеджмент, но его можно и нативно подать) Данная конфигурация служит для того, чтобы в случае чего можно было пошейпить тот или иной канал.
Важный момент - время на всех серверах CEPH кластера должно быть синхронизированно, поэтому не забываем выставлять правильную таймзону и включать ntpd.
Добавляем пользователя, под которым будем работать с кластером
# mkdir /home/ceph # useradd ceph -s /bin/bash -d /home/ceph || usermod ceph -s /bin/bash -d /home/ceph # chown ceph. /home/ceph # passwd ceph
Проделываем эту операцию на всех хостах, которые будут входить в кластер, после чего выбираем хост, с которого будем деплоиться и генерируем на нём ssh ключ для ceph'a:
$ ssh-keygen
Раскатываем файл /var/lib/ceph/.ssh/id_rsa.pub по всем серверам (включая тот, с которого разворачиваемся) в файл /var/lib/ceph/.ssh/authorized_keys (если мы не пользуем SCM можно использовать ssh-copy-id), после чего проверяем, что всё получилось и выдаём пользователю ceph на всех нодах следующие права на sudo:
Defaults:ceph !requiretty ceph ALL=(ALL) NOPASSWD:ALL
После создания файла требуется выставить на него права 0400.
Так же официальная документация советует использовать файл /etc/hosts в обход использования глобальной системы доменных имён, поскольку в случае смерти последней можно не хило так выстрелить себе в ногу с переходом кластера в RO. Так же данное решение позволяет сократить задержки на ресолвинг адресов. Поэтому на все ноды заносим на всякий случай всех соседей:
192.168.1.10 mon1 mon1.sds2.owlhost.in 192.168.1.11 osd1 osd1.sds2.owlhost.in 192.168.1.12 osd2 osd2.sds2.owlhost.in
Важный момент - в /etc/hosts мы вносим адреса из публичной сети.
Если всё получилось и все наши соседи доступны, то переходим непосредственно к деплою.
А вот и те саме костыли для костылей, о которых я говорил в начале. Устанавливаются просто и незамысловато:
# yum install ceph-deploy
Не забываем, что стоковые репозитории содержат не совместимую версию ceph-deploy с CentOS 7, поэтому заранее правильно настройте свои репозитории.2)
В нашем случае мы будем разворачивать кластер из 3 нод с фактором репликации 2 3)4) Соответственно произведя нехитрые математические рассчёты из трёх osd, общим объёмом 300 Гб мы получим 150 5) Гб полезного пространства. Стоит отметить, что CEPH при заполнении OSD на 85% останавливает репликацию новых данных и переводит кластер в HEALTH_WARN, а когда заполнение достигает 95% вся запись на OSD останавливается и кластер переходит в HEALTH_ERR, поэтому максимальное безопасное количество данных, которые можно хранить в кластере CEPH не должно по возможности первышать 75%. При достижении данной отметки желательно начать расширять кластер. Для пущей уверенности в отказоустойчивости системы сделаем ещё и три монитора, чтобы было кому достигать кворума, но вполне можно обойтись и одним.
Итак, action! Начинаем с того, что создаём себе новую директорию для разворачивания кластера. Она может понадобиться, например, если захочется ввести ещё несколько нод хранения или сделать иные не менее интересные, но и не менее затратные операции:
$ mkdir cluster $ cd cluster
Теперь на всякий случай, если мы вдруг уже несколько раз попробовали собрать свой кластер, но у нас ещё ни разу не получилось - сотрём все старые ошибки:
$ ceph-deploy purge {node} [{node-1}[, {node-2}[, ...]]] $ ceph-deploy purgedata {node} [{node-1}[, {node-2}[, ...]]] $ ceph-deploy forgetkeys $ rm ceph.*
И уже теперь создаём новый кластер с чистого листа
$ ceph-deploy new mon1 osd1 osd2
На выходе мы получим первичный ceph.conf, куда я сразу же советую добавить нужные настроечки, как то
... public_network = 192.168.0.0/23 private_network = 192.168.10.0/24 osd_pool_default_size = 2 # по желанию, но вообще 3 лучше ...
Приватная сеть используется для репликации данных внутри кластера, а в публичной всё остальное. Эдакое разделение данных.
Так же требуется проверить правильность адресов mon нод (public сеть), а так же mon_initial_members (адреса тоже должны быть из public сети)
И, наконец, можно переходить к установке пакетов на сервера. Так как у нас уже все репозитории подключены и нам никчему мусор в системе - мы едем с аргументом –no-adjust-repos. В противном случае ceph-deploy затянет пакеты centos-epel-repo, centos-storage-ceph-repo и прочие, а так же может испортить уже существующие настройки репозиториев:
# ceph-deploy install --no-adjust-repos mon1 osd1 osd2
После того, как всё ПО приехало, инициализируем ранее объявленные мониторы:
# ceph-deploy mon create-initial
Для удобства управления кластером раскатываем админские ключи и прочие конфиги по мониторным нодам:
# ceph-deploy admin mon1 osd1 osd2
После деплоя требуется обязательно на всех нодах проверить, что /etc/ceph/ceph.client.admin.keyring и /etc/ceph/ceph.conf имеет права доступа 644.
Если у нас релиз luminous+, то потребуется ещё настроить управляющий демон
# ceph-deploy mgr create mon1
Сами по себе osd могут представлять из себя как raw device (/dev/sda, /dev/sdb, /dev/md0, /dev/dm-3), так и уже смонтированные ФС (например люди любят брать xfs). В нашей инсталляции мы будем использовать и xfs, и raw device под OSD. Поэтому размечаем на всех серверах поданные под данные диски, форматируем и добавляем их в fstab.
mon1# fdisk /dev/sdb mon1# mkfs.xfs /dev/sdb1 mon1# mkdir /var/lib/ceph/osd/0 mon1# chown ceph. /var/lib/ceph/osd/0 mon1# echo '/dev/sdb1 /var/lib/ceph/osd/0 xfs auto,noatime 0 0' >> /etc/fstab osd1# fdisk /dev/sdb osd1# mkfs.xfs /dev/sdb1 osd1# mkdir /var/lib/ceph/osd/1 osd1# chown ceph. /var/lib/ceph/osd/1 osd1# echo '/dev/sdb1 /var/lib/ceph/osd/1 xfs auto,noatime 0 0' >> /etc/fstab
А третий сервер будет особенным, мы будем использовать весь диск. Для этого требуется чтобы на диске была пустая таблица разделов GPT.
mon1$ ceph-deploy disk zap osd2:/dev/sdb
У меня по одному диску на сервер, у кого-то может быть больше. Точка монтирования может быть любой, не обязательно цифры и не обязательно по указанному пути, это скорее для себя, чтобы не запутаться.
После того, как всё подготовлено - вводим osd в кластер:
# ceph-deploy osd create mon1:/var/lib/ceph/osd/0 osd1:/var/lib/ceph/osd/1 osd2:/dev/sdb
и активируем их
# ceph-deploy osd activate mon1:/var/lib/ceph/osd/0 osd1:/var/lib/ceph/osd/1 osd2:/dev/sdb1
После всех манипуляций проверяем
# ceph health
и если на выходе HEALTH_OK - значит у нас получилось и кластер готов. Если HEALTH_WARN, то можно выполнить
# ceph -s
и посмотреть что не так.
Посмотреть список и статус OSD можно командой
# ceph osd tree
Как я понял это возможность распределить OSD внутри кластера по группам. Идея проста - мы складываем нужные OSD в одну группу, задаём и веса, а потом определяем правила для этих групп.
Изначально все osd попадают в следующую иерархию
# ceph osd crush tree [ { "id": -1, "name": "default", "type": "root", "type_id": 10, "items": [ { "id": -2, "name": "mon1", "type": "host", "type_id": 1, "items": [ { "id": 0, "name": "osd.0", "type": "osd", "type_id": 0, "crush_weight": 0.097595, "depth": 2 } ] }, { "id": -3, "name": "osd1", "type": "host", "type_id": 1, "items": [ { "id": 1, "name": "osd.1", "type": "osd", "type_id": 0, "crush_weight": 0.097595, "depth": 2 } ] }, { "id": -4, "name": "osd2", "type": "host", "type_id": 1, "items": [ { "id": 2, "name": "osd.2", "type": "osd", "type_id": 0, "crush_weight": 0.097595, "depth": 2 } ] } ] } ]
И на всю группу root=default применяется replucated_ruleset
# ceph osd crush rule list [ "replicated_ruleset" ]
Мы же в свою очередь можем создать свою группу со своими правилами. Например если у нас есть 20 OSD и мы хотим сделать две независимых зоны. (4 сервера по 5 OSD на каждом) Для этого назначаем нашим OSD сначала нужную иерархию:
# ceph osd crush create-or-move osd.0 1.0 root=zone1 rack=r1 host=mon1 # ceph osd crush create-or-move osd.1 1.0 root=zone1 rack=r1 host=mon1 # ceph osd crush create-or-move osd.2 1.0 root=zone2 rack=r1 host=mon1 # ceph osd crush create-or-move osd.3 1.0 root=zone2 rack=r1 host=mon1 # ceph osd crush create-or-move osd.4 1.0 root=zone2 rack=r1 host=mon1 # ceph osd crush create-or-move osd.5 1.0 root=zone1 rack=r1 host=osd1 # ceph osd crush create-or-move osd.6 1.0 root=zone1 rack=r1 host=osd1 # ceph osd crush create-or-move osd.7 1.0 root=zone1 rack=r1 host=osd1 # ceph osd crush create-or-move osd.8 1.0 root=zone2 rack=r1 host=osd1 # ceph osd crush create-or-move osd.9 1.0 root=zone2 rack=r1 host=osd1 # ceph osd crush create-or-move osd.10 1.0 root=zone1 rack=r1 host=osd2 # ceph osd crush create-or-move osd.11 1.0 root=zone1 rack=r1 host=osd2 # ceph osd crush create-or-move osd.12 1.0 root=zone2 rack=r1 host=osd2 # ceph osd crush create-or-move osd.13 1.0 root=zone2 rack=r1 host=osd2 # ceph osd crush create-or-move osd.14 1.0 root=zone2 rack=r1 host=osd2 # ceph osd crush create-or-move osd.15 1.0 root=zone1 rack=r1 host=osd3 # ceph osd crush create-or-move osd.16 1.0 root=zone1 rack=r1 host=osd3 # ceph osd crush create-or-move osd.17 1.0 root=zone1 rack=r1 host=osd3 # ceph osd crush create-or-move osd.18 1.0 root=zone2 rack=r1 host=osd3 # ceph osd crush create-or-move osd.19 1.0 root=zone2 rack=r1 host=osd3
Создаём правило
# ceph osd crush rule create-simple zone1 zone1 host # ceph osd crush rule create-simple zone2 zone2 host
Где первое zone1 - название правила, второе zone1 - значение root= в иерархии, а rack узел, который является единицей отказа. Так у нас в иерархии zone1-r1-<server>-osdX точкой отказа являются <server>, бишь сервера. Если указать, например, точкой отказа стойку - то в случае с одной стойкой репликация не пойдёт и кластер умрёт с HEALTH_WARN. Очень важно, чтобы в иерархии osd лежал на том же сервере, на котором находится физически, иначе при перезапуске osd-сервера все osd на нём находящиеся попадут в root=default,
OSD уникальны в пределах crushmap.
Итак, у нас имеется кластер, однако пока мы ничего с ним сделать не можем, поэтому сперва нам требуется создать пул, в который уже и будут записываться наши данные. Итак, в процессе создания пула нужно указать количество Placement groups. С помощью PG объекты в CEPH группируются и именно PG осуществляется резервирование данных. Если OSD умирает - его PG реплицируется на ещё живые OSD. Кто-то скажет - чем больше атомарность - тем больше отказоустойчивость и я, возможно даже соглашусь, но для рассчёта правильных количеств PG на пул существует несколько различных калькуляторов на просторах интернетов. Так же есть ограничения - минимум 30 PG на один OSD, максимум - 300. Я, лично, возьму рандомное число с потолка, потому что всё одно это стенд:
# ceph osd pool create volumes 128 zone1
По аргументам здесь следующее: volumes - название пула, 128 - количество PG, zone1 - правило в crushmap.