Инцидент с базой данных GitLab.com от 31/01/2017
31 января 2017 года произошло важное для мира OpenSource событие: один из админов GitLab.com, пытаясь починить репликацию, перепутал консоли и удалил основную базу PostgreSQL, в результате чего было потеряно большое количество пользовательских данных, и сам сервис ушел в офф-лайн. При этом все 5 различных способов бэкапа/репликации оказались нерабочими. Восстановились же с LVM-снимка, случайно сделанного за 6 часов до удаления базы.
Понесенные потери
Потеряны данные за примерно 6 часов.
Потеряно 4613 обычных проектов, 74 форка и 350 импортов (грубо); всего 5037. Поскольку Git-репозитории НЕ потеряны, мы сможем воссоздать те проекты, пользователи/группы которых существовали до потери данных, но мы не сможем восстановить задачи (issues) этих проектов.
Потеряно около 4979 (можно сказать, около 5000) комментариев.
Потенциально потеряно 707 пользователей (сложно сказать точнее по логам Kibana).
Веб-хуки, созданные до 31 января 17:20, восстановлены, созданные после — потеряны.
Хронология (время указано в UTC)
2017/01/31 16:00/17:00 — 21:00
- YP работает над настройкой pgpool и репликацией в staging, создает LVM-снимок, чтобы загрузить боевые данные в staging, а также в надежде на то, что сможет использовать эти данные для ускорения загрузки базы на другие реплики. Это происходит примерно за 6 часов до потери данных.
- Настройка репликации оказывается проблематичной и очень долгой (оценочно ~20 часов только на начальную синхронизацию pg_basebackup). LVM-снимок YP использовать не смог. Работа на этом этапе была прервана (так как YP была нужна помощь другого коллеги, который в тот день не работал), а также из-за спама/высокой нагрузки на GitLab.com.
2017/01/31 21:00 — Всплеск нагрузки на сайт из-за спамеров
- Блокирование пользователей по их IP-адресам
- Удаление пользователя за использование репозитория в качестве CDN, в результате чего 47 000 айпишников залогинились под тем же аккаунтом (вызвав высокую нагрузку на БД). Информация была передана командам технической поддержки и инфраструктуры.
- Удаление пользователей за спам (с помощью создания сниппетов)
- Нагрузка на БД вернулась к норме, было запущено несколько ручных вакуумов PostgreSQL, чтобы почистить большое количество оставшихся пустых строк.
2017/01/31 22:00 — Получено предупреждение об отставании репликации
- Попытки починить db2, отставание на этом этапе 4 GB.
- db2.cluster отказывается реплицироваться, каталог /var/opt/gitlab/postgresql/data вычищен, чтобы обеспечить чистую репликацию.
- db2.cluster отказывается подключаться к db1, ругаясь на слишком низкое значение max_wal_senders. Эта настройка используется для ограничения количества клиентов WAL (репликации).
- YP увеличивает max_wal_senders до 32 на db1, перезапускает PostgreSQL.
- PostgreSQL ругается на то, что открыто слишком много семафоров, и не стартует
- YP уменьшает max_connections с 8000 до 2000, PostgreSQL стартует (при том, что он нормально работал с 8000 почти целый год).
- db2.cluster все еще отказывается реплицироваться, но на соединения больше не жалуется, а вместо это просто висит и ничего не делает.
- В этот время YP начинает чувствовать безысходность. Раньше в этот день он сообщил, что собирается заканчивать работу, так как становилось уже поздно (около 23:00 по местному времени), но он остался на месте по причине неожиданно возникших проблем с репликацией.
2017/01/31 около 23:00
- YP думает, что, возможно, pg_basebackup чересчур педантичен по поводу чистоты директории для данных и решает ее удалить. Спустя пару секунд он замечает, что запустил команду на db1.cluster.gitlab.com вместо db2.cluster.gitlab.com.
- 2017/01/31 23:27: YP отменяет удаление, но уже слишком поздно. Из примерно 310 Гб осталось только 4.5
Восстановление — 2017/01/31 23:00 (бэкап от ~17:20 UTC)
Предложенные способы восстановления:
1. Смигрировать db1.staging.gitlab.com на GitLab.com (отставание около 6 часов)
- CW: Проблема с веб-хуками, которые были удалены во время синхронизации.
2. Восстановить LVM-снимок (отстает на 6 часов).
3. Sid: попробовать восстановить файлы?
- CW: Невозможно! rm -Rvf Sid: OK.
- JEJ: Наверное, уже слишком поздно, но может ли помочь, если достаточно быстро перевести диск в режим read-only? Также нельзя ли получить дескриптор файла, если он используется работающим процессом (согласно http://unix.stackexchange.com/a/101247/213510).
- YP: PostgreSQL не держит все свои файлы постоянно открытыми, так что это не сработает. Также, похоже, что Azure очень быстро удаляет данные, а вот пересылает их на другие реплики уже не так шустро. Другими словами, данные с самого диска восстановить не получится.
- SH: Похоже, что на db1 staging-сервере отдельный PostgreSQL-процесс льет поток production-данных с db2 в каталог gitlab_replicator. Согласно отставанию репликации, db2 был погашен в 2016-01-31 05:53, что привело к остановке gitlab_replicator. Хорошие новости заключаются в том, что данные вплоть до этого момента выглядят нетронутыми, поэтому мы, возможно, сможем восстановить веб-хуки.
Предпринятые действия:
2017/02/01 23:00 — 00:00: Принято решение восстанавливать данные с db1.staging.gitlab.com на db1.cluster.gitlab.com (production). Несмотря на то, что они отстают на 6 часов и не содержат веб-хуков, это единственный доступный снимок. YP говорит, что ему сегодня лучше больше не запускать никаких команд, начинающихся с sudo, и передает управление JN.
2017/02/01 00:36 — JN: Делаю бэкап данных db1.staging.gitlab.com.
2017/02/01 00:55 — JN: Монтирую db1.staging.gitlab.com на db1.cluster.gitlab.com.
Копирую данные со staging /var/opt/gitlab/postgresql/data/ в production /var/opt/gitlab/postgresql/data/.
2017/02/01 01:05 — JN: nfs-share01 сервер выделен в качестве временного хранилища в /var/opt/gitlab/db-meltdown.
2017/02/01 01:18 — JN: Копирую оставшиеся production-данные, включая запакованный pg_xlog: ‘20170131-db-meltodwn-backup.tar.gz’.
2017/02/01 01:58 — JN: Начинаю синхронизацию из stage в production.
2017/02/01 02:00 — CW: Для объяснения ситуации обновлена страничка развертывания (deploy page). Link.
2017/02/01 03:00 — AR: rsync выполнился примерно на 50% (по количеству файлов).
2017/02/01 04:00 — JN: rsync выполнился примерно на 56.4% (по количеству файлов). Передача данных идет медленно по следующим причинам: пропускная способность сети между us-east и us-east-2, а также ограничение производительности диска на staging-сервере (60 Mb/s).
2017/02/01 07:00 — JN: Нашел копию нетронутых данных на db1 staging в /var/opt/gitlab_replicator/postgresql. Запустил виртуальную машину db-crutch VM на us-east, чтобы сделать бэкап этих данных на другую машину. К сожалению, она ограничена 120 GB RAM и не потянет рабочую нагрузку. Эта копия будет использована для проверки состояния базы данных и выгрузки данных веб-хуков.
2017/02/01 08:07 — JN: Передача данных идет медленно: по объему данных передано 42%.
2017/02/02 16:28 — JN: Передача данных закончилась.
Процедура восстановления
[x] — Сделать снимок сервер DB1 — или 2 или 3 — сделано в 16:36 UTC.
[x] — Обновить db1.cluster.gitlab.com до PostgreSQL 9.6.1, на нем по-прежнему 9.6.0, а staging использует 9.6.1 (в противном случае PostgreSQL может не запуститься).
Установить 8.16.3-EE.1.
Переместить chef-noop в chef-client (было отключено вручную).
Запустить chef-client на хосте (сделано в 16:45).
[x] — Запустить DB — 16:53 UTC
Мониторить запуск и убедиться, что все прошло нормально.
Сделать бэкап.
[x] — Обновить Sentry DSN, чтобы ошибки не попали в staging.
[x] — Увеличить идентификаторы во всех таблицах на 10k, чтобы избежать проблем при создании новых проектов/замечаний. Выполнено с помощью https://gist.github.com/anonymous/23e3c0d41e2beac018c4099d45..., который читает текстовый файл, содержащий все последовательности (по одной на строку).
[x] — Очистить кеш Rails/Redis.
[x] — Попытаться по возможности восстановить веб-хуки
[x] Запустить staging, используя снимок, сделанный до удаления веб-хуков.
[x] Убедиться, что веб-хуки на месте.
[x] Создать SQL-дамп (только данные) таблицы “web_hooks” (если там есть данные).
[x] Скопировать SQL-дамп на production-сервер.
[x] Импортировать SQL-дамп в рабочую базу.
[x] — Проверить через Rails Console, могут ли подключаться рабочие процессы (workers).
[x] — Постепенно запустить рабочие процессы.
[x] — Отключить страницу развертывания.
[x] — Затвитить с @gitlabstatus.
Возникшие проблемы
1. LVM-снимки по умолчанию делаются лишь один раз в 24 часа. По счастливой случайности YP за 6 часов до сбоя сделал один вручную.
2. Регулярные бэкапы, похоже, также делались только раз в сутки, хотя YP еще не выяснил, где они хранятся. Согласно JN они не работают: создаются файлы размером в несколько байт.
SH: Похоже, что pg_dump работает неправильно, поскольку выполняются бинарники от PostgreSQL 9.2 вместо 9.6. Это происходит из-за того, что omnibus использует только Pg 9.6, если data/PG_VERSION установлено в 9.6, но на рабочих узлах этого файла нет. В результате по умолчанию запускается 9.2 и тихо завершается, ничего не сделав. В итоге SQL-дампы не создаются. Fog-гем, возможно, вычистил старые бэкапы.
3. Снимки дисков в Azure включены для NFS-сервера, для серверов баз данных — нет.
4. Процесс синхронизации удаляет веб-хуки после того, как он синхронизировал данные на staging. Если мы не сможем вытащить их из обычного бэкапа, сделанного в течение 24 часов, они будут потеряны.
5. Процедура репликации оказалось очень хрупкой, склонной к ошибкам, зависящей от случайных shell-скриптов и плохо документированной.
SH: Мы позже выяснили, что обновление базы данных staging работает путем создания снимка директории gitlab_replicator, удаления конфигурации репликации и запуска отдельного PostgreSQL-сервера.
6. Наши S3-бэкапы также не работают: папка пуста.
7. У нас нет надежной системы оповещений о неудачных попытках создания бэкапов, мы теперь видим такие же проблемы и на dev-хосте.
Другими словами, из 5 используемых способов бэкапа/репликации ни один не работает. => сейчас мы восстанавливаем рабочий бэкап, сделанный 6 часов назад.
Заключение
Что показательно, ребята из GitLab сумели превратить свою грубейшую ошибку в поучительную историю, и, думаю, не только не потерять, но и завоевать уважение многих айтишников. Также за счет открытости, написав о проблеме в Twitter и выложив лог в Google Docs, они очень быстро получили квалифицированную помощь со стороны, причем, похоже, совершенно безвозмездно.
Как всегда, радуют люди с хорошим чувством юмора: главный виновник инцидента теперь называет себя "Database (removal) specialist" (Специалист по [удалению] баз данных), какие-то шутники предложили 1 февраля сделать днем проверки бэкапов http://checkyourbackups.work/
Оригинал https://docs.google.com/document/d/1GCK53YDcBWQveod9kfzW-VCx...