В свете того, что Zabbix с некоторых пор поддерживает TimescaleDB, а тут еще и вышел новый LTS релиз Zabbix, то наверняка многие заинтересовались, как осуществить миграцию с MySQL на PostgreSQL.
Несмотря на текст на картинке, вполне можно просто так взять и мигрировать Zabbix с MySQL на PostgreSQL. В интернете есть немало рецептов такой миграции, например:
Доклад с Zabbix Meetup
Видео с Youtube канала Dmitry Lambert
Но я не нашел ни в одном из таких докладов информации, как же минимизировать downtime вашего мониторинга в случае, если полная миграция занимает несколько часов, что конечно зачастую является неприемлемым.
Ниже я опишу свое решение данной проблемы и те подводные камни, которые пришлось обходить по пути.
Важно упомянуть, что я до сих использую Zabbix 4.0. Возможно, в новых версиях схема БД поменялась и поэтапная миграция, описанная ниже, там невозможна.
Основная идея
Как известно, основной объем базы данных Zabbix сервера занимают исторические данные: таблицы истории и трендов (далее для краткости я буду называть эти данные историей). При этом сам Zabbix сервер способен без проблем запуститься и без этих данных. Сразу же возникает мысль: а что если при миграции для начала проигнорировать историю, запустить мониторинг, а уже затем разобраться с историей?
Подготовка
Я не буду по ходу статьи описывать все команды, например, как установить PostgreSQL. Думаю, в этой статье оно ни к чему, да и каждый сам может с этим справиться.
Установим нужное ПО:
- PostgreSQL, в моем случае версию 12.
- PgLoader 3.6.2. На версии 3.6.1 я мигрировать не смог из-за возникающих проблем.
Необходимо настроить PostgreSQL сервер и операционную систему, на которой он установлен.
Я также не буду подробно останавливаться на этой теме, так как пост не о тюнинге производительности, скажу лишь:
- Очень рекомендую доклад Ильи Космодемьянского для общего понимания, в какую сторону тюнить PostgreSQL и ОС: https://habr.com/ru/post/505108/
- Посмотрите, что рекомендует этот сервис: https://pgtune.leopard.in.ua/
- Посмотрите описание параметров в документации: https://postgrespro.ru/docs/postgrespro/12/index.html
Также нужно до начала миграции настроить мониторинг PostgreSQL.
Так как я еще не переехал с версии Zabbix 4.0, то новеньким официальным шаблоном воспользоваться не могу, но мне отлично подошел вот этот:
https://github.com/lesovsky/zabbix-extensions/tree/master/files/postgresql
Кроме этого, непосредственно перед началом миграции лучше отключить все способы оповещения в Zabbix, чтобы не потревожить людей сработавшими триггерами nodata, т.к. при первом после миграции запуске исторических данных в базе у нас не будет.
Ну и обязательно делайте резервные копии и проверяйте весь процесс на тестовом окружении. Собственно, все приключения я получил именно на нем.
Последний момент подготовки перед миграцией — останавливаем Zabbix server:
systemctl stop zabbix-server
Первый шаг миграции
Для начала, как и в любом другом мануале по миграции Zabbix, разделим файл schema.sql, расположенный в директории database/postgresql исходников zabbix, на 2 части:
в одной у нас будут CREATE, в другой ALTER.
На выходе имеем 2 файла: create.sql, alter.sql
Применим файл create.sql к нашей БД:
cat create.sql | psql -Uzabbix zabbix
Подготовим файл для pgloader — zabbix.load.config
LOAD DATABASE
FROM mysql://zabbix:zabbix-password@localhost/zabbix
INTO postgresql://zabbix:zabbix-password@192.168.1.1:5432/zabbix
WITH include no drop,
truncate,
create no tables,
create no indexes,
no foreign keys,
reset sequences,
data only
SET MySQL PARAMETERS
max_execution_time = '0'
SET PostgreSQL PARAMETERS
maintenance_work_mem TO '1024MB', work_mem to '128MB'
EXCLUDING TABLE NAMES MATCHING ~/history.*/, ~/trend.*/
ALTER SCHEMA 'zabbix' RENAME TO 'public';
Обратите внимание на строку
EXCLUDING TABLE NAMES MATCHING ~/history.*/, ~/trend.*/
с ее помощью мы пропускаем таблицы истории
Запустим миграцию:
pgloader zabbix.load.config
Ждем буквально минуту, проверяем, что ошибок при миграции нет (символ галки во втором столбце):
Применим файл alter.sql к нашей БД:
cat alter.sql | psql -Uzabbix zabbix
Полагаю, важно, что в файле alter.sql нет никаких упоминаний таблиц trend и history. По крайней мере в версии 4.0 именно так обстоят дела. Наверное, при ином раскладе были бы проблемы, т.к. в существующих в сети мануалах по миграции данные сначала загружаются в БД, а потом применяется alter.sql
Удалим пакеты Zabbix, связанные с MySQL и заменим их теми, что нужны для PostgreSQL. На этот раз я приведу примеры для CentOS 7, чтобы было понятно, какие пакеты нам нужны:
yum remove zabbix-server-mysql zabbix-web-mysql
yum install zabbix-server-pgsql zabbix-web-pgsql php-pgsql
А лучше смотрите официальную документацию по установке для вашей ОС:
https://www.zabbix.com/documentation/4.0/manual/installation/install_from_packages
Сбросим настройки Web интерфейса Zabbix, чтобы заново пройти его настройку:
rm /etc/zabbix/web/zabbix.conf.php
Укажем часовой пояс в файле:
/etc/httpd/conf.d/zabbix.conf
Настроим новый zabbix_server.conf: укажем нужные значения в директивах DBHost, DBPort, DBUser, DBName, DBPassword.
Настала пора запустить наш Zabbix Server!
systemctl start zabbix-server
Идем по адресу web-интерфейса zabbix (http(s)://ip/zabbix), проходим по шагам мастера.
После настройки web-интерфейса вы увидите, что все ваши узлы, шаблоны, группы и т.п. будут на месте. Не будет только исторических данных. Вы можете в этом убедиться, посмотрев любые графики за прошлые периоды — там пусто.
Теперь можно проследить, что данные от ваших проверок начали поступать на сервер, что ошибок в логах нет, что ложные срабатывания триггеров отсутствуют. Как только вы поняли, что ситуация под контролем, можете включить оповещения, отключенные на этапе подготовки.
Можно немного выдохнуть — мониторинг опять заработал.
Второй шаг миграции
Теперь надо просто запустить миграцию истории, но чтобы этот процесс не сильно нагружал СУБД.
Так думал я, но все оказалось интереснее.
Перед вторым шагом миграции я решил создать в моем тестовом окружении нагрузку на Zabbix сервер. В тестовом окружении отсутствовали Zabbix прокси (это логично, так как мои основные прокси с тестовым сервером работать не намерены), так что нагрузку я решил создавать на самом сервере. Для этого с помощью API я создал около 200 хостов и попросил Zabbix сервер совершать проверки icmp и web checks к этим хостам каждую секунду. Получил около 1000 NVPS.
Очереди не росли, Zabbix server и PostgreSQL легко справлялись с такой нагрузкой.
Пора мигрировать историю.
Подготовим файл zabbix.load.data для pgloader:
LOAD DATABASE
FROM mysql://zabbix:zabbix-password@localhost/zabbix
INTO postgresql://zabbix:zabbix-password@192.168.1.1:5432/zabbix
WITH include no drop,
no truncate,
create no tables,
create no indexes,
no foreign keys,
reset sequences,
data only,
prefetch rows = 5000,
multiple readers per thread
SET MySQL PARAMETERS
max_execution_time = '0',
net_read_timeout = ‘86400’,
net_write_timeout = ‘86400’
SET PostgreSQL PARAMETERS
maintenance_work_mem TO '1024MB', work_mem to '128MB'
INCLUDING ONLY TABLE NAMES MATCHING ~/history.*/, ~/trend.*/
ALTER SCHEMA 'zabbix' RENAME TO 'public';
Обратите внимание на строки:
no truncate
говорит о том, что при начале миграции не будут очищены все мигрируемые таблицы. Ведь в истории уже появляются данные, так как мониторинг запущен.
INCLUDING ONLY TABLE NAMES MATCHING ~/history.*/, ~/trend.*/
говорит о том, что мигрировать в этот раз будут только таблицы истории.
Запускаем миграцию
pgloader zabbix.load.data
Миграция истории началась, процесс этот может быть долгим. В моем случае при размере базы около 150 Гб и не самом сильном железе миграция истории занимает 4-5 часов.
Поначалу все шло хорошо. Потом я обнаружил, что загрузка CPU на PostgreSQL сервере растет линейно, и со временем ресурсов для нормальной работы уже не хватало (на графике load average на 1 ядро):
В логи Zabbix сервера сплошным полотном начали сыпаться медленные запросы SELECT. Как я понял, для того, чтобы совершить web check, нужно для начала сделать SELECT.
Я попробовал снизить интенсивность миграции. Для этого изменил следующие параметры в файле zabbix.load.data:
prefetch_rows = 1000,
workers = 1,
concurrency = 1,
single reader per thread
Но с такими настройками не поменялось ничего, кроме скорости миграции. Нагрузка росла также линейно со временем.
Ну не зря же я обвесил PostgreSQL мониторингом. Изучаю его показатели и вижу, что линейный рост нагрузки явно совпадал с продолжительностью транзакции, которую создал pgloader:
А также с количеством tuples, которые возвращала PostgreSQL клиентам в ответ на их запросы:
Идем в интернет, ищем информацию о том, как ведет себя PostgreSQL при длинных транзакциях. И находим вот такой замечательный доклад с конференции HighLoad:
https://www.youtube.com/watch?v=3h48iowNbwo
Советую посмотреть доклад, но если вкратце, то при наличии длинной транзакции в PostgreSQL не срабатывает механизм внутристраничной очистки (single-page cleanup). И это приводит к росту тех самых возвращаемых tuple. СУБД приходится разбираться, какой tuple ей нужен, что ведет к росту загрузки CPU.
Я проверил мою ситуацию — действительно, достаточно открыть транзакцию, создать в ней таблицу, а затем НЕ закрыть транзакцию:
begin;
create table x(n numeric);
У спустя некоторое время видим рост загрузки CPU:
Падение загрузки в конце графика — это закрытая вручную транзакция.
Видимо, с этой особенностью придется мириться. Что можно сделать? Например, мигрировать историю отдельно, тренды отдельно, чтобы хоть немного уменьшить продолжительность транзакции.
Но это не всё. У меня все таки получилось сделать так, чтобы эта особенность PostgreSQL не мешала.
Немного о моей реальной инсталляции Zabbix. На ней почти все задачи сбора данных делегированы на Zabbix прокси. Сервер занимается лишь приемом данных и всякими триггерами/препроцессингом. Но как я написал выше, на тестовой инсталляции все проверки исполнял сам сервер.
Я вынес все задачи сбора данных на прокси в тестовой инсталляции, сильно снизив нагрузку на Zabbix Server и PostgreSQL. И оказалось, что это помогло! После этого никакая длинная транзакция не создавала сверхвысокой нагрузки на CPU, не мешала мониторингу нормально работать. Ну либо по какой-то причине внутристраничная очистка PostgreSQL при таком сценарии заработала. Может быть знатоки PostgreSQL подскажут?
Заключение
Как оказалось, произвести миграцию Zabbix с MySQL на PostgreSQL с минимизацией downtime лишь немного сложнее стандартной миграции. Делайте тестовую инсталяцию, экспериментируйте, и все получится.
Спасибо всем, кто дочитал пост до конца. Надеюсь, он кому-то поможет спланировать и оттестировать миграцию.
Ну и конечно жду feedback, может быть более опытные коллеги укажут на недочеты.
awsswa59
Я по этой статья прикручивал TimescaleDB к Zabbix
awsswa.livejournal.com/45940.html