sudo rm -rf або хроніка інциденту з базою даних GitLab.com від 2017/01/31


Він п'янів повільно, але все-таки сп'янів, як-то відразу, стрибком; і коли в хвилину просвітління побачив перед собою розрубаний дубовий стіл в абсолютно незнайомій кімнаті, оголений меч у своїй руці і плеще в долоні безгрошових донів навколо, то подумав, що пора йти додому. Але було пізно.

Аркадій і Борис Стругацькі
31 січня 2017 року відбулася важлива для світу OpenSource подія: один з адмінів GitLab.com, намагаючись полагодити реплікацію, переплутав консолі і видалив основну базу PostgreSQL, в результаті чого було втрачено велику кількість даних, і сам сервіс пішов в офф-лайн. При цьому всі 5 різних способів бекапа/реплікації виявилися неробочими. Відновилися ж з LVM-знімка, випадково зробленої за 6 годин до видалення бази. It, як кажуть, happens. Але треба віддати належне команді проекту, так як вони знайшли в собі сили поставитися до всього з гумором, не втратили голову і проявили дивовижну відкритість, написавши про все в твіттері і виклавши в загальний доступ, по суті, внутрішній документ, в якому команда в реальному часі вела опис розгортаються подій.
Під час його читання буквально відчуваєш себе на місці бідного YP, який в 11 годин вечора після важкого трудового дня і безрезультатної боротьби з Постгресом, стомлено мружачись, вбиває в консоль бойового сервера фатальне
sudo rm -rf
і тисне Enter. Через секунду він розуміє, що накоїв, скасовує видалення, але вже пізно — бази більше немає...
З причини важливості і в багатьох сенсах поучительности цього випадку, ми вирішили повністю перевести на російську мову його журнал-звіт, зроблений співробітниками GitLab.com у процесі роботи над інцидентом. Результат ви можете знайти під катом.
Отже, давайте дізнаємося у всіх подробицях, як це було.
Інцидент з базою даних GitLab.com від 31/01/2017
Зауваження: цей інцидент торкнувся базу даних (включаючи завдання (issues) та запити на злиття (merge requests)), git-репозиторії і wiki-сторінки не постраждали.
Пряма трансляція на YouTube — стежте за тим, як ми обговорюємо і вирішуємо проблему!
  1. Понесені втрати
  2. Хронологія (час вказано у UTC)
  3. Відновлення — 2017/01/31 23:00 (бекап від приблизно 17:20 UTC)
  4. Виниклі проблеми
  5. Допомога
    • HugOps (додавайте тут пости з twitter і звідки-небудь ще, в яких люди по-доброму реагували на те, що трапилося)
    • Stephen Frost
    • Sam McLeod
Понесені втрати
  1. Втрачені дані за приблизно 6 годин.
  2. Втрачено 4613 звичайних проектів, 74 форк і 350 импортов (грубо); всього 5037. Оскільки Git-репозиторії НЕ втрачені, ми зможемо відтворити ті проекти, користувачі/групи яких існували до втрати даних, але ми не зможемо відновити завдання (issues) цих проектів.
  3. Втрачено близько 4979 (можна сказати, близько 5000) коментарів.
  4. Потенційно втрачено 707 користувачів (складно сказати точніше по логам Kibana).
  5. Веб-хуки, створені до 31 січня 17:20, відновлені, створені після — втрачені.
Хронологія (час вказано у UTC)
  1. 2017/01/31 16:00/17:00 — 21:00
    • YP працює над налаштуванням pgpool та реплікацією в staging, створює LVM-знімок, щоб завантажити бойові дані в staging, а також в надії на те, що зможе використати ці дані для прискорення завантаження бази на інші репліки. Це відбувається приблизно за 6 годин до втрати даних.
    • Налаштування реплікації виявляється проблематичною і дуже довгої (оціночно ~20 годин тільки на початкову синхронізацію pg_basebackup). LVM-знімок YP використовувати не зміг. Робота на цьому етапі була перервана (так як YP була потрібна допомога іншого колеги, який у той день не працював), а також з-за спаму/високого навантаження на GitLab.com.
  2. 2017/01/31 21:00Сплеск навантаження на сайт через спамерівTwitter | Slack
    • Блокування користувачів за їх IP-адресами
    • Видалення користувача за використання репозиторію як CDN, в результаті чого 47 000 айпишников залогинились під тим же обліковим записом (викликавши високе навантаження на БД). Інформація була передана командам технічної підтримки та інфраструктури.
    • Видалення користувачів за спам (з допомогою створення фрагментів) — Slack
    • Навантаження на БД повернулася до норми, було запущено кілька ручних вакуумов PostgreSQL, щоб почистити велику кількість порожніх рядків.
  3. 2017/01/31 22:00Отримано попередження про відставання реплікаціїSlack
    • Спроби полагодити 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 за місцевим часом), але він залишився на місці з причини несподівано виниклих проблем з реплікацією.
  4. 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 — Slack.
Відновлення — 2017/01/31 23:00 (бекап від ~17:20 UTC)
  1. Запропоновані способи відновлення:
    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. Хороші новини полягають в тому, що дані аж до цього моменту виглядають недоторканими, тому ми, можливо, зможемо відновити веб-хуки.
  2. Вжиті дії:
    1. 2017/02/01 23:00 — 00:00: Прийнято рішення відновлювати дані з db1.staging.gitlab.com на db1.cluster.gitlab.com (production). Незважаючи на те, що вони відстають на 6 годин і не містять веб-хуков, це єдиний доступний знімок. YP каже, що йому сьогодні краще більше не запускати ніяких команд, які починаються з sudo, і передає управління JN.
    2. 2017/02/01 00:36 — JN: Роблю бекап даних db1.staging.gitlab.com.
    3. 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/.
    4. 2017/02/01 01:05 — JN: nfs-share01 сервер виділений в якості тимчасового сховища в /var/opt/gitlab/db-meltdown.
    5. 2017/02/01 01:18 — JN: Копіюю залишилися production-дані, включаючи запакований pg_xlog: '20170131-db-meltodwn-backup.tar.gz'.
    6. 2017/02/01 01:58 — JN: Починаю синхронізацію з stage production.
    7. 2017/02/01 02:00 — CW: Для пояснення ситуації оновлена сторінка розгортання (deploy page). Link.
    8. 2017/02/01 03:00 — AR: rsync виконався приблизно на 50% (по кількості файлів).
    9. 017/02/01 04:00 — JN: rsync виконався приблизно на 56.4% (по кількості файлів). Передача даних йде повільно з наступних причин: пропускна здатність мережі між us-east us-east-2, а також обмеження продуктивності диска на staging-сервері (60 Mb/s).
    10. 2017/02/01 07:00 — JN: Знайшов копію незайманих даних на db1 staging в /var/opt/gitlab_replicator/postgresql. Запустив віртуальну машину db-crutch VM на us-east, щоб зробити бекап цих даних на іншу машину. На жаль, вона обмежена 120 GB RAM і не потягне робоче навантаження. Ця копія буде використана для перевірки стану бази даних і вивантаження даних веб-хуків.
    11. 2017/02/01 08:07 — JN: Передача даних йде повільно: за обсягом даних передано 42%.
    12. 2017/02/02 16:28 — JN: Передача даних закінчилася.
    13. 2017/02/02 16:45 — Нижче наведена процедура відновлення.
  3. Процедура відновлення
    1. [x] — Зробити знімок сервер DB1 — 2 або 3 — зроблено в 16:36 UTC.
    2. [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).
    3. [x] — Запустити DB — 16:53 UTC
      • Моніторити запуск і переконатися, що все пройшло нормально.
      • Зробити бекап.

    4. [x] — Оновити Sentry DSN, щоб помилки не потрапили в staging.
    5. [x] — Збільшити ідентифікатори в усіх таблицях на 10k, щоб уникнути проблем при створенні нових проектів/зауважень. Виконано з допомогою https://gist.github.com/anonymous/23e3c0d41e2beac018c4099d45ec88f5, який читає текстовий файл, що містить всі послідовності (по одній на рядок).
    6. [x] — Очистити кеш Rails/Redis.
    7. [x] — Спробувати по можливості відновити веб-хуки
      • [x] Запустити staging, використовуючи знімок, зроблений до видалення веб-хуків.
      • [x] Переконатися, що веб-хуки на місці.

      • [x] Створити SQL дамп (тільки дані таблиці «web_hooks» (якщо там є дані).
      • [x] Скопіювати SQL дамп на production-сервер.
      • [x] Імпортувати SQL дамп в робочу базу.
    8. [x] — Перевірити через Rails Console, можуть підключатися робочі процеси (workers).
    9. [x] — Поступово запустити робочі процеси.
    10. [x] — Вимкнути сторінку розгортання.
    11. [x] — Затвитить з @gitlabstatus.
    12. [x] — Створити пов'язані зі збоєм задачі, що описують подальші плани/дії Прихований текст[ ] — Створити нові записи Project Git-репозиторіїв, у яких немає записів Project, у випадках, коли простір імен відповідає існуючому користувачеві/групі.
      • PC — Я створюю список цих репозиторіїв, щоб ми могли перевірити в базі даних, чи існують вони.
      [ ] — Видалити репозиторії з невідомими (втраченими) просторами імен.
      • AR — працюю над скриптом, заснованим на даних з попередньої точки.
      [x] — Ще раз видалити спам-користувачів (щоб вони знову не створили проблем).
      • [x] CDN-користувач з 47 000 IP-адресами.
    13. Зробити після відновлення даних:
      1. Створити завдання на зміну в терміналах PS1-формату/квітів, щоб було одразу зрозуміло, яка середа використовується: production або staging (production — червоний, staging — жовтий). Для всіх користувачів за умовчанням в запрошенні bash показувати повне ім'я хоста (наприклад, «db1.staging.gitlab.com» замість «db1»): https://gitlab.com/gitlab-com/infrastructure/issues/1094
      2. Як-небудь заборонити rm -rf для директорії PostgreSQL data? Не впевнений, що це можливо або необхідно (в тому випадку, якщо є нормальні бекапи).
      3. Додати оповіщення для бекапів: перевірка сховища S3 і т. д. Додати графік, що показує зміни розмірів бекапів, видавати попередження, коли розмір зменшується більш ніж на 10%: https://gitlab.com/gitlab-com/infrastructure/issues/1095.
      4. Розглянути додавання часу останнього успішного бекапа в БД, щоб адміни могли легко побачити цю інформацію (запропоновано клієнтом https://gitlab.zendesk.com/agent/tickets/58274).
      5. Розібратися, чому в PostgreSQL раптово виникли проблеми з max_connections, встановленим в 8000, незважаючи на те, що це працювало з 2016-05-13. Несподівана поява цієї проблеми багато в чому відповідальна за навалившиеся відчай і безвихідь: https://gitlab.com/gitlab-com/infrastructure/issues/1096.
      6. Подивитися на збільшення порогів реплікації через архівацію WAL / PITR — також буде корисно після невдалих оновлень: https://gitlab.com/gitlab-com/infrastructure/issues/1097.
      7. Створити для користувачів посібник з вирішення проблем, які можуть виникнути після запуску сервісу.
      8. Поекспериментувати з переміщенням даних з одного дата-центру в інший за допомогою AzCopy: Microsoft говорить, що це має працювати швидше rsync:
        • Схоже, це Windows-специфічна річ, а у нас немає експертів з Windows (або кого-то хоча б віддалено, але достатньою мірою знайомого з питанням, щоб грамотно це протестувати).
    Виниклі проблеми
    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 годин тому.

    http://monitor.gitlab.net/dashboard/db/postgres-stats?panelId=10&fullscreen&from=now-24h&to=now
    Допомога з боку
    Hugops (додавайте тут пости з twitter або звідкись ще, в яких люди по-доброму реагували на те, що трапилося)
    Stephen Frost
    • https://twitter.com/net_snow/status/826622954964393984 @gitlabstatus Привіт, я розробник PG і мені подобається те, що ви робите. Повідомте, якщо я можу чимось допомогти, я був би радий бути корисним.
    Sam McLeod
    • Привіт, Сід, дуже шкода, що у вас проблеми з базою даних / LVM, це дуже неприємно. У нас працює кілька кластерів PostgreSQL (master/slave), і я звернув увагу на кілька речей у вашому звіті:
      1. Ви використовуєте Slony, а це ще той шматок самі знаєте чого, і це зовсім не перебільшення, тут ось навіть сміються над ним http://howfuckedismydatabase.com, при цьому вбудований бінарники PostgreSQL, який відповідає за потокове реплікацію, дуже надійний і швидкий, пропоную перейти на нього.

      2. Не згадується використання пулів сполук, натомість йдеться про тисячі з'єднань в postgresql.conf — це дуже погано та неефективно з точки зору продуктивності, пропоную використовувати pg_bouncer і не виставляти max_connection в PostgreSQL вище 512-1024; практично, якщо у вас більше 256 активних сполук, треба масшабироваться горизонтально, а не вертикально.
      3. У звіті говориться, наскільки ненадійні ваші процеси відпрацювання відмови і резервного копіювання, ми написали і задокументували простий скрипт для postgresql failover — якщо хочете, я вам перешлю. Що стосується бекапів, то для інкрементних резервних копій протягом дня ми використовуємо pgbarman, а також робимо повні бекапи двічі на день, barman і pg_dump, важливо з точки зору продуктивності і надійності зберігати ваші бекапи і директорію з даними postgresql на різних дисках.
      4. Ви все ще в Azure?!?! Я б запропонував звідти якомога швидше з'їхати, так як там чимало дивних проблем з внутрішнім DNS, NTP, маршрутизацією і сховищами, також я чув кілька страшних історій про те, як там все влаштовано всередині.
    Довга переписка Сіда і Сема з упором в налаштування PostgreSQL
    • Повідомте, якщо вам потрібна допомога по налаштуванню PostgreSQL, у мене в цьому питанні пристойний досвід.
    • Capt. McLeod: ще питання: скільки місця на диску займає ваша база (бази)? мова йде про терабайтах, чи це все ще гігабайти?

    • Capt. McLeod: виклав свій скрипт відпрацювання відмови / реплікації:
    • Я також бачу, що ви дивитеся на pgpool — я б запропонував pgbouncer взамін
    • Capt. McLeod: У pgpool достатньо проблем, ми його гарненько протестували і викинули.
    • Capt. McLeod: дайте мені знати, якщо я можу сказати щось публічно через твіттер або якось ще в підтримку GitLab і вашої прозорості в роботі над цією проблемою, я знаю, як це важко, коли я тільки починав, у нас був split-brain на рівні SAN в infoxchange, і мене в буквальному сенсі рвало — настільки сильно нервував!
    • Sid Sijbrandij: Привіт, Сем, спасибі за допомогу. Ти не проти, якщо я скопирую це публічний документ, щоб це могли інші члени команди?
    • Capt. McLeod: Скрипт відпрацювання відмови?
    • Sid Sijbrandij: Все, що ти написав.
    • Звичайно, в будь-якому випадку це публічний репозиторій, але він не ідеальний, дуже далекий від цього, але добре робить свою роботу, я постійно плутаю хости без всяких наслідків, але у вас все може бути по-іншому.
    • Так, звичайно, ти можеш переслати і мої рекомендації.
    • Якщо ти зможеш надіслати мені інформацію про свою VM, на якій запущено PostgreSQL і свій PostgreSQL.conf, я дам рекомендації щодо його поліпшення.
    • Sid: ми використовували Slony тільки для оновлення 9.2 до 9.6, в інших випадках у нас працює потокова реплікація.
      Примітка: ДОБРЕ, це добре, для інформації: в рамках мажорних версій PostgreSQL для виконання оновлень можна використовувати вбудовану реплікацію.
    • Rails вже створює пули сполук (25 на процес). При 20 процесах на 20 хостах виходить десь 10 000 з'єднань, при цьому буде близько 400 активних (оскільки Unicorn — це однопотоковий додаток).
      Примітка: Кожне PostgreSQL-з'єднання використовує пам'ять, тримати відразу багато відкритих з'єднань неефективно; тут може допомогти pg_bouncer — фантастично простий і швидкий інструмент по створенню пулів сполук. Він робить тільки одну річ, але робить її добре, коли як pgpool все ускладнює. Він може переписувати запити, деякі з яких починають працювати не так, як очікувалося. Pgpool створений не для використання спільно з ORM/db-фреймворками.
    Варто почитати: https://wiki.postgresql.org/wiki/Number_Of_Database_Connections
    • Для балансування навантаження, створення пулів сполук, якісної відпрацювання відмов і т. д., ми дивимося на pgpool + потокова реплікація з синхронними коммитами (для узгодженості даних). Pgbouncer, наскільки ми знаємо, не балансує навантаження (принаймні, з коробки). Ось це https://github.com/awslabs/pgbouncer-rr-patch варто розглянути в якості одного з варіантів.
    Запитання: чи Використовуєте ви в даний час кілька active/active PostgreSQL-нод, і якщо ні, то як виконуєте балансування навантаження?
    Запитання: Яка щоденне навантаження на сайті? Скільки завантажень сторінок і запитів в секунду?
    * По всій імовірності, секція з питаннями від Сема, відповідями команди GitLab.com і підсумковими рекомендаціями буде ще деякий час поповнюватися і до самого інциденту прямого стосунку вже не має. Ми поки не стали включати її в переклад, так як оригінал ще не стабілізувався.
    Висновок
    Що показово, хлопці з GitLab зуміли перетворити свою грубу помилку в повчальну історію, і, думаю, не тільки не втратити, але і завоювати повагу багатьох айтішників. Також за рахунок відкритості, написавши про проблеми в Twitter і виклавши лог в Google Docs, вони дуже швидко отримали кваліфіковану допомогу з боку, причому, схоже, абсолютно безоплатно.
    Як завжди, радують люди з хорошим почуттям гумору: головний винуватець інциденту тепер називає себе "Database (removal) specialist" (Спеціаліст з [видаленню] баз даних), якісь жартівники запропонували 1 лютого зробити днем перевірки бекапів http://checkyourbackups.work/, а користувачі Хабра згадали чудову тематичну картинку:

    Джерело
    Які можна зробити висновки?
    1. Потрібно перевіряти бекапи.
    2. Необхідно враховувати додаткові труднощі при відновленні файлів в хмарі (принаймні, в Azure).
    3. LVM — це не так вже й погано, і його використовують для розміщення БД навіть такі помітні компанії, як GitLab.com незважаючи на втрати в продуктивності.
    4. Не давати dev/stage/prod-серверів схожі назви.
    5. Робити інтерфейси dev/stage/prod-серверів, які відрізняються один від одного за кольором/формату.
    6. Не боятися розповісти всьому світу про свою помилку — добрих людей більше, і вони допоможуть.
    7. Пам'ятати, що навіть найважче ураження можна перетворити на перемогу.
    Посилання по темі:
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.