Сисадмины делятся на тех, кто не делает бэкапы, и тех кто, их уже делает =)
Про то как восстанавливать файлы с ext3 / ufs написана не одна статья, так что не буду повторятся и напишу про не самые широко известные способы восстановления конфигов на продакшн сервере.
Как это произошло?
Звонок вечером от старого знакомого, работающего ныне в веб студии. На другой стороне полная паника и неопределённость.
— !"№;%: ААаа! Всё упало нечего не работает. Мне капец, спасай.
После пятнадцати минут приведения человека в адекватное состояние и выяснения что же всё-таки случилось стало ясно следующее:
- Их студия не только делает сайты, но и хостит их.
- Конфиг nginx генерируется скриптом вытаскивая location'ы и rewrite'ы из служебной базы MySQL.
- База находится на хороших серверах с RAID-1 и master-slave репликацией
- Бэкапы не делаются, так как «шанс того, что умрут оба винта на обоих серверах равен нулю» (с) Сисадмин этой студии
По поводу бэкапов
Что правда, то правда. Действительно, 4 винта умереть одновременно не могут (possible, but statistically unlikely © «Charlie» Eppes, Numb3rs), однако почемуто люди не думают о том, что rm -rf /* выполненный на RAID-1, убьёт инфу и на обоих винтах, так же забывают, что DROP TABLE у нас прореплицируется с одного сервера на другой. Так же, редко кто подозревает о том что в один прекрасный день офис может сгореть из-за пожара / утонуть из-за наводнения / разрушиться из-за землетрясения / уехать вместе с ОБЭПом. В общем off-site бэкапы вообще мало кто делает… А зря, хотя бы раз в месяц сливать всё на флэшку в запароленный .rar и уносить домой можно даже вручную, не особа запариваясь.
Ни ZFS снапшоты, ни RAID, ни репликация не являются заменой бэкапов. Хоть всё это и понижает шансы потерять данные, и очень хорошо, что это есть, однако, Off-site бэкапы должны быть всегда!
Ближе к делу
По Закону Мерфи, то что может случится, просто обязано произойти. Так в этот злополучный вечер из-за ошибки в UPDATE SQL запросе, служебная таблица с данными из которых генерировался конфиг nginx'a была заполнена '', а из-за ошибки в скрипте nginx.conf был перезаписан пустым файлом. Благо nginx штука умная и перед reload'ом конфига проверяет его на правильность, так что применять новый конфиг nginx отказался.
Как же восстановить перезаписанный конфиг?
Мой старый знакомый дал мне доступ до фронтенда с nginx.
Тут всё обыденно: Машинка на FreeBSD, gmirror на два диска и nginx, нечего более.
Первым делом остановил gmirror, чтобы все мои изменения не перезаписывали файлы на втором винте. Далее начал думать как же восстановить с диска убитый файл, но тут посмотрев на аптайм сервера и вспомнив, что знакомый говорил, мол, конфиг меняется довольно редко, решил попробовать другой метод.
Посмотрел сколько у нас свапа.
# swapinfo
Device 1K-blocks Used Avail Capacity
/dev/ad4s1b 2063152 94612 1968540 5%
То, что он в данный момент занят на 5% далеко не значит, что там всего 5% информации, скорее всего там её намного больше =)
Сохраним его текущее состояние
# cat /dev/ad4s1b > /usr/SWAP
И зная какую-нить строку из конфига начнём по ней грепить. Так как большинство людей тюнят, как фряху, так и nginx «по Сысоеву», то в конфиге скорее всего есть строчка «reset_timedout_connection on», что ж, проверим мою удачу и попробуем погрепить по ней:
# cat /usr/SWAP | grep -a -A10 reset_timedout_connection
�Lj�Lj��Lj��Lj�Lj�Lj$�Lj0�Lj8�Lj<�LjX�Lj\�Ljd�Ljp�Lj��Lj��Lj��Lj��Lj��Lj�Lj��Lj��Lj��Lj���Lj8�LjP�Ljp�Lj��Lj��Lj��Lj�Lj��LjX�Lj
��Lj�Lj�m [Ȉh�LjxȈҰLj@������� �.�`�`���0u�0u2�d�d�Lj�Ȉ<4�@TȈ��Ȉ
--
reset_timedout_connection on;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
send_lowat 12000;
keepalive_timeout 65;
gzip on;
gzip_min_length 2048;
gzip_types text/css text/js text/xml;
^C
и вот, вуаля, кусок конфига, осталось только поиграть со значениями -A и -B, выцепить конфиг целиком и выбрать из вариантов самый новый/небитый (возможно их в свапе будет несколько)
# cat /usr/SWAP | grep -a -A400 -B12 "reset_timedout_connection on;"
Всё конфиг у нас в руках. Вроде даж не битый и актуальный. Теперь отпарсив его можно восстановить MySQL таблицу.
Данный метод не панацея и не серебряная пуля, то что он сработал в моём случае скорее исключение нежели правило, однако может быть кому-то из вас этот метод когда-нить поможет восстановить важные данные.
Если в swap'e нет, а файл с винта не восстановить
Так же есть второй, менее предпочтительный вариант восстановления информации в случае если на сервере всё ещё запущен процесс nginx
Для начала ищем напущенный nginx master
# ps -auxww | grep nginx
root 1197 0,0 0,1 13216 2488 ?? Is ср18 0:00,02 nginx: master process /usr/local/sbin/nginx
www 29484 0,0 2,3 57248 47576 ?? I 7:58 0:00,06 nginx: worker process (nginx)
Далее деаем его coredump
# gcore 1197
А потом ковыряем его как хотим, хоть так
# cat core.1197 | strings | grep -B10 -A10 reset_timedout_connection
хоть так
# cat core.1197 | grep -a -B10 -A10 reset_timedout_connection
… И ужасаемся от того как же сложно собрать конфиг по кусочкам
Вывод
Люди, не будьте Сами Себе Злобными Буратинами, делайте частые хорошо защищённые автоматические бэкапы данных. И помните, что даже из самой глубокой жопы есть как минимум два выхода %)
Вместо послесловия
MySQL базу в итоге восстановили. Админ сам того не зная включил --bin-log с самого начала жизни базы (кстати, к тому моменту как я начал востанавливать базу binlog уже занимал 89% /var и через пару месяцев mysql перестал бы запускаться). Благодаря тому, что их никто не удалял, можно было сделать Point-in-Time Recovery
ПС. Неплохо было бы если б nginx мог по запросу выдавать свой текущий конфиг или diff от текущего и того, что лежит в файле на диске =)