Все доброго времени суток!
Как известно, недавно вышел PostgreSQL 9.2 с массой интересных и полезных вещей. Недолго думая мы решили обновить наш кластер потоковой репликации с 9.0 на 9.2. Все бы ничего, если бы не несколько обстоятельств:
- это продакшен с большой суточной посещаемостью.
- даунтайм исключен.
Чтож, так даже интересней… Как мы это делали и что из этого вышло читайте дальше.
Зачем все это?
- желание заполучить плюшки появившиеся в PostgreSQL 9.2;
- перевод PostgreSQL мастера на сервер с flashcache.
Дано:
- 3 аппаратных сервера, на двух из которых размещена связка мастер+слейв 9.0 и еще один ничем незанятый сервер с flashcache;
- связка реплицируется с помощью встроенной потоковой репликации;
- с серверами БД постоянно работают четыре бэкенда с приложением и машина со sphinx'ом.
Сложности:
- кластер не заработает если просто обновить пакет с 9.0 на 9.2 (кластер следует переинициализировать или обновить);
- pg_upgrade невозможен без остановки кластера;
- переинициализация и последующий pg_restore тоже делать нельзя, ибо даунтайм;
- нельзя сначала обновить мастер, а потом обновить слейв. Потоковая репликация между мажорными версиями не будет работать.
Какой мы нашли выход?
Выход в использовании Londiste из пакета Skytools-3.0, однажды мы уже переезжали с его помощью с 8.4 на 9.0, поэтому опыт есть. Репликация с Londiste удобна тем что позволяет реплицировать отдельные таблицы и базы в кластере (к примеру потоковая репликация, реплицирует целиком кластер). Плюс ко всему относительно прошлого переезда у нас появилась потоковая репликация. И это тоже не проблема. Данные среплицированные через Londiste будем тут же реплицировать на свежеподнятый слейв 9.2 с помощью потоковой репликации. Отличная выходит схема: среплицировавшись на 9.2 мы прозрачно заполним данными слейв 9.2. Итак схема и алгоритм задачи:
1. Админская часть:
- поднимаем мастер и слейв 9.2. Cлейв 9.2 запускаем на порту 6543, так как стандартный порт уже занят (см. картинку);
- поднимаем между ними потоковую репликацию;
- устанавливаем Skytools на свеженастроенном мастере 9.2;
- конфигурируем Londiste. Из мастера 9.0 делаем провайдера, из мастера 9.2 делаем подписчика;
- запускаем на мастере 9.2 londiste и pgqd, затем проверяем работоспособность связки встроенными в londiste средствами;
- на стороне провайдера добавляем в репликацию все таблицы и секвенции(стоить отметить что способными к репликации таблицами, являются только те, у которые есть первичные ключи. Если обнаружились таблицы без ключей, то следует создать там ключи или придется переносить их руками… у нас была часть схем, которые было дешевле перенести руками, чем создавать там ключи);
- определяем те схемы и таблицы которые нужно будет перенести руками;
- на подписчике запускаем репликацию тестовой таблицы и по логам убеждаемся что данные с провайдера 9.0 попадают на подписчика 9.2 и далее по потоковой репликации добираются до слейва 9.2.
Итак с технической стороны все готово. Теперь остается спланировать ход выполнения репликации и момент переключения. Днем переключения выбрана суббота. Если что-то пойдет не так, у нас остается воскресенье. Мероприятия мы разбили на две стадии, подготовительная стадия и стадия переключения. Как же будет выполняться переключение? Для этого мы ввели два новых DNS-имени для новой связки 9.2: db-master и db-slave. В нужный момент мы пропишем эти имена в конфиги бэкендов и перезапустим приложения.
Часть мероприятий подготовительного плана уже описана выше, но для полноты картины я их все же оставлю в кратком виде:
До пятницы:
- поднимаем новый кластер pg-9.2;
- настраиваем londiste между pg-9.0 и pg-9.2;
- поднимаем новый slave.pg-9.2 на соседнем порту и настраиваем потоковую репликацию c master.pg-9.2;
- подготовить свежие отчеты pgfouine с мастера 9.0 и слейва 9.0 за любой будний день. Отчеты понадобятся для сравнения планов топовых запросов;
- добавить в провайдера londiste все схемы, которые не потребуют создания первичных ключей;
- проверить со всех бекендов возможность подключения к новым инстансам PostgreSQL;
- перепроверить конфиги для новых баз, лимиты коннектов, настройки автовакуума;
- настроить мониторинг для новых баз (используем zabbix вкупе с самописными bash-скриптами дергающими таблицы pg_stat*);
- для новых баз создать dns-имена db-master и db-slave;
- предупредить редакцию о проведении работ в субботу (это просто преупреждение верхов чтоб были готовы, и не задавали вопросы в случае чего).
Пятница:
- отключить ночной импорт (это адский импорт данных, мехнизм таков что 100% ломает londiste-репликацию. Это внутренняя кухня проекта, но я упоминаю ее потому что в любом проекте может найтись аналогичный компонент, поэтому стоит учитывать все элементы которые затрагивают цель операции — базу данных);
- запустить перенос данных через londistе. При добавлении таблицы в подписчике, запускается механизм репликации посредством COPY, после чего фиксируется консистентное состояние таблицы и она считается реплицируемой);
- подготовить список схем для ручного переноса;
Суббота: это день переключения:
- проверить топ 10 запросов с мастера и слейва на 9.2 (может игра вобще не стоит свеч?);
- подготовить команды для ручного переноса схем (тупо забить их в консоли и в нужный момент нажать Enter);
11.00-12.00 приостановка редактирования:
- остановить кроны, демон фоновых задач и дождаться завершения активных задач;
- закрыть редактирование (в этот момент со стороны приложения невозможно совершить операции записи в БД, таким образом мы избежим правки базы со стороны клиентов сайта, и риска получения неконсистентных данных в момент перезапуска приложений на бекендах);
- перенести дампом оставшиеся схемы в новую базу;
12.00-12.30 переключение:
- разваливаем londiste репликацию (выводим таблицы, секвенции, ноды, останавливаем londiste и pgqd);
- поправить конфиги на бэкендах;
- перезапустить приложения на бэкендах (nginx+passenger);
- обновить конфиг для sphinx и перезапустить его;
Всё. после этого репликация через londiste становится неконсистентной, так как вся внешняя запись (источник записи — клиенты на сайте) пошла в кластер 9.2;
- поправить конфигурацию демона фоновых задач и запустить его. запустить кроны;
- открыть редактирование;
- открыть все мониторинги и искать возможные косяки.
После переключения:
- включить ночной импорт;
- проверить логи крона, логи демона фоновых задач, лаг репликации.
После переезда
- перенести db-slave на стандартный порт, для этого:
- бэкенды работающие со слейвом переключить на работу с мастером;
- выключить pg-9.0;
- настроить новый pg-9.2 на работу с полным объемом памяти (незабываем что на хосте было 2 инстанса PostgreSQL, поэтому пришлось поделить между ними память);
- запустить db-slave на порту 5432, проверить подключение с бэкендов и со sphinx'а;
- проверить целостность и лаг репликации;
- ввести в строй слейв на стороне бэкендов.
Откат. План на случай если что-то пойдет не так:
- закрыть редактирование;
- остановить демон фоновых задач и кроны, дождаться завершения активных задач;
- исправить в конфигах имена серверов БД на оригинальные, перезапустить бэкенды, запустить демон фоновых задач;
- откатить конфиг для sphinx;
- открыть редактирование.
Собственно весь алгоритм. По ходу мероприятия конечно же не все прошло в соответствии с генеральным планом. К счастью прибегнуть к плану отката не пришлось.
Если говорить о том что пошло не так, то тут всего пара пунктов,
первый пункт касается недавно введенного в строй сервиса и механизма ручного переноса схем (которого вобще желательно избегать). Пара слов о сервисе: завалился сервис основанный на работе pgq, несовсем было понятно как реплицировать схему pgq (pgq являлся сам частью механизма репликации). Перенос вручную тоже не исправил ситуацию, поэтому пришлось переинициализировать схему и перезапускать сервис (благо это некритично, но все же косяк).
Про перенос схем… практика показала что перенос схем не всегда проходит как хочется. Учитывая что схема всей базы создается на раннем этапе настройки репликации, в дальнейшем приходится переносить либо схему поверх существующих объектов, либо отдельные данные, то в ходе переноса можно напороться на ошибки типа:
ERROR: insert or update on table violates foreign key constraint
DETAIL: Key is not present in table.
Отсюда вывод что перенос схем лучше делать так:
Переименовываем существующуюю пустую схему в базе назначения, затем переносим полностью схему из источника, удаляем из базы назначения старую переименованую схему. Проверка одинаковости схем, можно осуществить через bash конструкцию. Команду запускаем на обоих хостах, вывод сравниваем на предмет соответствия (использовать diff)
# for i in schema_1 schema_2 schema_3; do psql -ltAF. -U postgres -c "dt $i." db_name |cut -d. -f1,2 ; done |while read line ; do echo "$line" - $(psql -qAtX -U postgres -c "select count() from $line" db_name); done
В конце конечно хочется отметить что нужно на несколько раз проверить все места где может появиться запись в базу и исключить вероятность записи при переключении, когда часть сервисов/бэкендов уже переключилась на новую базу, а другая часть еще нет. Если рассуждать еще дальше, то теоретически можно уж совсем перевести том в readonly, и выполнять переключение (mount/dmsetup/blockdev).
Ну и чуть-чуть графиков.
1. NewRelic. Процесс переключения бэкендов
2. Zabbix. Суточная работа сервера с PG 9.0 (понедельник 10 сентября)
3. Zabbix. Дневная работа сервера с PG 9.0 (понедельник 10 сентября)
4. Zabbix. Суточная работа сервера с PG 9.2 + FlashCache (понедельник 17 сентября)
5. Zabbix. Дневная работа сервера с PG 9.2 + FlashCache (понедельник 17 сентября)
Самое большое зло в графиках Zabbix, это черная линия, отражающая iowait. Как видно, использование flashcache позволило значительно снизить нагрузку на жесткие диски.
Кому интересны технические детали:
как настривается потоковая репликация в PostgreSQL, смотреть здесь.
как настривается потабличная репликация между класетрами PostgreSQL с помощью Skytool-3, смотреть здесь.
Вот такая история одного субботника. Спасибо за внимание!
Автор: lesovsky
очень прилично!
можно графики кликабельными, пжлст?