31 января 2017 года у GitLab случилась авария, связанная с эксплуатацией СУБД PostgreSQL, в результате которой часть данных была удалена, а проект был остановлен на время восстановления. Прошло уже несколько месяцев, и было очень много написано на эту тему, а сам GitLab представил исчерпывающий некролог, в котором рассказал, что произошло, какие предпринимались меры для восстановления и какие меры будут предприняты для предотвращения подобных аварий. Очень занимательное чтиво, рекомендуем его прочесть даже тем, кто далек от Постгреса.
В комментариях к нашему интервью с Алексеем Лесовским, некоторые представители сообщества, шутя, высказали претензию, что мы упомянули про аварию GitLab, но в итоге так и не провели подробный разбор полетов. Мы решили исправиться и попросили Алексея написать небольшой «разбор полетов». Основной целью этой публикации является детальный анализ некролога, выделение ключевых моментов, попытка проанализировать их и предложить рекомендации, как следовало бы действовать в подобной ситуации. И, конечно же рассмотрим меры, которые команда GitLab планирует предпринять для предотвращения таких инцидентов в будущем.
Список ключевых моментов в хронологическом порядке, на которые мы обратим внимание, выглядит следующим образом:
- LVM снимки как средство переноса базы данных с production на staging.
- Что делать, когда реплика отстает от мастера?
- Делаем pg_basebackup правильно, часть 1.
- Закончился max_wal_senders, но как?
- max_connections = 8000.
- «Зависший» pg_basebackup, или делаем pg_basebackup правильно, часть 2.
- strace: что хорошо для админа, то для дба не всегда нужно.
- rm or not rm: как быть с sensitive data?
- Методы резервного копирования.
- Разные версии Постгреса на production.
- Неработающая почта в кроне.
Приступим к разбору полетов. Также, в конце будет второй список, в нем мы коротко разберем те меры, которые приняты или будут приняты в будущем чтобы избежать повторения похожих инцидентов.
1. LVM снимки как средство переноса базы данных с production на staging.
Задача обновления staging окружения встречается довольно часто, поэтому уже появились отработанные практики и решения. Одно из таких решений — это создание снимка и перенос снимка на staging. Решение довольно удобное и легко настраиваемое, но имеет несколько недостатков. Большинство реализаций снимков сделано на основе copy-on-write, поэтому, при создании снимка на момент его существования, мы получаем дополнительную нагрузку на дисковое хранилище. В основном, это актуально для write-intensive нагрузки. В случае, когда уровень latency важен для вас, это может стать проблемой.
Более правильным способом переноса базы с production на staging являются резервные копии. Имеется настроенное резервное копирование, и staging обновляется оттуда. При таком раскладе не создается дополнительная нагрузка снимками и косвенно проверяются резервные копии на предмет успешности восстановления. Если staging не обновился — это явный признак, что с резервными копиями что-то не так.
2. Что делать, когда реплика отстает от мастера?
К сожалению, в статье не упоминается, как инженеры обнаружили наличие лага репликации, но, учитывая что реплика в итоге отвалилась безвозвратно, можно сделать вывод, что заметили слишком поздно. Какие здесь можно дать рекомендации? Конечно же, мониторинг лага репликации, в идеальном варианте, с алертингом. Но пойдет и “наколеночный” способ проверки лага скриптом по крону с последующей отправкой письма, смс или чего-нибудь еще.
При эксплуатации потоковой репликации, мониторинг лага — это одна их тех вещей, которые следует делать сразу после настройки. Существует масса причин, по которым реплика может начать отставать. Для мониторинга репликации в Постгресе есть представление pg_stat_replication — это очень крутая штука, при наличии некоторого опыта позволяющая определить даже причину лага: сеть, диски, долгие запросы и т.д.
Если вы обнаружили что реплика отстает, и вы при этом не знаете как скоро она отвалится безвозвратно, то временным решением является увеличение параметра wal_keep_segments. Этот параметр обновляется без рестарта постгреса, обычным релоадом (см. pg_reload_conf()), но стоит помнить, что ценой может быть увеличение использования дискового пространства, 1 сегмент — это 16MB. После того как параметр увеличен, можно приступить к поиску и устранению причин, вызвавших отставание
Также можно отметить, что ребята признались в том, что не используют WAL archiving, что тоже зря — это помогло бы восстановить реплику. Но, в целом, роль WAL архивов заключается не только в задаче подстраховать реплику. При наличии резервного копирования через pg_basebackup, использование архивов позволяет гибко управлять восстановлением (см. Point in time recovery).
3. Делаем pg_basebackup правильно, часть 1.
Как известно из некролога, перед тем как запустить pg_basebackup, инженер очистил каталог, в который должен был копироваться base backup. И действительно, pg_basebackup, обнаружив что каталог не является пустым, немедленно завершит работу. Однако, когда мы имеем дело с чувствительными данными, всегда лучше использовать mv вместо rm. Время дисков с маленьким объемом давно прошло, и такое правило может сильно облегчить жизнь. Это касается не только каталогов, но также таблиц в базах, баз с суффиксом _old и много другого. Хотя с терабайтными базами такое не пройдет, всегда лучше переименовать объект и через пару недель, убедившись, что он никому не нужен, тихо удалить, чем сделать это сразу.
4. Закончился max_wal_senders, но как?
При подключении pg_basebackup наблюдалась ошибка превышения лимита max_wal_senders. Этот параметр определяет лимит одновременных соединений для репликации. Если помните, у GitLab была всего одна реплика, и та безвозвратно отвалилась. Таким образом, все соединения должны были быть свободны. Однако, команда инженеров столкнулась с ошибкой, и тут нет других вариантов кроме попытки запустить несколько pg_basebackup одновременно.
Вообще, это бессмысленно: если не работает первый pg_basebackup, то почему должен заработать второй? Но, если мы все-таки столкнулись с этой ошибкой, стоит помнить, что изменение max_wal_senders требует рестарта постгреса (что часто являяется неприемлемым), и лучше было выяснить кто занял соединения (а это были ждущие pg_basebackup'ы) и остановить их. Зачем поднимать лимит? Чтобы запустить больше параллельных pg_basebackup?
5. max_connections = 8000
Следующий момент — это лимит соединений установленный в 8000. Это очень большая цифра для этой настройки. Постгрес устроен таким образом что на каждое клиентское соединение порождает отдельный процесс. Все это хорошо работает пока активно работающих постгресовых бэкендов не становится больше чем процессорных ядер в системе. После этого с дальнейшим увеличением процессов начинает снижаться производительность.
Чтобы не держать большое количество соединений к постгресу, был придуман pgbouncer который позволяет как бы сжать большое количество клиентских соединений в относительно небольшое количество соединений к постгресу. Например те же 8000 клиентских соединений можно замкнуть на pgbouncer, и тот будет устанавливать соединения к постгресу лишь по мере необходимости. Опять же, использование pgbouncer потенциально помогло бы избежать траты времени на возникшую ошибку и дополнительный рестарт постгреса.
6. «Зависший» pg_basebackup, или делаем pg_basebackup правильно, часть 2.
Двигаемся дальше: лимиты коннектов поправлены, теперь надо разобраться, почему же зависает pg_basebackup? Причина так называемого зависания кроется в специфике работы pg_basebackup. Перед тем как начать копирование базы, нужно дождаться выполнения т.н. “чекпоинта” (checkpoint). Checkpoint — это такая процедура, которая может сильно напрячь постгрес, в результате чего производительность основательно просядет. Поэтому, чтобы не напрягать систему, pg_basebackup по-умолчанию ждет, когда завершится штатный checkpoint, время и скорость выполнения которого зависит от настроек постгреса. Если указать параметр "-c fast", то pg_basebackup инициирует выполнение срочного checkoint без промедлений.
Как вы поняли, в процессе ожидания, когда завершится checkpoint, pg_basebackup не пишет никаких сообщений, и это сыграло злую шутку с инженером. Однако, благодаря команде GitLab, постгрес теперь станет лучше.
7. strace: что хорошо для админа, то для дба не всегда нужно.
Далее, инженеры попытались выяснить, почему зависает pg_basebackup, и использовали для этого strace. По своему опыту могу сказать, что strace — не совсем то, что нужно. strace нужен в тот момент, когда исследуемый процесс валится с ошибками или в какие-то моменты времени работает некорректно. Понять, что процесс “висит”, можно с помощью утилиты top: если видим 0 в поле %CPU, с высокой вероятностью процесс находится в ожидании.
И для исследования причин так называемых «зависаний» нужен stack trace процесса, чтобы понять, на какой функции он сейчас находится. Для этого подходят /proc//stack и, при наличии определенного опыта, gdb. К сожалению, сам по себе Постгрес не представляет возможности посмотреть, чем там занят процесс, ожидающий начала копирования, поэтому инженерам стоит знать об особенностях работы pg_basebackup.
8. rm or not rm: как быть с sensitive data?
Мы подошли к тому моменту, когда был удален каталог базы на мастере. Собственно, этот пункт является повторением вышесказанного: старайтесь использовать mv, если это возможно. Используйте rm только после того, как вы убедились, что никто не спохватился о якобы пропавших данных.
9. Методы резервного копирования.
Итак, авария произошла, и нужно выполнять восстановление из резервных копий. Давайте посмотрим, на какие способы резервирования полагалась команда:
- ежесуточный pg_dump: довольно тупой метод, который предлагает восстановление только на определенный момент времени — время начала pg_dump;
- ежесуточный LVM снимок — к этому методу нет претензий, при условии, что база не испытывает write-intensive нагрузок;
- ежесуточный снимок Asure: тут мне нечего сказать, не приходилось использовать;
- репликация с мастера: репликацию не принято считать бэкапом, поскольку случайное удаление данных просто среплицируется на реплику. Можно использовать репликацию с отложенным восстановлением (см. recovery_min_apply_delay), но при таком раскладе нужно иметь понимание как восстанавливаться, если что-то случайно было удалено.
Так или иначе, общая рекомендация: стараться не использовать реплики как бэкап, для резервного копирования есть свои инструменты. Наиболее предпочтительным средством является pg_basebackup + WAL archiving. Основное преимущество в использовании этого метода — возможность восстановить данные на определенный момент времени или номер транзакции в рамках имеющегося архива. Однако, у этого решения есть и недостаток — требование по месту. Каждый base backup представляет собой полную копию instance, а размер архива зависит от количества CRUD операций. Поэтому, при настройке резервного копирования следует учитывать итоговый размер хранилища под резервные копии с учетом количества дней хранения.
На данный момент есть несколько продуктов, которые реализуют такую модель резервного копирования:
- Barman, если у вас свои сервера.
- WAL-E, если ваши сервера находятся в Amazon.
Последнее, что можно добавить — это необходимость мониторинга того, что 1) резервные копии делаются успешно; 2) резервные копии вообще существуют; 3) есть результат тестового восстановления из резервной копии.
10. Разные версии Постгреса на production.
Следующий момент, к которому можно придраться, — наличие разных версий потсгреса на production. В случае GitLab, это как раз привело отсутствию возможности снимать резервные копии. Поэтому в любом плане по апгрейду на следующую мажорную версию должен присутствовать пункт удаления старых пакетов постгреса.
11. Неработающая почта в кроне.
Как мне кажется, и многие могут со мной не согласиться, почтовые уведомления становятся довольно редким механизмом уведомления. Тем не менее, каким бы механизмом вы не пользовались, всегда следует быть уверенным в том, что он действительно работает. В моей практике был случай, когда внутри мониторинга который уведомлял команду по смс, была функция отправки тестового смс ежедневно вечером дежурному админу, для проверки что все хорошо с оператором и с балансом все хорошо. В случае неудачи начинался спам по почте и загорался флаг в веб-интерфейсе — и это действительно помогало.
В случае GitLab, почта для крона просто не настраивалась изначально. Если вы ставите что-то важное в крон, подумайте и об уведомлениях.
В итоге база была восстановлена из LVM снимка, копирование заняло 18 часов, и это очень долгий период. При этом снимок находился на staging, и это оказался самый приемлемый вариант. Все это — неудачная выбранная стратегия резервного копирования (каждые 24 часа) и стечение обстоятельств. Результатом должен стать более продуманный подход к резервному копированию с более гибкими опциями восстановления, которые предусматривают минимальную потерю данных.
Команда GitLab выложила информацию о тех мерах, которые должны быть предприняты для того чтобы избежать подобных событий в будущем. Давайте коротко разберем и их.
1. Update PS1 across all hosts to more clearly differentiate between hosts and environments (#1094).
Выглядит вполне разумно. Сделать индивидуальный текст приглашения командной строки — довольно частая и правильная практика.
2. Prometheus monitoring for backups (#1095).
Эта мера также выглядит очень разумной, однако настройку мониторинга стоит делать после того, как вся система бэкапов пересмотрена и отстроена. Мониторинг должен включать в себя не только возраст бэкапа и размер, но и количество доступных резервных копий, а также информацию о том какие копии были успешно проверены на предмет восстановления.
3. Set PostgreSQL's max_connections to a sane value (#1096).
Даже 800 это слишком много, я порекомендовал бы посмотреть в сторону pgbouncer и попробовать использовать его. Однако у него есть свои ограничения, которые он накладывает на приложение, работающее с ним, поэтому тут нужно предварительно тестировать.
4. Investigate Point in time recovery & continuous archiving for PostgreSQL (#1097).
Да, это обязательно следует сделать.
5. Hourly LVM snapshots of the production databases (#1098).
Сомнительная идея, учитывая, что снимки использовались для обновления staging. Для этой цели вполне подойдут и резервные копии, сделанные в рамках задачи #1097.
6. Azure disk snapshots of production databases (#1099).
Сложно комментировать, но если хочется продублировать основную функциональность по резервным копиям, то почему бы и нет?
7. Move staging to the ARM environment (#1100).
Задача выглядит странной. Восстанавливать production из staging, где все данные могут быть существенно изменены, — сомнительная затея, в конечном счете от такого восстановления будет только хуже.
8. Recover production replica(s) (#1101).
Это конечно же нужно сделать ASAP. Любая более-менее серьезная постгресовая инсталляция сейчас развернута, как минимум, с одной потоковой репликой.
9. Automated testing of recovering PostgreSQL database backups (#1102).
Да, это очень нужная штука, она обязательно нужна.
10. Improve PostgreSQL replication documentation/runbooks (#1103).
Да, инструкции нужно обновить таким образом, чтобы даже ночной сторож соседнего склада смог по ним сделать восстановление.
11. Investigate pgbarman for creating PostgreSQL backups (#1105).
Да, barman — определенно, хороший инструмент, но, учитывая что GitLab использует Amazon S3, WAL-E выглядит предпочтительней. Однако разработчики Barman не исключают возможности, и даже предлагают спонсировать поддержку Amazon S3 в Barman.
12. Investigate using WAL-E as a means of Database Backup and Realtime Replication (#494).
Да, штука надежная, несмотря на немалое количество issues в трекере, работает стабильно и без претензий.
13. Build Streaming Database Restore.
Да именно так, есть бэкапы и есть проверка восстановления этих бэкапов через восстановление на staging окружении.
14. Assign an owner for data durability.
Ответственный человек, безусловно, нужен. Кроме этого, всем желательно ознакомиться с инструкциями, которые сделаны в рамках #1103.
Большое спасибо Алексею за подробный анализ. Обязательно оставляйте свои комментарии и соображения по поводу случившейся в Gitlab аварии и предпринятых мер! Ну и, конечно же, приглашаем всех подискутировать на эту тему с Лешей летом в Санкт-Петербурге на PG Day Russia! :)
Автор: rdruzyagin