Как вернуть удалённый конфиг или Никогда не сдавайся!
Как это произошло?
Звонок вечером от старого знакомого, работающего ныне в веб студии. На другой стороне полная паника и неопределённость.
— !"№;%: ААаа! Всё упало нечего не работает. Мне капец, спасай.
После пятнадцати минут приведения человека в адекватное состояние и выяснения что же всё-таки случилось стало ясно следующее:
Их студия не только делает сайты, но и хостит их.
Конфиг nginx генерируется скриптом вытаскивая location'ы и rewrite'ы из служебной базы MySQL.
База находится на хороших серверах с RAID-1 и master-slave репликацией
Бэкапы не делаются, так как «шанс того, что умрут оба винта на обоих серверах равен нулю» (с) Сисадмин этой студии
Ни 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», что ж, проверим мою удачу и попробуем погрепить по ней:
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 от текущего и того, что лежит в файле на диске =)