Автоматизация дампа базы данных MySQL на Ruby

Итак, существует немало методов/стратегий/вариаций создания дампов Базы Данных (бэкапов), т.е. резервирования прогресса развития контента вэб-портала во времени. Так что в данной статье рассмотрим частный случай (не самый скучный из всех возможных smile ):
  • БД - MySQL, установлен phpMyAdmin
  • БД функционирует на удаленном сервере, к которому нет доступа по SSH
  • БД открыта для запросов только с локального хоста (0.0.0.0) - в конфигурационном файле /etc/mysql/mysql.conf.d/mysqld.cnf раскомментировано:
bind-address = 0.0.0.0
  • Процесс дампа требуется автоматизировать

Планируем

Отсутствие доступа по SSH не позволяет воспользоваться замечательной утилитой mysqldump. Собственно из доступных инструментов управления БД остается только вэб-панель phpMyAdmin

через который получение файла дампа происходит буквально в 2 клика поле авторизации:

Окей, так как интерфейс phpMyAdmin не содержит навороченных jаvascript сценариев, то автоматизировать логин и дальнейший вызов процедуры дампа получится с помощью безоконного браузера для Ruby Mechanize.
Определимся с интервалами запуска дампа. Вряд ли мы пишем/обслуживаем какую нибудь соц. сеть, так что нет смысла в бесконечных циклах на уровне Ruby-скрипта. А для, допустим, небольшого интернет-магазина достаточно будет ежедневного дампа, т.е. каждые 24 часа - тогда ответственность за запуск скрипта можно возложить на плечи линуксового cron’а:

Скрипт

Импортируем библиотеки, запишем в переменные хост (URL входа в phpMyAdmin) и лог/пасс для доступа к админке, создадим объект “агента” и перейдем на требуемый хост:
require 'mechanize'
require 'pry-byebug'

host = 'https://foobar.ru'
login = 'systopthebest'
password = 'cryptomonster'

agent = Mechanize.new
page = agent.get host
binding.pry

binding.pry - объект библиотеки pry-byebug, позволяющий остановить выполнение скрипта на любой строке и запустить Ruby-консоль чтобы оценить обстановку

На этой странице находятся сразу несколько форм, чтоб попасть в нужную заглянем в HTML код:

нашу форму можно идентифицировать по атрибуту name=”login_form” :
agent.page.form_with(name: 'login_form') do |form| 
 form.pma_username = login 
 form.pma_password = password 
end.submit 
binding.pry 

Среди списка ссылок текущей страницы в консоли наблюдаем Log out, значит авторизация прошла успешно, Элвис в здании:

Но к сожалению ссылка, ведущая на страницу экспорта БД на уровне кода выглядит страшновато, разумнее будет перейти по ней вручную и скопировать конечный URL: https://blabla.ru/server_export.php?db=
эту строку можно также добавить в кучку начальных переменных скрипта:
host = 'https://foobar.ru' 
login = 'systopthebest' 
password = 'cryptomonster' 
export_url = "https://blabla.ru/server_export.php?db=" 

Просто перейдем по записанному URL’у
page = agent.get export_url 

Итак, кнопка запуска дампа является частью формы. Аналогично идентифицируем ее:
Заполнять форму ничем не требуется, нужно лишь подтвердить ее, после чего объект Mechanize-страницы станет скачанным файлом дампа, сохраним его в переменную:
file = agent.page.form_with(name: "dump").submit

С течением времени файлы дампов будут накапливаться в папке, чтоб удобно их дифференцировать зададим текущую дату в качестве имени файла в формате db_dump_%Y-%d-%m.sql:
file.save("#{Time.now.strftime("db_dump_%Y-%d-%m")}.sql")

не забываем, что время берется с сервера, на котором запущен скрипт, сама БД тут ни при чем.

Готово, можно проверять. Полный код приведен в конце статьи.

Проверку проводим сочетанием двух команд: переход в папку со скриптом и запуск скрипта так как именно такая команда будет забита в планировщик для автоматизированного регулярного запуска:
$ cd ~/systop/blog/dump_db && ruby mech.rb

Запуск

Для задания расписания запуска дампа воспользуемся стандартным Линукс-планировщиком cron. Он конфигурируется в текстовом виде, чтоб перейти к конфигу вводим
$ crontab -e 


при первом запуске потребуется выбрать текстовый редактор по умолчанию из доступных (рекомендую nano). Конфиг встретит стандартным шаблоном - строкой, начинающейся с нескольких звездочек:

* * * * *


Каждая звезда отвечает за свой промежуток времени
минута
час
день_месяца
месяц
день_недели
*
*
*
*
*
Соответственно, если звезды не заменять ничем, то команда будет выполняться каждую минуту. Рассмотрим примеры:

следующий конфиг будет выполняться каждый час на пятой минуте, то есть в 00:05, затем в 01:05, затем в 02:05 и т.д.
5
*
*
*
*
Следующий пример будет выполняться каждый день в 22:05 и так далее
5
22
*
*
*
Запишем в конфиг ранее протестированную команду (вывод команды будет сохраняться рядом в текстовом файле log.log):

39 9 * * * /bin/bash -l -c 'cd ~/systop/blog/dump_db && ruby mech.rb’ >> log.log

Флаг -l говорит о том, что команда будет выполняться от имени пользователя, задавшего конфиг. Флаг -c означает, что команда подается строкой, т.е. в кавычках или апострофах.

Для сохранения: Ctrl + O (сохранение) Ctrl + X (выход)

Данная команда запуска Ruby-скрипта справедлива если на данном хосте менеджером версий Ruby является RVM, который как правило используется на неттопах - локальных машинах, где осуществляется разработка. Если же в качестве менеджера используется rbenv - есть нюансы, читаем дальше.

Деплой на VPS

Логичным завершением темы резервирования будет деплой на виртуальный сервер, так как он:

  • функционирует 24/7

  • всегда имеет доступ в Интернет

  • как правило изобилует неиспользуемым дисковым пространством

На удаленных серверах принято устанавливать Ruby с помощью rbenv, так как он легковеснее - не содержит бесполезного в продакшене балласта. Однако для cron’а запись Ruby-скрипта придется предварить командой

export PATH="$HOME/.rbenv/bin:$PATH" ; eval "$(rbenv init -)"

Продакшен вариант:

12 23 * * * /bin/bash -l -c 'export PATH="$HOME/.rbenv/bin:$PATH" ; eval "$(rbenv init -)" && cd /home/systop/shop_dump && ruby mech.rb >> log.log'



Полный код:

require 'mechanize' 
require 'pry-byebug'

host = 'https://barbar.ru' 
login = 'hulk' 
password = 'johnsena' 
export_url = "https://barbar.ru/server_export.php?db="

agent = Mechanize.new 
page = agent.get host

agent.page.form_with(name: 'login_form') do |form| 
 form.pma_username = login 
 form.pma_password = password 
end.submit

puts "logged" 
page = agent.get export_url 
puts "downloading ..."

file = agent.page.form_with(name: "dump").submit 
file.save("#{Time.now.strftime("db_dump_%Y-%d-%m")}.sql")

puts "dumped successfully"
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.