Видеонаблюдение через интернет в веб-браузере

Автоматическое восстановление баз данных MySQL.

Исходные условия.

Задача: по включению сервера необходимо автоматически (без вмешательства администратора) гарантированно обеспечить целостность баз данных MySQL.

Зачем: для гарантированного запуска после аварии питания на сервере ключевых сервисов (демонов, программ, приложений), зависимых от баз данных.

Способ: синхронная(блокирующая) проверка баз данных с принудительным автоматическим восстановлением найденных повреждённых таблиц, выполняемая по каждому запуску/перезапуску демона mysql.

Хотя целевой системой для написания статьи предполагаются Debian GNU/Linux или его производные (Ubuntu, Mint, …), по нашему поверхностному мнению, рассмотренное решение легко портируется на другие дистрибутивы Linux или даже другие OS и другие SQL-сервера.

Реализация.

В качестве зависимого от MySQL важного сервиса предположим вымышленный демон MySuperMajorDaemon, хотя это может быть просто веб-сервер c сайтом, движок которого использует базу MySQL, LAMP, например.

Прежде всего нужно проверить систему инициализации (init) вашего дистрибутива, а именно:

  1. зависимость по запуску: MySuperMajorDaemon должен запускаться после mysqld, а останавливаться раньше;
  2. в init-системах с параллельной загрузкой (upstart, initng) нужно запретить параллельный запуск (если конечно такой режим используется) демонов MySuperMajorDaemon от mysql.

В Debian GNU/Linux 4.0 используется классический sysvint (System-V-like init utilities) в которой init-сценарии демонов из каталога /etc/init.d/ запускаются последовательно и верно, но медленно, поэтому нам остаётся проверить лишь порядок запуска и останова нашего демона, относительно демона mysqld.

Примечание: в Ubuntu, несмотря на использование «параллельного» upstart вместо классического sysvint, init-скрипты, похоже, также стартуют последовательно. FIXME

Итак, ниже приводим самый низкоуровневый способ проверки, подходящий для большинства дистрибутивов, использующих sysvinit и upstart. Для начала, определим текущий RUNLEVEL.

% sudo runlevel
N 2

Выводим на экран имена скриптов в одну колонку в порядке запуска/останова.

# последовательность запуска для runlevel=2
% ls -1 /etc/rc2.d/
# последовательность остановки при выключении runlevel=0
% ls -1 /etc/rc0.d/
# последовательность остановки при перезагрузке runlevel=6
% ls -1 /etc/rc6.d/

Примечания:

С порядком порядок :-) ? Тогда двигаемся дальше.

Теперь куда-то нужно вставить код проверки и лечения баз.
Таких мест (чтобы было наверняка) всего два:

  1. сразу после успешного запуска демона mysqld;
  2. перед запуском демона MySuperMajorDaemon.

Мы выбираем первый вариант и, как оказывается, «всё уже украдено до нас» — нужный нам код уже предусмотрен прямо «из коробки» (из пакета «mysql-server»).

Часть кода init-сценария запуска демона mysqld

# скрипт /etc/init.d/mysql
...
start)
...
   /usr/bin/mysqld_safe > /dev/null 2>&1 &
      # 6s was reported in #352070 to be too few when using ndbcluster
      for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14; do
          sleep 1
          if mysqld_status check_alive nowarn ; then break; fi
          log_progress_msg "."
      done
      if mysqld_status check_alive warn; then
          log_end_msg 0
          # Now start mysqlcheck or whatever the admin wants.
          output=$(/etc/mysql/debian-start)
          [ -n "$output" ] && log_action_msg "$output"
      else
          log_end_msg 1
          log_failure_msg "Please take a look at the syslog"
      fi
...

Как видно из кода, строка «output=$(/etc/mysql/debian-start)» запускает некий исполняемый скрипт «/etc/mysql/debian-start»

Смотрим его.

% cat /etc/mysql/debian-start

#!/bin/bash
#
# This script is executed by "/etc/init.d/mysql" on every (re)start.
#
# Changes to this file will be preserved when updating the Debian package.
#

source /usr/share/mysql/debian-start.inc.sh

MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
MYUPGRADE="/usr/bin/mysql_upgrade --defaults-extra-file=/etc/mysql/debian.cnf"
MYCHECK="/usr/bin/mysqlcheck --defaults-file=/etc/mysql/debian.cnf"
MYCHECK_SUBJECT="WARNING: mysqlcheck has found corrupt tables"
MYCHECK_PARAMS="--all-databases --fast --silent"
MYCHECK_RCPT="root"

# The following commands should be run when the server is up but in background
# where they do not block the server start and in one shell instance so that
# they run sequentially. They are supposed not to echo anything to stdout.
# If you want to disable the check for crashed tables comment
# "check_for_crashed_tables" out.
# (There may be no output to stdout inside the background process!)
echo "Checking for corrupt, not cleanly closed and upgrade needing tables."
(
  upgrade_system_tables_if_necessary;
  check_for_crashed_tables;
) >&2 &

exit 0

Отлично. Вкратце, скрипт работает так:

  • подгружает библиотечный код из /usr/share/mysql/debian-start.inc.sh, в котором описаны функции upgrade_system_tables_if_necessary() и check_for_crashed_tables();
  • запускает их (функции) в фоне;
  • немедленно возвращает управление /etc/init.d/mysql.

Запуск системы продолжается (загружаются другие демоны), а функции (см. выше) параллельно отрабатывают в фоне и сообщают о результатах своей работы в системный журнал (Syslog) и дополнительно, в случае ошибок, root-y на e-mail.

Хм, в штатно-предусмотренном сценарии «/etc/mysql/debian-start» нас не устраивают две вещи:

  1. проверка и восстановление осуществляется в фоне, т.е. при загрузке системы возможен вариант, когда наш демонюга MySuperMajorDaemon запустится на «недолеченных» повреждённых базах и может страшно обидится на это;
  2. mysqlcheck, запущенный с дефолтными опциями определёнными в MYCHECK_PARAMS, не будет восстанавливать повреждённые базы, а только лишь найдёт их, запишет об этом в журнал и уведомит root-а.

Изменим это.

патч для debian-start

% diff -u0 /etc/mysql/debian-start.orig /etc/mysql/debian-start
--- /etc/mysql/debian-start.orig        2007-02-22 03:04:51.000000000 +0300
+++ /etc/mysql/debian-start     2008-12-02 13:02:56.000000000 +0300
@@ -14 +14 @@
-MYCHECK_PARAMS="--all-databases --fast --silent"
+MYCHECK_PARAMS="--all-databases --silent --medium-check --auto-repair"
@@ -27 +27 @@
-) >&2 &
+) >&2

Раскроем опции mysqlcheck, выбранные на наш вкус.
Как убрали фоновую обработку (background), надеемся, понятно.

% man mysqlcheck

...
--medium-check, -m
          Проверять быстрее чем с --extended опцией.
          Этот режим найдёт 99.99% ошибок, что оправдано в большинстве случаев.
...
--auto-repair
          Автоматически восстанавливать повреждённые таблицы.
          Процессы восстановления начинаются только после проверки всех таблиц.
...
Все рассматриваемые скрипты принадлежат deb-пакету mysql-server-5.0
% dpkg -S /etc/mysql/debian-start
mysql-server-5.0: /etc/mysql/debian-start

и, соответственно, могут быть вытащены из него для использования в других дистрибутивах, в которых не предусмотрено подобного механизма.

Недостатки.

К недостаткам решения можно отнести увеличившееся время выполнения скрипта «/etc/init.d/mysql start». Если базы данных небольшие и целые, то это время вы можете даже не почувствовать. Но если базы приличного объёма и при этом повреждённые, то вы точно успеете сварить кофе или даже сходить пообедать LOL .

Об этом следует помнить, особенно при удалённой работе с сервером по ssh и особенно при удалённой перезагрузке сервера Debian 4.0 Etch, ибо:

Debian 4.0 Etch

% ls -1 /etc/rc2.d | egrep '(mysql|ssh)'
S17mysql-ndb-mgm
S18mysql-ndb
S19mysql
S20ssh

То есть, по включению/перезагрузке компьютера, никто не сможет войти в систему (ни локально, ни по сети) пока не завершится процесс проверки и восстановления баз данных

В Убунту с этим делом полегче, сервер SSH запускается раньше MySQL.

Ubuntu 8.04

ls -1 /etc/rc2.d | egrep '(mysql|ssh)'
S16ssh
S17mysql-ndb-mgm
S18mysql-ndb
S19mysql
Для сокращения времени проверки и восстановления можно ограничить состав баз данных (databases) или даже указать конкретные таблицы (tables), но только одной базы данных. Детали смотрите в первоисточнике «mysqlcheck(1)».

«Магия» debian.cnf

Особо наблюдательные могут спросить: а как mysqlcheck будет что-то там лечить, ведь для этого он должен знать пароль root-а (mysql-льного, не путать с системным).
Отвечаем: пароль mysql-ного root-а mysqlcheck не знает, он запускается от пользователя «debian-sys-maint», пароль которого прописан в файле «/etc/mysql/debian.cnf».

$ sudo cat /etc/mysql/debian.cnf

# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost
user     = debian-sys-maint
password = **************
socket   = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
user     = debian-sys-maint
password = **************
socket   = /var/run/mysqld/mysqld.sock
basedir  = /usr

Mysql-пользователь с именем «debian-sys-maint» создаётся при установке пакета mysql-server, пароль для него из 16 знаков генерируется случайным образом. «debian-sys-maint» может почти то же, что и root.

Теперь опять внимательно посмотрим «/etc/mysql/debian-start»

MYCHECK="/usr/bin/mysqlcheck --defaults-file=/etc/mysql/debian.cnf"

Теперь понятно откуда mysqlcheck берёт имя и пароль для работы с сервером?

Благодаря сохранённому в файле аккаунту «debian-sys-maint» многие Debian/Ubuntu пакеты, использующие базы данных, при установке/обновлении автоматически или после подтверждения пользователя создают/обновляют структуры своих баз. Это один из механизмов «дружественности к пользователю», которым достигается принцип «установил/обновил и сразу работает» принятых(возможных) в пакетной базе дистрибутивов, основанных на Debian.

Вопросы безопасности.

Уже слышим брюзжание экспертов в области безопасности 8-) .

Экперты: Как это так, пароль почти root-а (опять напомним, mysql-льного, не путать с системным) сохранён в файле на диске.
Отвечаем: /etc/mysql/debian.cnf имеет атрибуты доступа 0600 root:root, которые позволяют читать этот файл только одному пользователю в системе - root-у. А если вы профукаете пароль системного root-а, то злоумышленник легко получит доступ с базам и без информации из /etc/mysql/debian.cnf.

Эксперты: Нельзя считать безопасным дистрибутив, в котором устанавливаемые/обновляемые пакеты могут автоматически править базы данных.
Отвечаем: Спорный вопрос, во многом зависит от качества сборки и тестирования конкретного пакета, а также доверия к его создателям.
Debian/Ubuntu, на сегодняшний день, имеют две самые большие пакетные базы среди всех дистрибутивов GNU/Linux, доступ к которым (установка/обновление пакетов) осуществятся через сетевые депозитории и пакетный менеджер APT. Т.е., почти всё, что может понадобиться пользователю, уже есть в централизованном репозитории, в тщательно проверенных, оттестированных пакетах. Схема тестирования и проверки пакетов в Debian более чем (по нашему мнению - самая) серьёзная: циклы «experimental -> unstable -> testing -> stable».

Ну а самым яростным специалистам в области безопасности, советуем вспомнить установлены ли вообще или давно ли менялись пароли root-ов, и системного и mysql-ного ;-)

Немного о «железе».

Чтобы сервер (компьютер) автоматически запустился после возобновления питания нужно:

  1. Установить соответствующую опции в BIOS. Какую - зависит от версии BIOS, берите в руки англо-русский словарь и ищите по ключевому слову POWER, если такая опция есть — не пропустите.
  2. Желательно иметь управляемый UPS и использовать софт для работы с UPS.

Всё?

howto/mysql-auto-repair.txt · Последние изменения: 2011-03-14