Привет, Хабр! У этой статьи два автора – Василий Меньшаков и Алексей Кузнецов. Мы системные архитекторы развития платформы больших данных в X5 Tech. Решили поделиться своим опытом построения нового хранилища резервных копий для Greenplum. Какие были проблемы у предыдущего решения? Почему мы выбрали Ceph? Какой способ интеграции лучше? С какими проблемами мы сталкивались при внедрении этого инструмента? Что мы настраивали? Читайте подробности в нашей статье. В инфраструктуре X5 Tech есть несколько кластеров Greenplum, среди которых и проект, представляющий собой корпоративное хранилище данных. В нём хранится свыше 220ТБ данных в 50+ тысячах таблиц, к которым ежедневно обращаются сотни пользователей (аналитики, менеджеры, разработчики и т. д.). Такая большая и важная система нуждается в регулярном обслуживании и резервном копировании для обеспечения заявленных показателей доступности. Именно здесь мы столкнулись с тем, что имеющееся резервное копирование в HDFS (так исторически сложилось) всё меньше подходит для решения такой задачи (в определённое время мы достигли ситуации, когда стабильность решения стала недопустимо низкой). Размещая такие массивы данных в HDFS, приходилось встречать следующие проблемы: большое число файлов в бэкапах негативно сказываются на производительности нейм-ноды, приходилось подкручивать размер Heap, но это не особо помогало; желая сократить число файлов, была внедрена асинхронная .har-архивация старых резервных копий. И если процесс архивации просто долгий (часы), то процесс разархивации был очень долгим (сутки). Это делало процесс извлечения объектов из старых резервных копий слишком долгим (например, когда приходит задача восстановить удалённую на прошлой неделе таблицу); падение бэкапов при переключении неймноды (особенность Hadoop-2 без федерации); дата-ноды в кластере Hadoop могли входить в состояние stale как от самого процесса резервного копирования Greenplum, так и от других задач на кластере, что также периодически приводило к сбоям процессов резервного копирования; для интеграции с HDFS используется коннектор hadoop-hdfs-fuse, который трудно дебажить (можно сказать, что бесполезно). Поэтому, когда зависали/падали бэкапы, точную причину проблемы назвать было практически невозможно (input-output error и всё на этом); усложнение скриптов резервного копирования, чтобы они включали в себя монтирование, проверки корректности монтирования и последующее отмонтирование HDFS; время снятия большой резервной копии было очень долгим (10-12 часов), что не только ограничивало работу пользователей (доступ закрывается на время бэкапа), но также и снижает саму вероятность успеха процесса резервного копирования; мешали задачам Hadoop-кластера, ровно как и пользовательские расчёты на кластере Hadoop могли мешать бэкапам. Так и родилась задача по поиску и внедрению нового инструмента для хранения резервных копий. Мы не рассчитывали на энтерпрайз-хранилища, также нам уже нужно было уйти от HDFS в пользу чего-то другого. И наш выбор пал на Ceph. Вначале нужно было определиться с направлением, в котором будем дальше двигаться: Строить ещё один HDFS только для бэкапов; Пойти в сторону энтерпрайз-хранилищ данных; Развивать имеющийся небольшой кластер Ceph; Внедрить какую-то другую распределённую файловую систему. Строить ещё один HDFS не имело смысла, так как это не решало обозначенных выше проблем технологии HDFS + FUSE. Поэтому мы сразу отмели этот вариант. Бюджетная оценка использования СХД от Dell или HP выходила слишком высокой, при этом такие решения имеют ограничения по масштабированию: можно проводить вертикальное масштабирование, но если упереться в пропускную способность, то быстрее работать не станет. Можно было пойти и в сторону внедрения какой-то новой для нас распределённой файловой системы, но было решено дать шанс технологии Ceph, ведь у нас уже имелся Ceph S3 в качестве хранилища для пользователей, поэтому был некоторый опыт работы с этим решением, и оно уже приличное время эксплуатировалось в компании. С этого момента можно отсчитывать историю появления Ceph в компании как хранилища для резервных копий Greenplum (и не только). Важное достоинство – отсутствие проблем при хранении большого числа объектов (и чем больше сегментов в кластере, тем больше и число объектов в бэкапе из-за распределения данных по всем сегментам кластера), фигурировало с самого начала как основная фишка данной технологии. К тому же, хотелось избегать в будущем хранилище бэкапов и других проблем, которые касались HDFS, а Ceph "на берегу" выглядел гораздо более подходящим для решения данной задачи решением. Забегая вперёд, скажем, что так оно и вышло – большинство проблем были устранены просто заменой технологии. Также ещё одним плюсом во внедрении Ceph является возможность использовать S3 и RBD – и мы этим активно пользуемся. Помимо этого, очень полезно иметь возможность разграничивать нагрузку во время резервного копирования. И в Ceph это реализуется посредством CRUSH Maps (подробнее об этом будет позже). Полезным свойством технологии также является возможность включения Erasure Coding, которое позволяет снизить стоимость хранения информации (кстати, эта функциональность есть и в HDFS 3.x). А это очень актуально при больших объёмах данных. Про то, почему мы используем Erasure Coding, будет рассказано далее. Подкрепить идею было решено путём практических экспериментов. Для пилотирования технологии был создан первый отдельный кластер Ceph, который состоял всего из трёх нод (ноды с OSD находились на одном сервере вместе с Monitor, MDS и т. д.). На этом этапе мы просто тестировали Ceph в нашей инфраструктуре как технологию, учились его разворачивать и провели первые опыты по резервному копированию в CephFS. В качестве кластера Greenplum выступал свежесобранный стенд с ADB 6.9.1 без нагрузки на 20 сегмент-нод (4 primary на ноду) с SAS SSD на борту (RAID 10 из 20 дисков), сеть в bond 2x10 Gbps, то есть пропускная способность довольно высокая. И первые же результаты нас поразили: скорость резервного копирования такого решения на БД размером 1,2 ТБ оказалось сопоставимой с HDFS! А на тот момент в кластере HDFS было 225 дата-нод, а в Ceph – всего 3. Поняв, что узким местом при работе с Ceph является сеть, было решено уменьшить размер резервной копии путём её сжатия, что дало во время пилотного запуска следующие результаты: Таких впечатляющих результатов уже было достаточно для того, чтобы убедиться в верности выбранного пути и продолжить исследования. На тот момент мы ещё не вдавались в подробности различных способов интеграции с Greenplum и использовали mount.ceph. А как оказалось, способов несколько. Итак, был собран первый кластер Ceph (тот самый, из трёх нод). Теперь нужно было принять решение, а каким именно образом обеспечивать его интеграцию с Greenplum. Существуют три способа это сделать: gpbackup_s3_plugin; mount.ceph; ceph-fuse. На первый взгляд, первый способ кажется самым предпочтительным, так как для его настройки нужно всего лишь разместить один конфигурационный файл на мастер-ноде и запустить процесс резервного копирования командой вида (сам плагин уже есть в поставке ADB): Второй способ (монтирование на уровне ядра) считается нативным для интеграции с Ceph, но требует установки дополнительного ПО на каждый из хостов кластера Greenplum, а также управления монтированием (которое доступно только привилегированному пользователю операционной системы). Третий способ является альтернативой второму, но изначально считается менее производительным. И мы проверили все из них. Начали с тестирования между mount.ceph и ceph-fuse, чтобы сразу выбрать оптимальный вариант из этой пары. Здесь из рассмотрения был исключён коннектор ceph-fuse, так как он: во-первых, не захотел корректно монтироваться без прав суперпользователя, как это делает hdfs-fuse (что не добавляет ему очков по сравнению с mount.ceph), а во-вторых, работает медленнее конкурента. К моменту проведения испытаний у нас уже не было в доступе исходного 20-нодового кластера, вместо этого мы проводили тестирование на dev-стенде ADB 6.13 (8 сегмент-нод, по 3 primary на ноду, диски SAS HDD 10К RAID 10 из 20 дисков) и мы получили следующие результаты при резервировании БД размером 1,3 ТБ (~4,5 тысячи объектов): Сам gpbackup запускался командой вида: Собственно, из-за разницы в скорости почти в два раза вариант использования ceph-fuse дальше и не рассматривался. Параллельно шли попытки запустить резервное копирование через S3 (здесь и далее будет идти речь про реализацию S3 в Ceph). Нам пришлось снизить число jobs до 4, backup_max_concurrent_requests до 500 и backup_multipart_chunksize_mb до 10, чтобы перестать ловить ошибки и получить положительный результат: эта же БД снялась за 1:27:46. Это и стало отправной точкой появления сомнений в производительности S3-решения, которое тогда решили "положить на полку". Тем более, что сразу был виден масштаб предстоящих доработок: В S3 нет классических каталогов файловой системы, там бакеты. А это потребует переделывания скриптов ротации резервных копий; Подобрать подходящее значение jobs под S3 для обеспечения стабильной работы (это число будет ниже, чем при работе с HDFS или mount.ceph). Тем временем, у нас строился новый кластер (5 сегмент-нод, по 16 primary на ноду, диски SAS HDD 10К RAID 10 из 20 дисков) под отдельный проект и там сразу планировалось применять решение с резервным копированием в Ceph. На нём мы ещё раз протестировали коннектор к S3 и mount.ceph. Полученные замеры показали (таблицы 1 и 2), что скорость второго решения заметно выше и его стабильность не зависит от размера резервной копии. Начали с небольшой БД в стандартные 10 потоков: Таблица 1. Время снятия резервной копии db1 (2.3 TB, ~5000 объектов)  Технология Время HDFS 1:15:00 S3 1:15:00 Mount 0:59:00 С большой БД число потоков уже было увеличено до 15:  Таблица 2. Время снятия резервной копии db2 (9.8 TB, ~800 объектов) Технология Время HDFS 3:58:00 S3 Ни разу не снялось Mount 4:21:00 Таким образом, скорость работы через gpbackup_s3_plugin не была быстрее mount.ceph, к тому же на БД "побольше" мы так и не получили успешных результатов (процесс gpbackup падал даже при заниженных значениях jobs). На этом было решено закончить пытаться настроить интеграцию Greenplum с CephS3, поскольку решение с монтированием работало и быстрее, и стабильнее. Сравнивая эти реализации уже на практике, были получены такие выводы: У монтирования раздела (CephFS) следующие преимущества по сравнению с HDFS: Решается "проблема" с Kerberos (у нас керберизованный кластер Hadoop, поэтому для работы с HDFS нужно использовать keytab, который нужно регулярно обновлять: если не делать этого, то спустя 8 часов работы полученное разрешение на доступ станет невалидным и процесс бэкапирования упадёт с ошибкой Permission Denied); Разгружается кластер Hadoop; Есть возможность отказаться от технологии FUSE при монтировании; Более удобное администрирование данных внутри CephFS (нативные команды операционной системы вместо использования клиента hdfs dfs). У монтирования раздела (CephFS) следующие преимущества, по сравнению с S3: Не нужны дополнительные балансировщики и шлюзы (Rados Gateway); Не будет происходить OOM gpbackup-процесса, если понадобится увеличить количество исполняемых заданий или уровень сжатия архивов; Более плавное использование сетевых интерфейсов, без просадок (так как каждый сегмент Greenplum при использовании CephFS соединяется напрямую с отдельными OSD, а не через шлюзы Rados Gateway). Схематично это выглядит так: Легче администрировать и искать ошибки; Более удобное администрирование данных внутри CephFS (нативные команды операционной системы вместо использования клиента s3cmd); Легче масштабируется (увеличение количества OSD повышает производительность решения). Таким образом, было решено использовать mount.ceph в качестве способа соединения с CephFS. Но, в целом, S3-плагин (после настройки) неплохо работает для резервного копирования БД небольших размеров, так что его тоже можно использовать, если объёмы данных не слишком большие. Так как у нас уже был кластер Ceph (3 ноды), мы решили не создавать новый производственный кластер, а переделать существующий. Что мы и сделали: вынесли на отдельные физические сервера мониторы и MDS, а RGW перенесли на vm и настроили через наш балансировщик (он у нас как отдельный сервис), MGR также перенесли на vm. По железу получилось следующее: 3 сервера Monitor – железные сервера; 2 сервере MDS – железные сервера; 3 сервера RGW – vm; 2 сервера MGR – vm; 21 сервер OSD – железные сервера с 10 или 12 OSD. Ввиду того, что во время настройки и самых первых испытаний мы полагались на положительный опыт, который получили как на тестовых наборах данных, так и на не очень больших базах (1-2Тб), то ожидалось, что простая смена технологии не должна была заметно ухудшить имеющиеся показатели скорости. Однако, на деле всё оказалось не так просто. Первые прогоны сценария резервного копирования на корпоративном хранилище данных показали не самые выдающиеся результаты (см. таблицу 3), однако решение показало себя стабильным, чего мы и добивались в первую очередь. Отметим, что так как система Greenplum находится в производственной эксплуатации, то и размер данных в ней постоянно варьируется. Но тем и интереснее полученные результаты, ведь среди них скрыт и ещё один важный момент на пути к успешному решению задачи – это командная работа, в том числе и со стороны разработчиков хранилища. Здесь это проявляется в том, что была проведена оптимизация, в рамках которой размер данных был сокращён на 8%. Таблица 3. Первые опытные результаты Хранилище Размер резервной копии Время бэкапирования HDFS 232 Tb 8:47:28 Ceph 239 Tb 15:12:45 Ceph 243 Tb 11:11:20 Ceph 225 Tb 10:15:42 Ceph 226 Tb 10:15:03 Получив самый первый результат в 15 часов (вместо ожидаемых хотя бы 9 часов), было понятно, что необходим тюнинг системы. Поэтому были сделаны следующие шаги: Увеличение самого кластера Ceph, добавление нод с OSD. Было добавлено 6 новых серверов с OSD, что ускорило процесс резервирования на 4 часа; Разделение CRUSH Map (чтобы исключить влияние бэкапов для других пользователей). Как говорилось ранее, кластер Ceph у нас используется не только для бэкапов, так что нам важно было разнести нагрузки, особенно после случая, когда запущенный нами бекап аффектил на другую нашу команду, которая пользовалась S3. Происходило это из-за того, что диски были полностью утилизированы бэкапом, а их пул жил на этих же дисках. Решение простое – это настраивать CRUSH Map. Но так как новые сервера (18 шт.) не одновременно вводились в кластер, то чаcть OSD от разных root была на одном сервере. Вот пример того, что у нас получилось: Настройка Erasure Coding Чтобы было возможно использовать как можно больше места, мы решили использовать Erasure Coding. Плюс – это больше доступного места для хранения, а минусы – то, что после создания такого пула его уже не получится переместить на другой root в CRUSH Map,  а также он более затратный для мощностей сервера и медленнее, чем реплика. И вот почему: EC распределяет данные между нодами, поэтому чтение или запись данных приводят к повышенной сетевой нагрузке; Блоки чётности генерируются при записи данных, что влияет на скорость записи. Это может быть медленнее, чем запись файла при факторе репликации = 1, но будет быстрее, чем при двух или более репликах; Если данные утеряны или повреждены, то в процессе восстановления данных происходит чтение оставшихся данных и блоков чётности. Этот процесс требует ресурсов CPU и сети. В нашем случае мы использовали следующие настройки: Коэффициент EC можно посмотреть командой: Для нашего пула с данными он равен 1.2857142686843872: Тюнинг плейсмент групп (далее – PG) для более равномерного распределения данных внутри пула. Для вашего пула с данными мы советуем сразу отключить autoscale pg. Дело в том, что если вы также его будете использовать для бекапа, то количество данных у вас будет постоянно меняться. А из-за этого он будет менять вам количество PG, что в свою очередь запустит ребаланс данных, чего вы точно не хотите делать, когда у вас вот-вот запустится следующий бэкап. Для нашего пула у нас используется PG_NUM= 4096. Это нам нужно для того, чтобы в PG было меньше данных и было более ровное распределение их между нашими OSD, что в свою очередь сильно уменьшает возможность полностью занять место (как у нас это было при PG_NUM=512).  Чтобы понять, почему так может произойти, давайте разберёмся, как считается свободное место в pool: где min_size_AVAIL – это наименьший доступный размер места в OSD из этого CRUSH Map. Число PG влияет на значение этого параметра; osd_count – число OSD в CRUSH Map, которой принадлежит пул; full_ratio – это коэффициент заполнения OSD (по умолчанию 0,95); replica_factor – фактор репликации (либо коэффициент EC). По этой формуле понятно, что чем лучше перераспределение, тем меньше будет перекашивать в меньшую сторону min_size_AVAIL, и тем больше доступно места в пуле.  Например, для нашего случая расчёт доступного места под размещение данных будет следующим: Интерконнект Интерконнект нужен, когда у вас большие объёмы, это в разы ускоряет ребалансы, а также положительно влияет на доступность OSD в целом. Всегда делайте его в Ceph и будет хорошо; Тюнинг OSD, а именно вынос Block.DB на SSD После того, как у нас появился отдельный CRUSH Map для бэкапа, высокая нагрузка на диски OSD никуда не исчезла. Проблема была в том, ни Block.WAL, ни Block.DB не были вынесены на отдельные диски (хотя для этого на серверах была возможность). Для каждой OSD были созданы lv на SSD-дисках (которые объединены в RAID1). Мы решили, что будем выносить только Block.DB, так как из документации следует, что Block.WAL будет совмещён с Block.DB автоматически.   Пример команд для миграции Block.DB: Эта процедура долгая, так как обрабатывается по одной OSD, а их может быть много. После каждого ввода OSD нужно дожидаться ребаланса. Поэтому чтобы сэкономить время, мы просто стёрли CephFS (в котором жили только тестовые бекапы, и их тогда было немного). А уже на свободных OSD мы всё это сделали быстро и автоматизировано через Ansible. Если данные критичны, то нужно выводить OSD по одной и долго переделывать. Поэтому рекомендуем обратить на это внимание заранее. Создание нескольких инстансов MDS на одном сервере, увеличение активного количества MDS и увеличение памяти. Проведём краткий экскурс: Для работы CephFS необходим MDS. Ceph MDS является сокращением от "сервера метаданных". Он требуется только для файловой системы Ceph (CephFS) и других методов хранения блоков. Хранение на основе объектов не требует служб MDS. Ceph MDS работает как демон, который позволяет клиентам монтировать файловую систему POSIX любого размера. MDS не предоставляет клиенту напрямую никаких данных; предоставление данных осуществляется OSD. MDS обеспечивает совместно используемую, согласованную файловую систему с интеллектуальным уровнем кэширования; следовательно, резко сокращая чтения и запись. MDS расширяет свои преимущества в направлении разделения динамичных поддеревьев и отдельных MDS для фрагментов метаданных. Он является динамичным по своей природе; демоны могут присоединяться и уходить, а также быстро поглощать отказавшие узлы. И сейчас он стал у нас узким местом, но теперь в Ceph его можно масштабировать, что мы и сделали. Как говорилось ранее, у нас два железных сервера MDS, на которых не было никакой нагрузки. Было решено не плодить сервера, а запустить на каждом по несколько инстансов MDS (у нас по 5 инстансов на каждом сервере, итого 10): Вот как это у нас выглядит: Также мы увеличили для всех инстансов mds_cache_memory_limit (рассчитали очень просто: mds_cache_memory_limit = общая память на сервере - 4 gb для системы / количество инстансов). Помимо этого, мы перенесли пул cephfs_metadata на отдельный CRUSH Map c SSD-дисками, что решило проблему с задержками доступа к этим данным. На стороне кластеров Greenplum были проведены следующие работы: Перенос серверов самого большого кластера Greenplum в один VLAN с Ceph – это позволило оптимизировать нагрузку на сетевое оборудование, что принесло больше пользы не столько бэкапам, сколько остальной платформе: мощные бэкапы перестали "съедать" сетевой канал у других проектов; Балансировка трафика внутри bond (layer2+3 в качестве xmit_hash_policy) позволила обеспечить равномерную загрузку обоих портов сетевой карты, что привело и к равномерной загрузке коммутаторов (в нашей топологии порты подключены к разным коммутаторам); Управление монтированием отдано на откуп утилите autofs, которая раскатывается по всем хостам ролью Ansible – масштабирование проще некуда; Внедрено сжатие данных алгоритмом zstd. Да, данный алгоритм можно использовать вне зависимости от используемого хранилища данных, но именно он внёс наибольший вклад именно в задачу ускорения резервного копирования. Проанализировав загрузку сетевых интерфейсов в поисках бутылочного горлышка системы, было видно, что во время снятия резервной копии сеть утилизируется по-максимуму, из чего был сделан вывод: "хотим быстрее решить задачу – нужно уменьшить число передаваемых по сети данных". Да, можно также проапрегрейдить и саму сеть, но мы решили пойти путём попроще и проверить на практике заявления о значительном превосходстве в скорости алгоритма zstd над классическим zlib. Посмотрим, как влияет алгоритм сжатия на время резервного копирования и размер полученной копии (замеры на Ceph): Пришлось переписать скрипты бэкапа, чтобы вынести из них ротацию и сбор статистики (с hdfs dfs-клиентом подсчёт размера резервной копии выполняется быстро, так как берутся метаданные из нейм-ноды, а удаление файлов выполняется асинхронно и тоже довольно быстро); Также из-за однажды возникшей проблемы с зависшими клиентами CephFS на некоторых сегмент-нодах Greenplum были внедрены доработки, которые не позволяют запуститься процессу резервного копирования, если Ceph недоступен хоть на одной ноде (с отправкой алерта). По итогам проделанной работы можно с уверенностью сказать, что полученная конфигурация стала не только надёжнее, но и быстрее, а также удобнее в управлении. Путём простого добавления нод OSD в кластер Ceph происходит его горизонтальное масштабирование, повышая пропускную способность и ёмкость решения. В новое хранилище мы собираем как ежедневные бэкапы с небольших баз, так и раз в неделю полную копию нашего большого кластера Greenplum (плюс инкрементальные бэкапы). Полученное решение эксплуатируется на постоянной основе уже более полугода, за которые было всего две проблемы: в первый раз переполнился Ceph (после чего перенастроили плейсмент группы), а во второй раз были зависшие клиенты CephFS(решается рестартом OSD). И обе проблемы были только с самым большим бэкапом, а БД поменьше (например, на 10-20 ТБ), вообще ни разу не привлекали к себе внимание. Для сравнения, конфигурация на HDFS не отличалась стабильностью даже на не очень больших базах данных. При этом мы больше не мешаем расчётам пользователей на кластере Hadoop и не расходуем в нём место. Таким образом, ещё и улучшилась бюджетная оценка решения. Ключевым достижением оказалась и скорость. Фактически, за счёт комплекса мероприятий мы сократили время бэкапирования почти в полтора раза (на примере самого большого стенда): Основными локомотивами прироста скорости можно выделить следующие мероприятия: добавление нод OSD в кластер Ceph; исключение из бэкапа части объектов (чистка мусора, удаление дублирующихся сущностей, исключение sandbox из резервной копии); переход на сжатие zstd уровня 3. То есть это – коллективная работа, требующая слаженной работы нескольких команд. Надеемся, что эта информация будет полезна для вас, поможет сэкономить массу времени и избежать ряд ошибок при проектировании подобной системы, с которыми мы столкнулись.  DBA / Системный архитектор