Расширение кластера PostgreSQL размером 5,7 ТБ и переход с версии 9.6 на 12.4

Автор оригинала: Tommy Li
  • Перевод

Фото Ричарда Джекобса на Unsplash

В ноябре 2020 года мы начали крупную миграцию для обновления кластера PostgreSQL с версии 9.6 на 12.4. В этом посте я вкратце расскажу про нашу архитектуру в компании Coffee Meets Bagel, объясню, как даунтайм апгрейда удалось снизить ниже 30 минут, и расскажу про то, что мы узнали в процессе.

Архитектура


Для справки: Coffee Meets Bagel — это приложение для романтических знакомств с системой курирования. Каждый день наши пользователи получают в полдень по их часовому поясу ограниченную партию высококачественных кандидатов. Это приводит к хорошо предсказуемым закономерностям нагрузки. Если посмотреть данные за последнюю неделю от момента написания статьи, у нас в среднем получается 30 тысяч транзакций в секунду, в пике — до 65 тысяч.



До обновления у нас работали 6 серверов Postgres на инстансах i3.8xlarge в AWS. Они содержали одну главную ноду, три реплики для раздачи веб-трафика только для чтения, балансируемые с помощью HAProxy, один сервер для асинхронных воркеров и один сервер для ETL [Extract, Transform, Load] и Business Intelligence.

Для поддержания парка реплик в актуальном состоянии мы полагаемся на встроенную в Postgres потоковую репликацию.



Причины для апгрейда


Последние несколько лет мы заметно игнорировали наш уровень данных, и как результат, он слегка устарел. Особенно много «костылей» поднабрал в себя наш основной сервер — он находится в онлайне уже 3,5 года. Различные системные библиотеки и службы мы патчим без остановок сервера.


Мой кандидат для подреддита r/uptimeporn

Как итог, накопилось множество странностей, которые заставляют понервничать. К примеру, новые сервисы в systemd не запускаются. Пришлось настраивать запуск агента datadog в сессии screen. Иногда SSH переставал отвечать при загрузке процессора выше 50 %, а сам сервер исправно отдавал запросы базы данных.

А ещё свободное место на диске начало подходить к опасным значениям. Как я упоминал выше, Postgres работал на инстансах i3.8xlarge в EC2, у которых 7,6 ТБ хранилища NVMe. В отличие от EBS, здесь размер диска динамически менять нельзя — что заложено изначально, то и будет. И мы заполнили примерно 75 % диска. Стало понятно, что для поддержания будущего роста размер инстанса придётся менять.

Наши требования


  1. Минимальный даунтайм. Мы поставили целью ограничение в 4 часа суммарного даунтайма, включая незапланированные отключения, вызванные ошибками при обновлении.
  2. Собрать новый кластер баз данных на новых инстансах для замены текущего парка стареющих серверов.
  3. Перейти на i3.16xlarge, чтобы был простор для роста.

Нам известны три способа выполнить обновление Postgres: создание резервной копии и восстановление из неё, pg_upgrade и логическая репликация pglogical.

От первого способа, восстановления из резервной копии, мы отказались сразу: для нашего датасета на 5,7 ТБ он занял бы слишком много времени. При своей скорости приложение pg_upgrade не удовлетворяло требованиям 2 и 3: это инструмент для миграции на той же машине. Поэтому мы выбрали логическую репликацию.

Наш процесс


Про ключевые особенности работы pglogical написано уже достаточно. Поэтому вместо повторения прописных истин я просто приведу статьи, которые оказались полезными для меня:


Мы создали новый primary-сервер Postgres 12 и с помощью pglogical синхронизировали все наши данные. Когда он синхронизировался и перешёл к репликации входящих изменений, мы начали добавлять за него потоковые реплики. После настройки новой потоковой реплики мы включали её в HAProxy, а одну из старых версии 9.6 удаляли.

Этот процесс продолжался до полного отключения серверов Postgres 9.6, кроме мастера. Конфигурация приняла следующий вид.



Затем настал черёд переключения кластера (failover), на что мы запросили окно технических работ. Процесс переключения тоже хорошо задокументирован в Интернете, поэтому я расскажу лишь про общие шаги:

  1. Перевод сайта в режим технических работ;
  2. Смена записей DNS мастера на новый сервер;
  3. Принудительная синхронизация всех последовательностей (sequences) первичных ключей (primary key);
  4. Ручной запуск контрольной точки (CHECKPOINT) на старом мастере.
  5. На новом мастере — выполнение некоторых процедур валидации данных и тестов;
  6. Включение сайта.

В целом, переход прошёл отлично. Несмотря на столь крупные изменения в нашей инфраструктуре, незапланированного даунтайма не случилось.

Извлечённые уроки


При общем успехе операции пара проблем по пути всё же встретилась. Самая страшная из них чуть не убила наш мастер Postgres 9.6…

Урок №1: медленная синхронизация может быть опасной


Обозначим для начала контекст: как работает pglogical? Процесс передачи (sender) на поставщике (provider, в данном случае наш старый мастер 9.6) декодирует упреждающий журнал WAL [write-ahead log], извлекает логические изменения и посылает их на подписчика (subscriber).

Если подписчик отстаёт, то поставщик будет хранить сегменты WAL, чтобы когда подписчик его нагонит, никаких данных не потерялось.

При первом добавлении таблицы в поток репликации приложению pglogical сначала нужно синхронизировать данные таблицы. Это выполняется с помощью команды Postgres COPY. После этого сегменты WAL начинают копиться на поставщике, чтобы изменения за время работы COPY получилось передать на подписчика после изначальной синхронизации, гарантируя отсутствие потери данных.

На практике это означает, что при синхронизации большой таблицы на системе с большой нагрузкой по записям/изменениям нужно тщательно следить за использованием диска. При первой попытке синхронизации нашей самой крупной (4 ТБ) таблицы команда с оператором COPY работала больше суток. За это время на ноде поставщика набралось больше одного терабайта упреждающих журналов WAL.

Как вы можете помнить по уже сказанному, на наших старых серверах баз данных оставалось всего по два терабайта свободного дискового пространства. Мы оценили по заполненности диска сервера подписчика, что скопировалась всего четверть таблицы. Поэтому процесс синхронизации пришлось немедленно остановить — диск на мастере кончился бы раньше.


Доступное дисковое пространство на старом мастере при первой попытке синхронизации

Чтобы ускорить процесс синхронизации, мы внесли следующие изменения в базу данных подписчика:

  • Удалили все индексы в синхронизируемой таблице;
  • fsynch переключили на off;
  • Поменяли max_wal_size на 50GB;
  • Поменяли checkpoint_timeout на 1h.

Эти четыре действия значительно ускорили процесс синхронизации на подписчике, и наша вторая попытка синхронизации таблицы завершилась за 8 часов.

Урок №2: каждое изменение строк журналируется как конфликт


Когда pglogical обнаруживает конфликт, приложение оставляет в логах запись вида «CONFLICT: remote UPDATE on relation PUBLIC.foo. Resolution: apply_remote».

Однако выяснилось, что каждое обработанное подписчиком изменение строк вносилось в журнал как конфликт. За несколько часов репликации база данных подписчика оставляла после себя гигабайты файлов логов с конфликтами.

Эту проблему удалось решить заданием параметра pglogical.conflict_log_level = DEBUG в файле postgresql.conf.

Об авторе


Томми Ли — старший инженер программного обеспечения в компании Coffee Meets Bagel. До этого он работал в Microsoft и канадском производителе систем автоматизирования бухгалтерского учёта Wave HQ.
Альфа-Банк
Компания

Комментарии 14

    +6

    Опыт ценный, но по всей статье красные флаги.


    • СУБД для толстой базы с >50% утилизацией диска. Если диск занят на >50% такой сервер не может хранить вторую копию данных. Невозможно иметь "старую базу в соседнем каталоге когда бэкап восстанавливается", невозможно сделать копию базы для каких-то экспериментов или разбирательств. Только сеть, а она на таких объёмах ой какая медленная. И никакого быстрого mv.
    • Апгрейд развёртыванием бэкапа не был опцией из-за сроков, что означает, что у них и восстановление из бэкапов тоже по срокам не проходит. Если восстановить из бэкапов за разумное время нельзя, то ой, нарушение SLO в полный рост. TTR больше допустимого в разы. Ещё мне показалось, что они эти бэкапы не тестируют (хотя я могу клеветать, извините).
    • отключение fsync на продакшен базе. Это уже вообще leap of faith (если я зажмурив глаза буду быстро-быстро перебегать хайвей, то меня не собьют — я так уже два раза делал и всё ок!).

    В целом — классический геоизм по определению "героизм одних это последствия халатности других".

      +5
      fsync же был на подписчике на время копирования отключён. В этом нет ничего страшного. В худшем случае, при сбое подписчика пришлось бы просто всё заново прогонять. А после окончания копирования они наверняка fsync включили. Да, и вероятность крэша сервера подписчика низкая, и самое главное, в случае крэша даже с fsync доверия к результату копирования с крэшем быть не может — раз крэш был, то там уже что-то где-то некорректно представлено.
      В общем, это совершенно нормальная практика — подписчик ещё не является продакшином. Он станет продакшином после окончания копирования, настройки и прогонки тестов — вот, тогда ай-ай-ай за такие фокусы. И то в тяжёлый случаях вполне можно вывести сервер из работы, сделать грязное дело без fsync и вернуть в работу.
        +2

        А, пропустил. Да, на непродакшен сервере отключить fsync — это нормально. Главное, не забыть обратно включить.

        +2
        А что, pglogical — он горизонтально масштабируется? Просто эти вот упомянутые единицы терабайт — на сегодня далеко не предел. Можно построить репликацию, чтобы процессов было много, и на разных машинах? До какого предела?
          0
          Ну так они походу под базу вообще instance storage используют в raid 0
          0
          >При первой попытке синхронизации нашей самой крупной (4 ТБ) таблицы
          А партициирование бы помогло?
            0

            Как страшно жить. В 2021 году 5 терабайт БД мигрируется больше суток. А ведь 5 ТБ — это как 2 винчестера современного ноутбука или как один гейминг-комп. И, главное, свой личный опыт подсказывает, что все эти цифры вполне реалистичны, эффективнее фиг получится сделать нахрапом. Куда-то мы все не туда идем, ох не туда… как бы не вымереть, как динозавры вымерли.

              0
              Не очень понятно, зачем эта морока с одновременным апгрейдом postgres и переездом на новые сервера? Что мешало сначала поднять копию инфраструктуры на той же версии postgres при помощи обычной потоковой репликации, а затем быстренько ее проапгрейдить pg_upgrade?
                +1
                Судя по вашим рассуждениям видно что вы никогда не мигрировали с одной версии на другую, более менее приличный объем данных. Без остановки продакшена на сутки и более… К тому же эта чудо утилита то же достаточно странно работает. А когда разница между версиями 3 мажорных релиза, не известно как она отработает. Чего стоит переименование всех функций относящихся к wal и самих журналов, все критичные изменения не вспомню. Но из за этих мониторинг пришлось в срочном порядке допиливать.
                  0
                  Лично мы в 2020 делали апгрейд с Postgres 9.6 до Postgres 11. БД примерно на 1ТБ (сейчас уже почти 1.5ТБ). Как раз переносили БД с одного сервера на другой. Ещё у нас есть три реплики.

                  Всё сделали с помощью pgbasebackup + pg_upgrade. А точнее так:
                  1) На новом сервере установили postgres 9.6
                  2) С помощью pg_basebackup создали ещё одну реплику на новом сервере с помощью потоковой репликации
                  3) Когда пришло время, погасили прод-сервер
                  4) Удалили на новом мастере recovery.conf, сделали на нём апгрейд с 9.6 до 11 с помощью pg_upgrade
                  5) Переключили приложение на новый сервер БД
                  6) Сделали ещё три реплики

                  На всё ушло менее двух часов. Так что я бы не сказал, что обязательно уйдёт много времени. Всё зависит от сети и скорости дисков.
                    0
                    Я регулярно, с 9.6 версии, мигрирую пяток проектов, объемы баз 1-7 ТБ. Именно по озвученной схеме. Через три релиза — да, не пробовал, мы мигрируем на каждый новый релиз (не всегда меняя сервер), но я не думаю что возникнут какие-то кардинальные проблемы. В любом случае, всегда есть возможность при каких-то проблемах откатиться на старый сервер. Чудо-утилита с магическим ключиком -k работает очень быстро и не подводила ни разу. Использование pglogical никак не поможет при проблемах c переименованием xlog в wal или необходимостьи переделывать мониторинг.
                  0
                  pglogical для копирования синхронизации таблиц это тот еще инструмент. Не знаю как сейчас, а в свое время при попытке попользоваться им при миграции с 9.5 на 10. Вышло все печально, просто впустую потраченное время 2 суток на таблицу в 1,7ТБ, потом стало заканчиваться место на диске и пришлось это безобразие остановить. Взять londiste и без проблем перенести данные за вменяемое время. Без ужимок и прыжков с логами, ошибок в них от этой чудесной штуки. И снежного кома не удаленных wal журналов, которые в итоге чуть не остановили сервер(linux если выбрать место на диске в 0 перестает отвечать). Не рассчитана она на более менее приличный объем данных. Мне повезло, вовремя заметил что места почти нет и остановил репликацию. 4 ТБ wal журналов это круто, т.е. не удалялись журналы с начала репликации.
                    0
                    Это напомнило мне, как ООo держал в памяти все изменения примерно на 100 шагов назад, чтоб типа можно было откатить быстренько оные в таблице. В итоге удаление нескольких больших таблиц с формулами приводило к разрастанию процесса до 1,4 гб и падению ООо.

                    Подозреваю, что создатели подобных вещей редко задаются вопросами, а что ж будет, если вдруг закончится место или процесс выжрет больше ресурсов, чем ему можно. И проверяют всё на простых случаях, чтоб побыстрее протестировать…
                      0
                      Ну не нужно хранить БД в корневом разделе ОС, и тогда ничего не будет висеть )

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое